@fs/mycroft 0.1.0 → 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # mycroft-cli
1
+ # mycroft
2
2
 
3
3
  Command-line tool that ingests EPUB files, builds a local searchable index, and answers questions using a retrieval-augmented workflow.
4
4
 
@@ -16,10 +16,15 @@ Ensure `node` is on your PATH (the CLI uses Node.js as its runtime).
16
16
 
17
17
  ## Usage
18
18
 
19
+ Run `mycroft config onboard` first to set everything up, then use the book and chat commands.
20
+
19
21
  ```bash
20
22
  mycroft book list
21
23
  mycroft book ingest /path/to/book.epub
22
24
  mycroft book ask <id> "What is the main conflict?"
25
+ mycroft chat start <id>
26
+ mycroft chat ask <session> "What does this foreshadow?"
27
+ mycroft chat repl <session>
23
28
  mycroft config init
24
29
  mycroft config resolve
25
30
  mycroft config onboard
@@ -3,10 +3,11 @@ _mycroft() {
3
3
  cur="${COMP_WORDS[COMP_CWORD]}"
4
4
  prev="${COMP_WORDS[COMP_CWORD-1]}"
5
5
 
6
- local top_commands="book config"
6
+ local top_commands="book config chat"
7
7
  local global_flags="--help --version --data-dir"
8
8
  local book_commands="ingest list show ask search delete"
9
9
  local config_commands="path init resolve onboard"
10
+ local chat_commands="start ask list show repl"
10
11
 
11
12
  if [[ ${COMP_CWORD} -le 1 ]]; then
12
13
  COMPREPLY=( $(compgen -W "${top_commands} ${global_flags}" -- "${cur}") )
@@ -40,6 +41,26 @@ _mycroft() {
40
41
  return 0
41
42
  fi
42
43
  ;;
44
+ chat)
45
+ if [[ ${COMP_CWORD} -eq 2 ]]; then
46
+ COMPREPLY=( $(compgen -W "${chat_commands} ${global_flags}" -- "${cur}") )
47
+ return 0
48
+ fi
49
+ case "${COMP_WORDS[2]}" in
50
+ ask|repl)
51
+ COMPREPLY=( $(compgen -W "--top-k --max-chapter" -- "${cur}") )
52
+ return 0
53
+ ;;
54
+ show)
55
+ COMPREPLY=( $(compgen -W "--tail" -- "${cur}") )
56
+ return 0
57
+ ;;
58
+ start)
59
+ COMPREPLY=( $(compgen -W "--title" -- "${cur}") )
60
+ return 0
61
+ ;;
62
+ esac
63
+ ;;
43
64
  esac
44
65
  }
45
66
 
@@ -17,6 +17,7 @@ end
17
17
 
18
18
  complete -c mycroft -n '__mycroft_needs_command' -a 'book' -d 'Manage books and queries'
19
19
  complete -c mycroft -n '__mycroft_needs_command' -a 'config' -d 'Manage configuration'
20
+ complete -c mycroft -n '__mycroft_needs_command' -a 'chat' -d 'Run multi-turn chat sessions'
20
21
  complete -c mycroft -s h -l help -d 'Show help'
21
22
  complete -c mycroft -l version -d 'Show version'
22
23
  complete -c mycroft -l data-dir -r -d 'Override data directory'
@@ -38,3 +39,14 @@ complete -c mycroft -n '__mycroft_using_command config' -a 'path' -d 'Print conf
38
39
  complete -c mycroft -n '__mycroft_using_command config' -a 'init' -d 'Create default config file'
39
40
  complete -c mycroft -n '__mycroft_using_command config' -a 'resolve' -d 'Print resolved config values'
40
41
  complete -c mycroft -n '__mycroft_using_command config' -a 'onboard' -d 'Initialize config and show next step'
42
+
43
+ complete -c mycroft -n '__mycroft_using_command chat' -a 'start' -d 'Start a chat session for a book'
44
+ complete -c mycroft -n '__mycroft_using_command chat' -a 'ask' -d 'Ask a question in a chat session'
45
+ complete -c mycroft -n '__mycroft_using_command chat' -a 'list' -d 'List chat sessions'
46
+ complete -c mycroft -n '__mycroft_using_command chat' -a 'show' -d 'Show chat session details'
47
+ complete -c mycroft -n '__mycroft_using_command chat' -a 'repl' -d 'Start interactive chat session'
48
+
49
+ complete -c mycroft -n '__mycroft_using_command chat; and __fish_seen_subcommand_from ask repl' -l top-k -r -d 'Number of passages to retrieve'
50
+ complete -c mycroft -n '__mycroft_using_command chat; and __fish_seen_subcommand_from ask repl' -l max-chapter -r -d 'Spoiler-free limit'
51
+ complete -c mycroft -n '__mycroft_using_command chat; and __fish_seen_subcommand_from show' -l tail -r -d 'Show last N messages'
52
+ complete -c mycroft -n '__mycroft_using_command chat; and __fish_seen_subcommand_from start' -l title -r -d 'Session title'
@@ -2,7 +2,7 @@
2
2
 
3
3
  _mycroft() {
4
4
  local -a top_commands
5
- top_commands=(book config)
5
+ top_commands=(book config chat)
6
6
 
7
7
  local -a book_commands
8
8
  book_commands=(ingest list show ask search delete)
@@ -10,6 +10,9 @@ _mycroft() {
10
10
  local -a config_commands
11
11
  config_commands=(path init resolve onboard)
12
12
 
13
+ local -a chat_commands
14
+ chat_commands=(start ask list show repl)
15
+
13
16
  local -a global_flags
14
17
  global_flags=(--help --version --data-dir)
15
18
 
@@ -48,6 +51,27 @@ _mycroft() {
48
51
  return
49
52
  fi
50
53
  ;;
54
+ chat)
55
+ if (( CURRENT == 3 )); then
56
+ _describe -t commands "chat commands" chat_commands
57
+ _describe -t flags "global flags" global_flags
58
+ return
59
+ fi
60
+ case ${words[3]} in
61
+ ask|repl)
62
+ _arguments "--top-k[Number of passages to retrieve]:n" "--max-chapter[Spoiler-free limit]:n"
63
+ return
64
+ ;;
65
+ show)
66
+ _arguments "--tail[Show last N messages]:n"
67
+ return
68
+ ;;
69
+ start)
70
+ _arguments "--title[Session title]:text"
71
+ return
72
+ ;;
73
+ esac
74
+ ;;
51
75
  esac
52
76
  }
53
77
 
package/dist/cli.js CHANGED
@@ -566,6 +566,9 @@ var summarizeAllChapters = async (chapters) => {
566
566
  return summaries;
567
567
  };
568
568
 
569
+ // src/db/queries.ts
570
+ import "better-sqlite3";
571
+
569
572
  // src/db/schema.ts
570
573
  import Database from "better-sqlite3";
571
574
  var resolveDbPath = async () => {
@@ -588,6 +591,27 @@ var createDb = async () => {
588
591
  progress_chapter INTEGER
589
592
  );
590
593
  `);
594
+ db.exec(`
595
+ CREATE TABLE IF NOT EXISTS chat_sessions (
596
+ id TEXT PRIMARY KEY,
597
+ book_id TEXT NOT NULL,
598
+ title TEXT,
599
+ summary TEXT,
600
+ created_at INTEGER DEFAULT (strftime('%s','now')),
601
+ updated_at INTEGER DEFAULT (strftime('%s','now'))
602
+ );
603
+ `);
604
+ db.exec(`
605
+ CREATE TABLE IF NOT EXISTS chat_messages (
606
+ id TEXT PRIMARY KEY,
607
+ session_id TEXT NOT NULL,
608
+ role TEXT NOT NULL,
609
+ content TEXT NOT NULL,
610
+ token_count INTEGER,
611
+ created_at INTEGER DEFAULT (strftime('%s','now'))
612
+ );
613
+ `);
614
+ db.exec("CREATE INDEX IF NOT EXISTS chat_messages_session_idx ON chat_messages(session_id, created_at)");
591
615
  const columns = db.prepare("PRAGMA table_info(books)").all().map((col) => col.name);
592
616
  const ensureColumn = (name, definition) => {
593
617
  if (!columns.includes(name)) {
@@ -707,8 +731,95 @@ var getBook = async (id) => {
707
731
  };
708
732
  var deleteBook = async (id) => {
709
733
  const db = await getDb();
734
+ db.prepare("DELETE FROM chat_messages WHERE session_id IN (SELECT id FROM chat_sessions WHERE book_id = ?)").run(id);
735
+ db.prepare("DELETE FROM chat_sessions WHERE book_id = ?").run(id);
710
736
  db.prepare("DELETE FROM books WHERE id = ?").run(id);
711
737
  };
738
+ var mapSession = (row) => ({
739
+ id: row.id,
740
+ bookId: row.book_id,
741
+ title: row.title ?? null,
742
+ summary: row.summary ?? null,
743
+ createdAt: row.created_at ?? 0,
744
+ updatedAt: row.updated_at ?? 0
745
+ });
746
+ var mapSessionSummary = (row) => ({
747
+ ...mapSession(row),
748
+ bookTitle: row.book_title ?? null
749
+ });
750
+ var mapMessage = (row) => ({
751
+ id: row.id,
752
+ sessionId: row.session_id,
753
+ role: row.role,
754
+ content: row.content,
755
+ tokenCount: row.token_count ?? null,
756
+ createdAt: row.created_at ?? 0
757
+ });
758
+ var insertChatSession = async (session) => {
759
+ const db = await getDb();
760
+ db.prepare(
761
+ "INSERT INTO chat_sessions (id, book_id, title, summary, created_at, updated_at) VALUES (@id, @bookId, @title, @summary, @createdAt, @updatedAt)"
762
+ ).run({
763
+ id: session.id,
764
+ bookId: session.bookId,
765
+ title: session.title ?? null,
766
+ summary: session.summary ?? null,
767
+ createdAt: session.createdAt ?? Date.now(),
768
+ updatedAt: session.updatedAt ?? Date.now()
769
+ });
770
+ return session.id;
771
+ };
772
+ var updateChatSession = async (id, updates) => {
773
+ const fields = [];
774
+ const params = { id };
775
+ if (updates.title !== void 0) {
776
+ fields.push("title = @title");
777
+ params.title = updates.title;
778
+ }
779
+ if (updates.summary !== void 0) {
780
+ fields.push("summary = @summary");
781
+ params.summary = updates.summary;
782
+ }
783
+ if (updates.updatedAt !== void 0) {
784
+ fields.push("updated_at = @updatedAt");
785
+ params.updatedAt = updates.updatedAt;
786
+ }
787
+ if (fields.length === 0) return;
788
+ const db = await getDb();
789
+ db.prepare(`UPDATE chat_sessions SET ${fields.join(", ")} WHERE id = @id`).run(params);
790
+ };
791
+ var getChatSession = async (id) => {
792
+ const db = await getDb();
793
+ const row = db.prepare("SELECT * FROM chat_sessions WHERE id = ?").get(id);
794
+ return row ? mapSession(row) : null;
795
+ };
796
+ var listChatSessions = async () => {
797
+ const db = await getDb();
798
+ const rows = db.prepare(
799
+ "SELECT chat_sessions.*, books.title as book_title FROM chat_sessions LEFT JOIN books ON books.id = chat_sessions.book_id ORDER BY chat_sessions.updated_at DESC"
800
+ ).all();
801
+ return rows.map(mapSessionSummary);
802
+ };
803
+ var insertChatMessage = async (message) => {
804
+ const db = await getDb();
805
+ db.prepare(
806
+ "INSERT INTO chat_messages (id, session_id, role, content, token_count, created_at) VALUES (@id, @sessionId, @role, @content, @tokenCount, @createdAt)"
807
+ ).run({
808
+ id: message.id,
809
+ sessionId: message.sessionId,
810
+ role: message.role,
811
+ content: message.content,
812
+ tokenCount: message.tokenCount ?? null,
813
+ createdAt: message.createdAt ?? Date.now()
814
+ });
815
+ return message.id;
816
+ };
817
+ var getChatMessages = async (sessionId, limit) => {
818
+ const db = await getDb();
819
+ const rows = limit !== void 0 ? db.prepare("SELECT * FROM chat_messages WHERE session_id = ? ORDER BY created_at DESC LIMIT ?").all(sessionId, limit) : db.prepare("SELECT * FROM chat_messages WHERE session_id = ? ORDER BY created_at ASC").all(sessionId);
820
+ const mapped = rows.map(mapMessage);
821
+ return limit !== void 0 ? mapped.reverse() : mapped;
822
+ };
712
823
 
713
824
  // src/services/ingest.ts
714
825
  var formatDuration = (ms) => {
@@ -1035,21 +1146,27 @@ ${context}`
1035
1146
  }
1036
1147
  };
1037
1148
 
1149
+ // src/commands/query-options.ts
1150
+ var parseQueryOptions = (options) => {
1151
+ const topK = Number(options.topK);
1152
+ if (!Number.isFinite(topK) || topK <= 0) {
1153
+ throw new Error("--top-k must be a positive number.");
1154
+ }
1155
+ let maxChapter;
1156
+ if (options.maxChapter !== void 0) {
1157
+ const parsed = Number(options.maxChapter);
1158
+ if (!Number.isFinite(parsed) || parsed < 0) {
1159
+ throw new Error("--max-chapter must be a non-negative number.");
1160
+ }
1161
+ maxChapter = parsed;
1162
+ }
1163
+ return { topK, maxChapter };
1164
+ };
1165
+
1038
1166
  // src/commands/book/ask.ts
1039
1167
  var registerBookAsk = (program2) => {
1040
1168
  program2.command("ask").description("Ask a question about a book").argument("<id>", "Book id or prefix").argument("<question>", "Question to ask").option("--top-k <n>", "Number of passages to retrieve", "5").option("--max-chapter <n>", "Spoiler-free limit (0-based within narrative)").action(async (id, question, options) => {
1041
- const topK = Number(options.topK);
1042
- if (!Number.isFinite(topK) || topK <= 0) {
1043
- throw new Error("--top-k must be a positive number.");
1044
- }
1045
- let maxChapter;
1046
- if (options.maxChapter !== void 0) {
1047
- const parsed = Number(options.maxChapter);
1048
- if (!Number.isFinite(parsed) || parsed < 0) {
1049
- throw new Error("--max-chapter must be a non-negative number.");
1050
- }
1051
- maxChapter = parsed;
1052
- }
1169
+ const { topK, maxChapter } = parseQueryOptions(options);
1053
1170
  await askCommand(id, question, { topK, maxChapter });
1054
1171
  });
1055
1172
  };
@@ -1092,18 +1209,7 @@ var searchCommand = async (id, query, options) => {
1092
1209
  // src/commands/book/search.ts
1093
1210
  var registerBookSearch = (program2) => {
1094
1211
  program2.command("search").description("Vector search without LLM").argument("<id>", "Book id or prefix").argument("<query>", "Search query").option("--top-k <n>", "Number of passages to retrieve", "5").option("--max-chapter <n>", "Spoiler-free limit (0-based within narrative)").action(async (id, query, options) => {
1095
- const topK = Number(options.topK);
1096
- if (!Number.isFinite(topK) || topK <= 0) {
1097
- throw new Error("--top-k must be a positive number.");
1098
- }
1099
- let maxChapter;
1100
- if (options.maxChapter !== void 0) {
1101
- const parsed = Number(options.maxChapter);
1102
- if (!Number.isFinite(parsed) || parsed < 0) {
1103
- throw new Error("--max-chapter must be a non-negative number.");
1104
- }
1105
- maxChapter = parsed;
1106
- }
1212
+ const { topK, maxChapter } = parseQueryOptions(options);
1107
1213
  await searchCommand(id, query, { topK, maxChapter });
1108
1214
  });
1109
1215
  };
@@ -1220,7 +1326,7 @@ var onboardCommand = async () => {
1220
1326
  }
1221
1327
  const defaults = await loadConfig();
1222
1328
  const path = configPath();
1223
- stdout("\nEPUB RAG setup");
1329
+ stdout("\nmycroft");
1224
1330
  stdout("Press Enter or type -y to accept defaults.");
1225
1331
  const dataDirInput = await prompt(`Data directory [${defaults.dataDir}]: `);
1226
1332
  const dataDir = isDefault(dataDirInput) ? defaults.dataDir : dataDirInput;
@@ -1269,6 +1375,289 @@ var registerConfigOnboard = (program2) => {
1269
1375
  });
1270
1376
  };
1271
1377
 
1378
+ // src/services/chat.ts
1379
+ import { randomUUID as randomUUID2 } from "crypto";
1380
+ import { embed as embed3, generateText as generateText2 } from "ai";
1381
+ import { openai as openai5 } from "@ai-sdk/openai";
1382
+ var MAX_RECENT_MESSAGES = 12;
1383
+ var SUMMARY_TRIGGER_MESSAGES = 24;
1384
+ var SUMMARY_TARGET_WORDS2 = 160;
1385
+ var formatContext2 = (chunks) => chunks.map(
1386
+ (chunk, index) => `Excerpt [${index + 1}] (${chunk.chapterTitle || `Chapter ${chunk.chapterIndex + 1}`}):
1387
+ ${chunk.content}`
1388
+ ).join("\n\n");
1389
+ var estimateTokens2 = (text) => Math.ceil(text.length / 4);
1390
+ var summarizeMessages = async (messages) => {
1391
+ const transcript = messages.map((message) => `${message.role.toUpperCase()}: ${message.content}`).join("\n\n");
1392
+ const models = await getModels();
1393
+ const { text } = await generateText2({
1394
+ model: openai5(models.summary),
1395
+ prompt: `Summarize this conversation so far in ~${SUMMARY_TARGET_WORDS2} words. Focus on facts, decisions, and unresolved questions.
1396
+
1397
+ ${transcript}`,
1398
+ temperature: 0.3
1399
+ });
1400
+ return text.trim();
1401
+ };
1402
+ var buildConversationContext = (session, messages) => {
1403
+ const summary = session.summary ? `Conversation summary:
1404
+ ${session.summary}` : "";
1405
+ const recent = messages.slice(-MAX_RECENT_MESSAGES).map((message) => `${message.role.toUpperCase()}: ${message.content}`).join("\n\n");
1406
+ return [summary, recent].filter(Boolean).join("\n\n");
1407
+ };
1408
+ var maybeSummarizeSession = async (session, messages, updatedAt) => {
1409
+ if (messages.length < SUMMARY_TRIGGER_MESSAGES) return;
1410
+ const summary = await summarizeMessages(messages.slice(0, -MAX_RECENT_MESSAGES));
1411
+ await updateChatSession(session.id, { summary, updatedAt });
1412
+ };
1413
+ var listSessions = async () => listChatSessions();
1414
+ var getSession = async (id) => getChatSession(id);
1415
+ var getSessionMessages = async (sessionId, limit) => getChatMessages(sessionId, limit);
1416
+ var startSession = async (bookId, title) => {
1417
+ await ensureDataDirs();
1418
+ const resolvedId = await resolveBookId(bookId);
1419
+ if (!resolvedId) {
1420
+ throw new Error(`Book not found: ${bookId}`);
1421
+ }
1422
+ const sessionId = randomUUID2();
1423
+ await insertChatSession({
1424
+ id: sessionId,
1425
+ bookId: resolvedId,
1426
+ title: title ?? null,
1427
+ summary: null
1428
+ });
1429
+ const session = await getChatSession(sessionId);
1430
+ if (!session) {
1431
+ throw new Error("Failed to create chat session.");
1432
+ }
1433
+ return session;
1434
+ };
1435
+ var chatAsk = async (sessionId, question, options) => {
1436
+ if (!await isAskEnabled()) {
1437
+ throw new Error("Ask is disabled in config (askEnabled: false). Enable it to use this command.");
1438
+ }
1439
+ requireOpenAIKey();
1440
+ await ensureDataDirs();
1441
+ const session = await getChatSession(sessionId);
1442
+ if (!session) {
1443
+ throw new Error(`Chat session not found: ${sessionId}`);
1444
+ }
1445
+ const book = await getBook(session.bookId);
1446
+ if (!book) {
1447
+ throw new Error(`Book not found: ${session.bookId}`);
1448
+ }
1449
+ const models = await getModels();
1450
+ const { embedding } = await embed3({
1451
+ model: openai5.embeddingModel(models.embedding),
1452
+ value: question
1453
+ });
1454
+ const narrativeStart = book.narrativeStartIndex ?? 0;
1455
+ const userProgress = book.progressChapter ?? null;
1456
+ const maxChapterIndex = options.maxChapter !== void 0 ? narrativeStart + options.maxChapter : userProgress !== null ? narrativeStart + userProgress : void 0;
1457
+ const retrievalLimit = options.topK * 3;
1458
+ const allMatches = await queryBookIndex(session.bookId, embedding, question, retrievalLimit, maxChapterIndex);
1459
+ const summaries = allMatches.filter((m) => m.type === "summary");
1460
+ const chunks = allMatches.filter((m) => m.type !== "summary");
1461
+ const topSummaries = summaries.slice(0, 2);
1462
+ const topChunks = chunks.slice(0, Math.max(0, options.topK - topSummaries.length));
1463
+ const selectedMatches = [...topSummaries, ...topChunks];
1464
+ const context = formatContext2(selectedMatches);
1465
+ const messages = await getChatMessages(sessionId);
1466
+ const conversation = buildConversationContext(session, messages);
1467
+ const now = Date.now();
1468
+ const userMessage = {
1469
+ id: randomUUID2(),
1470
+ sessionId,
1471
+ role: "user",
1472
+ content: question,
1473
+ tokenCount: estimateTokens2(question),
1474
+ createdAt: now
1475
+ };
1476
+ await insertChatMessage(userMessage);
1477
+ const prompt2 = [
1478
+ conversation ? `Conversation:
1479
+ ${conversation}` : "",
1480
+ `Question: ${question}`,
1481
+ context
1482
+ ].filter(Boolean).join("\n\n");
1483
+ const { text } = await generateText2({
1484
+ model: openai5(models.chat),
1485
+ system: `You are a reading companion helping readers understand this book.
1486
+
1487
+ Guidelines:
1488
+ - Use the provided chapter summaries and excerpts to answer questions
1489
+ - Chapter summaries provide high-level context about characters, events, and plot
1490
+ - Excerpts provide specific details and quotes
1491
+ - When asked for recaps or "what happened", synthesize from summaries
1492
+ - Don't cite table of contents, front matter, or structural elements
1493
+ - If truly unsure, briefly say so - but try to answer from available context first
1494
+ - Cite sources using [1], [2], etc. at the end of relevant sentences
1495
+ - The context may be limited to earlier chapters only - don't infer beyond what's provided`,
1496
+ prompt: prompt2
1497
+ });
1498
+ const assistantMessage = {
1499
+ id: randomUUID2(),
1500
+ sessionId,
1501
+ role: "assistant",
1502
+ content: text,
1503
+ tokenCount: estimateTokens2(text),
1504
+ createdAt: now
1505
+ };
1506
+ await insertChatMessage(assistantMessage);
1507
+ const updatedAt = Date.now();
1508
+ await updateChatSession(sessionId, { updatedAt });
1509
+ await maybeSummarizeSession(session, [...messages, userMessage, assistantMessage], updatedAt);
1510
+ return { answer: text, sources: selectedMatches };
1511
+ };
1512
+
1513
+ // src/commands/chat/start.ts
1514
+ var registerChatStart = (program2) => {
1515
+ program2.command("start").description("Start a chat session for a book").argument("<id>", "Book id or prefix").option("--title <title>", "Session title").action(async (id, options) => {
1516
+ const session = await startSession(id, options.title);
1517
+ stdout(`Started chat session ${session.id} for book ${session.bookId}`);
1518
+ });
1519
+ };
1520
+
1521
+ // src/commands/chat/utils.ts
1522
+ var resolveChatSessionId = async (input) => {
1523
+ const sessions = await listChatSessions();
1524
+ const exact = sessions.find((session) => session.id === input);
1525
+ if (exact) return exact.id;
1526
+ const matches = sessions.filter((session) => session.id.startsWith(input));
1527
+ if (matches.length === 1) return matches[0].id;
1528
+ if (matches.length > 1) {
1529
+ throw new Error(`Ambiguous session id prefix "${input}" (${matches.length} matches)`);
1530
+ }
1531
+ return null;
1532
+ };
1533
+
1534
+ // src/commands/chat/ask.ts
1535
+ var registerChatAsk = (program2) => {
1536
+ program2.command("ask").description("Ask a question in a chat session").argument("<session>", "Chat session id or prefix").argument("<question>", "Question to ask").option("--top-k <n>", "Number of passages to retrieve", "5").option("--max-chapter <n>", "Spoiler-free limit (0-based within narrative)").action(async (sessionId, question, options) => {
1537
+ const { topK, maxChapter } = parseQueryOptions(options);
1538
+ const resolvedId = await resolveChatSessionId(sessionId);
1539
+ if (!resolvedId) {
1540
+ throw new Error(`Chat session not found: ${sessionId}`);
1541
+ }
1542
+ const { answer, sources } = await chatAsk(resolvedId, question, { topK, maxChapter });
1543
+ stdout(answer);
1544
+ if (sources.length > 0) {
1545
+ stdout("\nSources:");
1546
+ sources.forEach((match, index) => {
1547
+ const title = match.chapterTitle || `Chapter ${match.chapterIndex + 1}`;
1548
+ const excerpt = match.content.slice(0, 120).replace(/\s+/g, " ");
1549
+ stdout(`[${index + 1}] ${title}: ${excerpt}`);
1550
+ });
1551
+ }
1552
+ });
1553
+ };
1554
+
1555
+ // src/commands/chat/list.ts
1556
+ var formatDate2 = (timestamp) => {
1557
+ if (!timestamp) return "-";
1558
+ return new Date(timestamp).toISOString().slice(0, 10);
1559
+ };
1560
+ var registerChatList = (program2) => {
1561
+ program2.command("list").description("List chat sessions").action(async () => {
1562
+ const sessions = await listSessions();
1563
+ if (sessions.length === 0) {
1564
+ stdout("No chat sessions yet.");
1565
+ return;
1566
+ }
1567
+ stdout("ID | Book | Updated | Title");
1568
+ stdout("---------|------|---------|------");
1569
+ for (const session of sessions) {
1570
+ const shortId = session.id.slice(0, 8);
1571
+ const book = session.bookTitle || session.bookId.slice(0, 8);
1572
+ const updated = formatDate2(session.updatedAt);
1573
+ const title = session.title || "-";
1574
+ stdout(`${shortId} | ${book} | ${updated} | ${title}`);
1575
+ }
1576
+ });
1577
+ };
1578
+
1579
+ // src/commands/chat/show.ts
1580
+ var registerChatShow = (program2) => {
1581
+ program2.command("show").description("Show chat session details").argument("<session>", "Chat session id or prefix").option("--tail <n>", "Show last N messages", "10").action(async (sessionId, options) => {
1582
+ const tail = Number(options.tail);
1583
+ if (!Number.isFinite(tail) || tail <= 0) {
1584
+ throw new Error("--tail must be a positive number.");
1585
+ }
1586
+ const resolvedId = await resolveChatSessionId(sessionId);
1587
+ if (!resolvedId) {
1588
+ throw new Error(`Chat session not found: ${sessionId}`);
1589
+ }
1590
+ const session = await getSession(resolvedId);
1591
+ if (!session) {
1592
+ throw new Error(`Chat session not found: ${sessionId}`);
1593
+ }
1594
+ stdout(`ID: ${session.id}`);
1595
+ stdout(`Book ID: ${session.bookId}`);
1596
+ stdout(`Title: ${session.title ?? "-"}`);
1597
+ const updated = session.updatedAt ? new Date(session.updatedAt).toISOString() : "-";
1598
+ stdout(`Updated: ${updated}`);
1599
+ const messages = await getSessionMessages(resolvedId, tail);
1600
+ if (messages.length === 0) {
1601
+ stdout("\nNo messages yet.");
1602
+ return;
1603
+ }
1604
+ stdout("\nMessages:");
1605
+ messages.forEach((message) => {
1606
+ stdout(`[${message.role}] ${message.content}`);
1607
+ });
1608
+ });
1609
+ };
1610
+
1611
+ // src/commands/chat/repl.ts
1612
+ var shouldExit = (input) => {
1613
+ const normalized = input.trim().toLowerCase();
1614
+ return normalized === "exit" || normalized === "quit" || normalized === ":q";
1615
+ };
1616
+ var registerChatRepl = (program2) => {
1617
+ program2.command("repl").description("Start interactive chat session").argument("<session>", "Chat session id or prefix").option("--top-k <n>", "Number of passages to retrieve", "5").option("--max-chapter <n>", "Spoiler-free limit (0-based within narrative)").action(async (sessionId, options) => {
1618
+ if (!isInteractive()) {
1619
+ throw new Error("Chat repl requires an interactive terminal.");
1620
+ }
1621
+ const { topK, maxChapter } = parseQueryOptions(options);
1622
+ const resolvedId = await resolveChatSessionId(sessionId);
1623
+ if (!resolvedId) {
1624
+ throw new Error(`Chat session not found: ${sessionId}`);
1625
+ }
1626
+ const session = await getSession(resolvedId);
1627
+ if (!session) {
1628
+ throw new Error(`Chat session not found: ${sessionId}`);
1629
+ }
1630
+ stdout(`Chatting in session ${session.id}. Type 'exit' to quit.`);
1631
+ while (true) {
1632
+ const question = await prompt("You: ");
1633
+ if (!question.trim()) continue;
1634
+ if (shouldExit(question)) break;
1635
+ const { answer, sources } = await chatAsk(session.id, question, { topK, maxChapter });
1636
+ stdout(`
1637
+ ${answer}`);
1638
+ if (sources.length > 0) {
1639
+ stdout("\nSources:");
1640
+ sources.forEach((match, index) => {
1641
+ const title = match.chapterTitle || `Chapter ${match.chapterIndex + 1}`;
1642
+ const excerpt = match.content.slice(0, 120).replace(/\s+/g, " ");
1643
+ stdout(`[${index + 1}] ${title}: ${excerpt}`);
1644
+ });
1645
+ }
1646
+ stdout("");
1647
+ }
1648
+ });
1649
+ };
1650
+
1651
+ // src/commands/chat/index.ts
1652
+ var registerChatCommands = (program2) => {
1653
+ const chat = program2.command("chat").description("Run multi-turn chat sessions");
1654
+ registerChatStart(chat);
1655
+ registerChatAsk(chat);
1656
+ registerChatList(chat);
1657
+ registerChatShow(chat);
1658
+ registerChatRepl(chat);
1659
+ };
1660
+
1272
1661
  // src/cli.ts
1273
1662
  var resolveVersion = async () => {
1274
1663
  try {
@@ -1302,6 +1691,7 @@ var registerCommands = () => {
1302
1691
  registerConfigInit(config);
1303
1692
  registerConfigResolve(config);
1304
1693
  registerConfigOnboard(config);
1694
+ registerChatCommands(program);
1305
1695
  };
1306
1696
  program.exitOverride((error) => {
1307
1697
  if (error.code === "commander.helpDisplayed") {
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/config.ts","../src/commands/io.ts","../src/services/epub-parser.ts","../src/services/constants.ts","../src/services/ingest.ts","../src/services/chunker.ts","../src/services/embedder.ts","../src/services/vector-store.ts","../src/services/summarizer.ts","../src/db/schema.ts","../src/db/queries.ts","../src/commands/ingest.ts","../src/commands/prompt.ts","../src/commands/book/ingest.ts","../src/commands/list.ts","../src/commands/book/list.ts","../src/commands/utils.ts","../src/commands/show.ts","../src/commands/book/show.ts","../src/commands/ask.ts","../src/commands/book/ask.ts","../src/commands/search.ts","../src/commands/book/search.ts","../src/commands/delete.ts","../src/commands/book/delete.ts","../src/commands/config.ts","../src/commands/config/path.ts","../src/commands/init-config.ts","../src/commands/config/init.ts","../src/commands/resolve-config.ts","../src/commands/config/resolve.ts","../src/commands/onboard.ts","../src/commands/config/onboard.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport { setConfigOverrides } from \"./config\";\nimport { printError } from \"./commands/io\";\nimport { readFile } from \"node:fs/promises\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { registerBookIngest } from \"./commands/book/ingest\";\nimport { registerBookList } from \"./commands/book/list\";\nimport { registerBookShow } from \"./commands/book/show\";\nimport { registerBookAsk } from \"./commands/book/ask\";\nimport { registerBookSearch } from \"./commands/book/search\";\nimport { registerBookDelete } from \"./commands/book/delete\";\nimport { registerConfigPath } from \"./commands/config/path\";\nimport { registerConfigInit } from \"./commands/config/init\";\nimport { registerConfigResolve } from \"./commands/config/resolve\";\nimport { registerConfigOnboard } from \"./commands/config/onboard\";\n\nconst resolveVersion = async () => {\n try {\n const currentDir = dirname(fileURLToPath(import.meta.url));\n const pkgPath = resolve(currentDir, \"../package.json\");\n const raw = await readFile(pkgPath, \"utf-8\");\n return JSON.parse(raw).version || \"0.1.0\";\n } catch {\n return \"0.1.0\";\n }\n};\n\nconst program = new Command();\nconst configureProgram = async () => {\n program\n .name(\"mycroft\")\n .description(\"Ingest EPUBs, build a local index, and answer questions\")\n .version(await resolveVersion())\n .option(\"--data-dir <path>\", \"Override data directory\")\n .hook(\"preAction\", (cmd) => {\n const opts = cmd.opts();\n if (opts.dataDir) {\n setConfigOverrides({ dataDir: opts.dataDir });\n }\n });\n};\n\nconst registerCommands = () => {\n const book = program.command(\"book\").description(\"Manage books and queries\");\n registerBookIngest(book);\n registerBookList(book);\n registerBookShow(book);\n registerBookAsk(book);\n registerBookSearch(book);\n registerBookDelete(book);\n\n const config = program.command(\"config\").description(\"Manage configuration\");\n registerConfigPath(config);\n registerConfigInit(config);\n registerConfigResolve(config);\n registerConfigOnboard(config);\n};\n\nprogram.exitOverride((error) => {\n if (error.code === \"commander.helpDisplayed\") {\n process.exit(0);\n }\n throw error;\n});\n\nconst main = async () => {\n try {\n await configureProgram();\n registerCommands();\n await program.parseAsync(process.argv);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n printError(message);\n process.exit(1);\n }\n};\n\nmain();\n","import { mkdir, readFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\n\nexport type ConfigModels = {\n embedding: string;\n summary: string;\n chat: string;\n};\n\nexport type AppConfig = {\n dataDir: string;\n askEnabled: boolean;\n models: ConfigModels;\n};\n\nconst DEFAULT_CONFIG: AppConfig = {\n dataDir: \"~/.local/share/mycroft\",\n askEnabled: true,\n models: {\n embedding: \"text-embedding-3-small\",\n summary: \"gpt-5-nano\",\n chat: \"gpt-5.1\",\n },\n};\n\nconst expandHome = (input: string): string => {\n if (!input.startsWith(\"~\")) return input;\n return join(homedir(), input.slice(1));\n};\n\nconst resolvePath = (input: string): string => resolve(expandHome(input));\n\nconst getConfigPath = (): string => {\n const override = process.env.MYCROFT_CONFIG;\n if (override) return resolvePath(override);\n return resolvePath(\"~/.config/mycroft/config.json\");\n};\n\nconst normalizeModels = (models?: Partial<ConfigModels>): ConfigModels => ({\n embedding: models?.embedding || DEFAULT_CONFIG.models.embedding,\n summary: models?.summary || DEFAULT_CONFIG.models.summary,\n chat: models?.chat || DEFAULT_CONFIG.models.chat,\n});\n\ntype ConfigOverrides = {\n dataDir?: string;\n};\n\nlet overrides: ConfigOverrides = {};\n\nexport const setConfigOverrides = (next: ConfigOverrides) => {\n overrides = { ...overrides, ...next };\n};\n\nconst normalizeConfig = (input: Partial<AppConfig> | null): AppConfig => {\n const dataDirEnv = process.env.MYCROFT_DATA_DIR;\n const dataDir = overrides.dataDir || dataDirEnv || input?.dataDir || DEFAULT_CONFIG.dataDir;\n return {\n dataDir,\n askEnabled: input?.askEnabled ?? DEFAULT_CONFIG.askEnabled,\n models: normalizeModels(input?.models),\n };\n};\n\nconst readConfigFile = async (path: string): Promise<Partial<AppConfig> | null> => {\n try {\n const contents = await readFile(path, \"utf-8\");\n return JSON.parse(contents) as Partial<AppConfig>;\n } catch {\n return null;\n }\n};\n\nexport const loadConfig = async (): Promise<AppConfig> => {\n const configPath = getConfigPath();\n const data = await readConfigFile(configPath);\n const normalized = normalizeConfig(data);\n return {\n ...normalized,\n dataDir: resolvePath(normalized.dataDir),\n };\n};\n\nexport const ensureConfigDirs = async (configPath?: string) => {\n const path = configPath || getConfigPath();\n await mkdir(dirname(path), { recursive: true });\n};\n\nexport const configPath = () => getConfigPath();\n","import chalk from \"chalk\";\n\nconst isTTY = () => Boolean(process.stdout.isTTY);\nexport const isInteractive = () => Boolean(process.stdin.isTTY && process.stdout.isTTY);\n\nexport const formatDim = (text: string) => (isTTY() ? chalk.dim(text) : text);\nexport const formatError = (text: string) => (isTTY() ? chalk.red(text) : text);\nexport const formatBold = (text: string) => (isTTY() ? chalk.bold(text) : text);\nexport const formatWarn = (text: string) => (isTTY() ? chalk.yellow(text) : text);\n\nexport const stdout = (message: string) => {\n process.stdout.write(message.endsWith(\"\\n\") ? message : `${message}\\n`);\n};\n\nexport const stderr = (message: string) => {\n process.stderr.write(message.endsWith(\"\\n\") ? message : `${message}\\n`);\n};\n\nexport const printError = (message: string) => {\n stderr(formatError(`Error: ${message}`));\n};\n\nexport const logInfo = (message: string) => {\n stderr(message);\n};\n\nexport const logWarn = (message: string) => {\n stderr(formatWarn(message));\n};\n\nexport const handleSigint = (onCancel?: () => void) => {\n const handler = () => {\n if (onCancel) onCancel();\n stderr(\"\\nCancelled.\");\n process.exit(130);\n };\n process.once(\"SIGINT\", handler);\n return () => process.off(\"SIGINT\", handler);\n};\n","import { initEpubFile } from \"@lingo-reader/epub-parser\";\nimport { basename } from \"node:path\";\nimport type { Chapter } from \"../shared/types\";\nimport { logInfo } from \"./constants\";\n\nexport type ParsedBook = {\n title: string;\n author: string | null;\n coverImagePath: string | null;\n chapters: Chapter[];\n chapterTitles: string[];\n narrativeStartIndex: number;\n narrativeEndIndex: number;\n};\n\nconst detectNarrativeBoundaries = (chapterTitles: string[]): { start: number; end: number } => {\n const frontMatterPattern = /^(about|contents|table of contents|dedication|preface|foreword|title|half.?title|copyright|epigraph|frontispiece|map)/i;\n const backMatterPattern = /^(acknowledgment|afterword|appendix|glossary|index|bibliography|about the author|also by|praise|copyright page|notes|bonus|preview|excerpt|major characters|locations)/i;\n const narrativePattern = /^(I|II|III|IV|V|VI|VII|VIII|IX|X|XI|XII|1|2|3|4|5|6|7|8|9|one|two|three|chapter|prologue|epilogue|part\\s)/i;\n\n let start = 0;\n let end = chapterTitles.length - 1;\n\n for (let i = 0; i < chapterTitles.length; i++) {\n const title = chapterTitles[i]?.trim() || \"\";\n\n if (narrativePattern.test(title) && !frontMatterPattern.test(title)) {\n start = i;\n break;\n }\n\n if (!frontMatterPattern.test(title) && title.length > 0) {\n if (title.length > 3) {\n start = i;\n break;\n }\n }\n }\n\n for (let i = chapterTitles.length - 1; i >= start; i--) {\n const title = chapterTitles[i]?.trim() || \"\";\n if (!backMatterPattern.test(title)) {\n end = i;\n break;\n }\n }\n\n logInfo(`[EPUB Parser] Detected narrative boundaries: chapters ${start} to ${end} (out of ${chapterTitles.length} total)`);\n if (start > 0) {\n logInfo(`[EPUB Parser] Front matter: ${chapterTitles.slice(0, start).join(\", \")}`);\n }\n if (end < chapterTitles.length - 1) {\n logInfo(`[EPUB Parser] Back matter: ${chapterTitles.slice(end + 1).join(\", \")}`);\n }\n\n return { start, end };\n};\n\nconst stripHtml = (html: string) =>\n html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, \" \")\n .replace(/<style[\\s\\S]*?<\\/style>/gi, \" \")\n .replace(/<[^>]+>/g, \" \")\n .replace(/&nbsp;/g, \" \")\n .replace(/&amp;/g, \"&\")\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .replace(/\\s+/g, \" \")\n .trim();\n\nconst originalWarn = console.warn;\nconst createWarnFilter = () => {\n const suppressedWarnings: string[] = [];\n console.warn = (msg: any, ...args: unknown[]) => {\n if (typeof msg === \"string\" && msg.includes(\"No element with id\") && msg.includes(\"parsing <metadata>\")) {\n suppressedWarnings.push(msg);\n return;\n }\n originalWarn(msg, ...args);\n };\n return suppressedWarnings;\n};\n\nexport const parseEpub = async (epubPath: string, resourceSaveDir?: string): Promise<ParsedBook> => {\n logInfo(`[EPUB Parser] Starting parse for: ${basename(epubPath)}`);\n\n const suppressedWarnings = createWarnFilter();\n\n try {\n const epubFile = await initEpubFile(epubPath, resourceSaveDir);\n await epubFile.loadEpub();\n logInfo(`[EPUB Parser] EPUB loaded successfully`);\n\n await epubFile.parse();\n\n if (suppressedWarnings.length > 0) {\n logInfo(`[EPUB Parser] Suppressed ${suppressedWarnings.length} metadata warnings (non-critical)`);\n }\n logInfo(`[EPUB Parser] Parse completed`);\n\n const fileBaseName = basename(epubPath, \".epub\");\n type EpubMetadata = ReturnType<typeof epubFile.getMetadata>;\n let metadata: EpubMetadata | null = null;\n try {\n metadata = epubFile.getMetadata();\n } catch {\n metadata = null;\n }\n const safeMetadata = metadata ?? ({} as EpubMetadata);\n const spine = epubFile.getSpine();\n const toc = epubFile.getToc();\n\n logInfo(`[EPUB Parser] Found ${spine.length} spine items, ${toc.length} TOC entries`);\n\n const titleById = new Map<string, string>();\n const walkToc = (items: typeof toc) => {\n items.forEach((item: (typeof toc)[number]) => {\n const resolved = epubFile.resolveHref(item.href);\n if (resolved?.id) titleById.set(resolved.id, item.label);\n if (item.children?.length) walkToc(item.children);\n });\n };\n walkToc(toc);\n const coverImagePath = epubFile.getCoverImage() || null;\n\n const chapters: Chapter[] = [];\n const chapterTitles: string[] = [];\n for (const [index, item] of spine.entries()) {\n const chapter = await epubFile.loadChapter(item.id);\n const content = stripHtml(chapter.html);\n if (!content) continue;\n const chapterTitle = titleById.get(item.id) || item.id || `Chapter ${index + 1}`;\n chapters.push({\n title: chapterTitle,\n content,\n });\n chapterTitles.push(chapterTitle);\n }\n\n epubFile.destroy();\n\n const author = safeMetadata.creator?.[0]?.contributor ?? null;\n\n logInfo(`[EPUB Parser] Extracted ${chapters.length} chapters with content`);\n logInfo(`[EPUB Parser] Title: \"${safeMetadata.title || fileBaseName || \"Untitled\"}\", Author: \"${author || \"Unknown\"}\"`);\n\n const { start: narrativeStartIndex, end: narrativeEndIndex } = detectNarrativeBoundaries(chapterTitles);\n\n return {\n title: safeMetadata.title || fileBaseName || \"Untitled\",\n author,\n coverImagePath,\n chapters,\n chapterTitles,\n narrativeStartIndex,\n narrativeEndIndex,\n };\n } finally {\n console.warn = originalWarn;\n }\n};\n","import { mkdir } from \"node:fs/promises\";\nimport { loadConfig } from \"../config\";\nimport { logInfo, logWarn } from \"../commands/io\";\n\nexport const CHUNK_SIZE: number = 1000;\nexport const CHUNK_OVERLAP: number = 100;\nexport const SEPARATORS = [\"\\n\\n\", \"\\n\", \". \", \" \", \"\"] as const;\n\nexport const SUMMARY_MAX_TOKENS = 30000;\nexport const SUMMARY_CONCURRENCY = 3;\nexport const SUMMARY_TARGET_WORDS = 250;\n\nexport type ResolvedPaths = {\n dataDir: string;\n booksDir: string;\n vectorsDir: string;\n dbPath: string;\n};\n\nexport const resolvePaths = async (): Promise<ResolvedPaths> => {\n const config = await loadConfig();\n const dataDir = config.dataDir;\n return {\n dataDir,\n booksDir: `${dataDir}/books`,\n vectorsDir: `${dataDir}/vectors`,\n dbPath: `${dataDir}/metadata.db`,\n };\n};\n\nexport const ensureDataDirs = async () => {\n const paths = await resolvePaths();\n await mkdir(paths.dataDir, { recursive: true });\n await mkdir(paths.booksDir, { recursive: true });\n await mkdir(paths.vectorsDir, { recursive: true });\n return paths;\n};\n\nexport const getModels = async () => {\n const config = await loadConfig();\n return config.models;\n};\n\nexport const isAskEnabled = async () => {\n const config = await loadConfig();\n return config.askEnabled;\n};\n\nexport const requireOpenAIKey = () => {\n if (!process.env.OPENAI_API_KEY) {\n throw new Error(\"OPENAI_API_KEY is not set. Export it to use embeddings and chat.\");\n }\n};\n\nexport { logInfo, logWarn };\n","import { randomUUID } from \"node:crypto\";\nimport { mkdir, unlink, copyFile } from \"node:fs/promises\";\nimport { parseEpub } from \"./epub-parser\";\nimport { chunkChapters } from \"./chunker\";\nimport { embedChunks } from \"./embedder\";\nimport { addChunksToIndex, deleteBookIndex } from \"./vector-store\";\nimport { summarizeAllChapters } from \"./summarizer\";\nimport { ensureDataDirs, logInfo, logWarn } from \"./constants\";\nimport { deleteBook, insertBook, updateBook } from \"../db/queries\";\nimport type { BookChunk } from \"../shared/types\";\n\nconst formatDuration = (ms: number) => {\n const seconds = Math.round(ms / 100) / 10;\n return `${seconds}s`;\n};\n\nexport const ingestEpub = async (\n filePath: string,\n selectedChapterIndices?: number[],\n options?: { summarize?: boolean }\n) => {\n const bookId = randomUUID();\n const paths = await ensureDataDirs();\n const fileName = `${bookId}.epub`;\n const bookPath = `${paths.booksDir}/${fileName}`;\n\n logInfo(`[Ingest] Starting ingestion for book ${bookId}`);\n\n await mkdir(paths.booksDir, { recursive: true });\n await copyFile(filePath, bookPath);\n logInfo(`[Ingest] EPUB file saved to ${bookPath}`);\n\n const parseStart = Date.now();\n const parsed = await parseEpub(bookPath);\n logInfo(`[Ingest] Parsed \"${parsed.title}\" with ${parsed.chapters.length} chapters (${formatDuration(Date.now() - parseStart)})`);\n logInfo(`[Ingest] Narrative chapters: ${parsed.narrativeStartIndex} to ${parsed.narrativeEndIndex}`);\n\n await insertBook({\n id: bookId,\n title: parsed.title,\n author: parsed.author,\n coverPath: parsed.coverImagePath,\n epubPath: bookPath,\n chapters: parsed.chapterTitles,\n narrativeStartIndex: parsed.narrativeStartIndex,\n narrativeEndIndex: parsed.narrativeEndIndex,\n });\n logInfo(`[Ingest] Book record inserted into database`);\n\n try {\n const chaptersToProcess = selectedChapterIndices\n ? parsed.chapters.filter((_, index) => selectedChapterIndices.includes(index))\n : parsed.chapters.slice(parsed.narrativeStartIndex, parsed.narrativeEndIndex + 1);\n\n const selectedIndices = selectedChapterIndices ||\n Array.from({ length: parsed.narrativeEndIndex - parsed.narrativeStartIndex + 1 },\n (_, i) => i + parsed.narrativeStartIndex);\n\n logInfo(`[Ingest] Processing ${chaptersToProcess.length} selected chapters (indices: ${selectedIndices.join(\", \")})`);\n\n let adjustedSummaries: BookChunk[] = [];\n if (options?.summarize !== false) {\n logInfo(`[Ingest] Generating summaries for ${chaptersToProcess.length} chapters...`);\n const summarizeStart = Date.now();\n const summaries = await summarizeAllChapters(chaptersToProcess);\n logInfo(`[Ingest] Generated ${summaries.length}/${chaptersToProcess.length} summaries (${formatDuration(Date.now() - summarizeStart)})`);\n\n const summaryRecords = summaries.map((s, idx) => ({\n ...s,\n chapterIndex: selectedIndices[idx] ?? s.chapterIndex,\n }));\n\n await updateBook(bookId, {\n summaries: JSON.stringify(summaryRecords),\n });\n\n adjustedSummaries = summaryRecords.map((s) => ({\n id: `${bookId}-summary-${s.chapterIndex}`,\n bookId,\n chapterIndex: s.chapterIndex,\n chapterTitle: s.chapterTitle,\n chunkIndex: -1,\n content: s.fullSummary,\n type: \"summary\" as const,\n }));\n logInfo(`[Ingest] Created ${adjustedSummaries.length} summary chunks`);\n }\n\n const chunksToProcess = parsed.chapters.map((chapter, index) =>\n selectedIndices.includes(index) ? chapter : { title: chapter.title, content: \"\" }\n );\n const chunks = chunkChapters(bookId, chunksToProcess).filter((chunk) => chunk.content.length > 0);\n logInfo(`[Ingest] Created ${chunks.length} chunks from selected chapters`);\n\n const allChunks = [...chunks, ...adjustedSummaries];\n const embedStart = Date.now();\n const embedded = await embedChunks(allChunks);\n logInfo(`[Ingest] Embedded ${embedded.length} total chunks (${formatDuration(Date.now() - embedStart)})`);\n\n await addChunksToIndex(bookId, embedded);\n logInfo(`[Ingest] Added chunks to vector index`);\n\n await updateBook(bookId, { chunkCount: embedded.length, indexedAt: Date.now() });\n logInfo(`[Ingest] Updated book record with chunk count: ${embedded.length}`);\n } catch (error) {\n logWarn(`[Ingest] Error during chunking/embedding: ${error instanceof Error ? error.message : String(error)}`);\n await deleteBookIndex(bookId);\n await unlink(bookPath).catch(() => undefined);\n await deleteBook(bookId).catch(() => undefined);\n throw error;\n }\n\n logInfo(`[Ingest] Ingestion complete for ${bookId}`);\n return { id: bookId };\n};\n","import type { Chapter, BookChunk } from \"../shared/types\";\nimport { CHUNK_OVERLAP, CHUNK_SIZE, SEPARATORS } from \"./constants\";\n\nconst splitRecursive = (text: string, separators: readonly string[]): string[] => {\n if (text.length <= CHUNK_SIZE || separators.length === 0) return [text];\n const [separator, ...rest] = separators;\n if (!separator) return [text];\n\n const parts = text.split(separator);\n if (parts.length === 1) return splitRecursive(text, rest);\n\n const chunks: string[] = [];\n let current = \"\";\n\n for (const part of parts) {\n const next = current ? `${current}${separator}${part}` : part;\n if (next.length <= CHUNK_SIZE) {\n current = next;\n continue;\n }\n\n if (current) chunks.push(current);\n current = part;\n }\n\n if (current) chunks.push(current);\n\n const refined: string[] = [];\n for (const chunk of chunks) {\n if (chunk.length <= CHUNK_SIZE) {\n refined.push(chunk);\n continue;\n }\n refined.push(...splitRecursive(chunk, rest));\n }\n\n return refined;\n};\n\nconst withOverlap = (chunks: string[]): string[] => {\n if (chunks.length <= 1 || CHUNK_OVERLAP === 0) return chunks;\n\n const merged: string[] = [];\n for (let i = 0; i < chunks.length; i += 1) {\n const current = chunks[i] ?? \"\";\n const previous = merged[merged.length - 1];\n if (!previous) {\n merged.push(current);\n continue;\n }\n\n const overlap = previous.slice(-CHUNK_OVERLAP);\n merged.push(`${overlap}${current}`);\n }\n\n return merged;\n};\n\nexport const chunkChapters = (bookId: string, chapters: Chapter[]): BookChunk[] => {\n const chunks: BookChunk[] = [];\n\n chapters.forEach((chapter, chapterIndex) => {\n const trimmed = chapter.content.trim();\n if (!trimmed) return;\n\n const rawChunks = splitRecursive(trimmed, SEPARATORS);\n const overlapped = withOverlap(rawChunks);\n overlapped.forEach((content, chunkIndex) => {\n const normalized = content.replace(/\\s+/g, \" \").trim();\n if (!normalized) return;\n chunks.push({\n id: `${bookId}-${chapterIndex}-${chunkIndex}`,\n bookId,\n chapterIndex,\n chapterTitle: chapter.title,\n chunkIndex,\n content: normalized,\n });\n });\n });\n\n return chunks;\n};\n","import { embedMany } from \"ai\";\nimport { openai } from \"@ai-sdk/openai\";\nimport type { BookChunk } from \"../shared/types\";\nimport { getModels, logInfo } from \"./constants\";\n\nexport type EmbeddedChunk = BookChunk & {\n vector: number[];\n};\n\nconst MAX_TOKENS_PER_BATCH = 250_000;\nconst CHARS_PER_TOKEN = 4;\n\nexport const embedChunks = async (chunks: BookChunk[]): Promise<EmbeddedChunk[]> => {\n if (chunks.length === 0) return [];\n\n const batches: BookChunk[][] = [];\n let currentBatch: BookChunk[] = [];\n let currentTokens = 0;\n\n for (const chunk of chunks) {\n const estimatedTokens = Math.ceil(chunk.content.length / CHARS_PER_TOKEN);\n\n if (currentTokens + estimatedTokens > MAX_TOKENS_PER_BATCH && currentBatch.length > 0) {\n batches.push(currentBatch);\n currentBatch = [];\n currentTokens = 0;\n }\n\n currentBatch.push(chunk);\n currentTokens += estimatedTokens;\n }\n\n if (currentBatch.length > 0) {\n batches.push(currentBatch);\n }\n\n logInfo(`[Embedder] Processing ${chunks.length} chunks in ${batches.length} batch(es)`);\n\n const allEmbedded: EmbeddedChunk[] = [];\n const models = await getModels();\n\n for (let i = 0; i < batches.length; i++) {\n const batch = batches[i]!;\n const estimatedTokens = batch.reduce((sum, c) => sum + Math.ceil(c.content.length / CHARS_PER_TOKEN), 0);\n\n logInfo(`[Embedder] Batch ${i + 1}/${batches.length}: ${batch.length} chunks (~${estimatedTokens.toLocaleString()} tokens)`);\n\n const { embeddings } = await embedMany({\n model: openai.embeddingModel(models.embedding),\n values: batch.map((chunk) => chunk.content),\n });\n\n for (let j = 0; j < batch.length; j++) {\n allEmbedded.push({\n ...batch[j]!,\n vector: embeddings[j] ?? [],\n });\n }\n }\n\n logInfo(`[Embedder] Successfully embedded all ${allEmbedded.length} chunks`);\n\n return allEmbedded;\n};\n","import { LocalIndex } from \"vectra\";\nimport type { EmbeddedChunk } from \"./embedder\";\nimport type { BookChunk } from \"../shared/types\";\nimport { ensureDataDirs } from \"./constants\";\n\nexport type VectorMetadata = {\n bookId: string;\n chapterIndex: number;\n chapterTitle: string;\n chunkIndex: number;\n content: string;\n type?: \"chunk\" | \"summary\";\n};\n\nconst indexPathForBook = async (bookId: string) => {\n const paths = await ensureDataDirs();\n return `${paths.vectorsDir}/${bookId}`;\n};\n\nexport const createBookIndex = async (bookId: string): Promise<LocalIndex<VectorMetadata>> => {\n const index = new LocalIndex<VectorMetadata>(await indexPathForBook(bookId));\n const exists = await index.isIndexCreated();\n if (!exists) {\n await index.createIndex({\n version: 1,\n metadata_config: {\n indexed: [\"bookId\"],\n },\n });\n }\n return index;\n};\n\nexport const addChunksToIndex = async (bookId: string, chunks: EmbeddedChunk[]) => {\n const index = await createBookIndex(bookId);\n await index.batchInsertItems(\n chunks.map((chunk) => ({\n id: chunk.id,\n vector: chunk.vector,\n metadata: {\n bookId: chunk.bookId,\n chapterIndex: chunk.chapterIndex,\n chapterTitle: chunk.chapterTitle,\n chunkIndex: chunk.chunkIndex,\n content: chunk.content,\n type: chunk.type || \"chunk\",\n },\n }))\n );\n};\n\nexport const queryBookIndex = async (\n bookId: string,\n queryVector: number[],\n queryText: string,\n topK: number,\n maxChapterIndex?: number\n): Promise<(BookChunk & { score: number })[]> => {\n const index = await createBookIndex(bookId);\n const expandedTopK =\n maxChapterIndex === undefined || maxChapterIndex === null ? topK : Math.max(topK * 4, topK);\n const results = await index.queryItems(queryVector, queryText, expandedTopK);\n\n const mapped = results.map((result: (typeof results)[number]) => ({\n id: result.item.id ?? \"\",\n bookId,\n chapterIndex: result.item.metadata?.chapterIndex ?? 0,\n chapterTitle: result.item.metadata?.chapterTitle ?? \"\",\n chunkIndex: result.item.metadata?.chunkIndex ?? 0,\n content: result.item.metadata?.content ?? \"\",\n type: result.item.metadata?.type as \"chunk\" | \"summary\" | undefined,\n score: result.score,\n }));\n\n if (maxChapterIndex === undefined || maxChapterIndex === null) {\n return mapped.slice(0, topK);\n }\n\n return mapped.filter((item: (typeof mapped)[number]) => item.chapterIndex <= maxChapterIndex).slice(0, topK);\n};\n\nexport const deleteBookIndex = async (bookId: string) => {\n const index = new LocalIndex<VectorMetadata>(await indexPathForBook(bookId));\n const exists = await index.isIndexCreated();\n if (!exists) return;\n await index.deleteIndex();\n};\n","import { generateText } from \"ai\";\nimport { openai } from \"@ai-sdk/openai\";\nimport type { Chapter, ChapterSummary } from \"../shared/types\";\nimport { SUMMARY_MAX_TOKENS, SUMMARY_CONCURRENCY, SUMMARY_TARGET_WORDS, getModels, logInfo, logWarn } from \"./constants\";\n\nconst CHARS_PER_TOKEN = 4;\n\nconst estimateTokens = (text: string): number => Math.ceil(text.length / CHARS_PER_TOKEN);\n\nconst SUMMARY_PROMPT = (title: string, chapterNum: number, content: string) => `You are analyzing a chapter from a book (fiction or nonfiction). Extract key information to help readers understand the chapter's content.\n\nChapter Title: ${title}\nChapter Number: ${chapterNum}\n\n---\n${content}\n---\n\nExtract the following information and respond ONLY with valid JSON (no markdown, no code blocks):\n\n{\n \"characters\": [\"Name - brief description (role, traits, first appearance)\", ...],\n \"events\": \"What happens in this chapter? (2-3 sentences)\",\n \"setting\": \"Where does this chapter take place?\",\n \"revelations\": \"Any important information revealed? (secrets, backstory, foreshadowing)\"\n}\n\nKeep the total response around ${SUMMARY_TARGET_WORDS} words.`;\n\ntype SummaryJSON = {\n characters: string[];\n events: string;\n setting: string;\n revelations: string;\n};\n\nconst splitIntoSections = (text: string, maxTokens: number): string[] => {\n const estimatedTokens = estimateTokens(text);\n\n if (estimatedTokens <= maxTokens) {\n return [text];\n }\n\n const numSections = Math.ceil(estimatedTokens / maxTokens);\n const charsPerSection = Math.floor(text.length / numSections);\n const sections: string[] = [];\n\n for (let i = 0; i < numSections; i++) {\n const start = i * charsPerSection;\n const end = i === numSections - 1 ? text.length : (i + 1) * charsPerSection;\n sections.push(text.slice(start, end));\n }\n\n return sections;\n};\n\nconst summarizeSection = async (text: string, title: string, sectionNum: number): Promise<string> => {\n const models = await getModels();\n const { text: summary } = await generateText({\n model: openai(models.summary),\n prompt: `Summarize this section from chapter \"${title}\" (Part ${sectionNum}). Focus on key events, characters, and revelations. Keep it concise (100-150 words):\\n\\n${text}`,\n temperature: 0.3,\n });\n\n return summary;\n};\n\nconst generateStructuredSummary = async (\n content: string,\n title: string,\n chapterIndex: number\n): Promise<ChapterSummary | null> => {\n try {\n const models = await getModels();\n const { text } = await generateText({\n model: openai(models.summary),\n prompt: SUMMARY_PROMPT(title, chapterIndex + 1, content),\n temperature: 0.3,\n });\n\n let jsonText = text.trim();\n if (jsonText.startsWith(\"```json\")) {\n jsonText = jsonText.slice(7, -3).trim();\n } else if (jsonText.startsWith(\"```\")) {\n jsonText = jsonText.slice(3, -3).trim();\n }\n\n const parsed: SummaryJSON = JSON.parse(jsonText);\n\n const fullSummary = `Chapter ${chapterIndex + 1}: ${title}\n\nCharacters: ${parsed.characters.join(\", \")}\n\nEvents: ${parsed.events}\n\nSetting: ${parsed.setting}\n\nRevelations: ${parsed.revelations}`;\n\n return {\n chapterIndex,\n chapterTitle: title,\n characters: parsed.characters,\n events: parsed.events,\n setting: parsed.setting,\n revelations: parsed.revelations,\n fullSummary,\n };\n } catch (error) {\n logWarn(`[Summarizer] Failed to parse summary JSON for \"${title}\": ${error instanceof Error ? error.message : String(error)}`);\n return null;\n }\n};\n\nexport const summarizeChapter = async (\n chapter: Chapter,\n chapterIndex: number\n): Promise<ChapterSummary | null> => {\n const tokens = estimateTokens(chapter.content);\n\n logInfo(`[Summarizer] Chapter ${chapterIndex + 1} \"${chapter.title}\": ~${tokens.toLocaleString()} tokens`);\n\n try {\n if (tokens < SUMMARY_MAX_TOKENS) {\n return await generateStructuredSummary(chapter.content, chapter.title, chapterIndex);\n }\n\n logInfo(`[Summarizer] Chapter ${chapterIndex + 1} exceeds token limit, using two-pass approach`);\n\n const sections = splitIntoSections(chapter.content, SUMMARY_MAX_TOKENS);\n logInfo(`[Summarizer] Split into ${sections.length} sections`);\n\n const sectionSummaries = await Promise.all(\n sections.map((section, i) => summarizeSection(section, chapter.title, i + 1))\n );\n\n const combined = sectionSummaries.join(\"\\n\\n\");\n\n return await generateStructuredSummary(combined, chapter.title, chapterIndex);\n } catch (error) {\n logWarn(`[Summarizer] Failed to summarize chapter ${chapterIndex + 1}: ${error instanceof Error ? error.message : String(error)}`);\n return null;\n }\n};\n\nexport const summarizeAllChapters = async (chapters: Chapter[]): Promise<ChapterSummary[]> => {\n const summaries: ChapterSummary[] = [];\n\n logInfo(`[Summarizer] Starting summarization of ${chapters.length} chapters (concurrency: ${SUMMARY_CONCURRENCY})`);\n\n for (let i = 0; i < chapters.length; i += SUMMARY_CONCURRENCY) {\n const batch = chapters.slice(i, i + SUMMARY_CONCURRENCY);\n const batchPromises = batch.map((chapter, batchIndex) => summarizeChapter(chapter, i + batchIndex));\n\n const batchResults = await Promise.all(batchPromises);\n\n for (const summary of batchResults) {\n if (summary) {\n summaries.push(summary);\n }\n }\n\n logInfo(`[Summarizer] Progress: ${Math.min(i + SUMMARY_CONCURRENCY, chapters.length)}/${chapters.length} chapters processed`);\n }\n\n logInfo(`[Summarizer] Completed: ${summaries.length}/${chapters.length} summaries generated`);\n\n return summaries;\n};\n","import Database from \"better-sqlite3\";\nimport { resolvePaths } from \"../services/constants\";\n\nconst resolveDbPath = async () => {\n const paths = await resolvePaths();\n return paths.dbPath;\n};\n\nexport const createDb = async (): Promise<Database> => {\n const db = new Database(await resolveDbPath());\n\n db.exec(`\n CREATE TABLE IF NOT EXISTS books (\n id TEXT PRIMARY KEY,\n title TEXT NOT NULL,\n author TEXT,\n cover_path TEXT,\n chapters TEXT,\n epub_path TEXT NOT NULL,\n chunk_count INTEGER DEFAULT 0,\n created_at INTEGER DEFAULT (strftime('%s','now')),\n indexed_at INTEGER,\n progress_chapter INTEGER\n );\n `);\n\n const columns = db\n .prepare(\"PRAGMA table_info(books)\")\n .all()\n .map((col: { name: string }) => col.name);\n\n const ensureColumn = (name: string, definition: string) => {\n if (!columns.includes(name)) {\n db.exec(`ALTER TABLE books ADD COLUMN ${definition}`);\n }\n };\n\n ensureColumn(\"chapters\", \"chapters TEXT\");\n ensureColumn(\"progress_chapter\", \"progress_chapter INTEGER\");\n ensureColumn(\"summaries\", \"summaries TEXT\");\n ensureColumn(\"narrative_start_index\", \"narrative_start_index INTEGER DEFAULT 0\");\n ensureColumn(\"narrative_end_index\", \"narrative_end_index INTEGER\");\n\n return db;\n};\n","import type Database from \"better-sqlite3\";\nimport type { BookRecord } from \"../shared/types\";\nimport { createDb } from \"./schema\";\n\nexport type BookInsert = Omit<\n BookRecord,\n \"createdAt\" | \"indexedAt\" | \"chunkCount\" | \"progressChapter\" | \"narrativeStartIndex\" | \"narrativeEndIndex\"\n> & {\n chunkCount?: number;\n indexedAt?: number | null;\n progressChapter?: number | null;\n summaries?: string;\n narrativeStartIndex?: number | null;\n narrativeEndIndex?: number | null;\n};\n\nconst mapRow = (row: any): BookRecord => ({\n id: row.id,\n title: row.title,\n author: row.author ?? null,\n coverPath: row.cover_path ?? null,\n epubPath: row.epub_path,\n chunkCount: row.chunk_count ?? 0,\n createdAt: row.created_at ?? 0,\n indexedAt: row.indexed_at ?? null,\n chapters: row.chapters ? JSON.parse(row.chapters) : [],\n progressChapter: row.progress_chapter ?? null,\n narrativeStartIndex: row.narrative_start_index ?? null,\n narrativeEndIndex: row.narrative_end_index ?? null,\n});\n\nlet dbPromise: Promise<Database> | null = null;\n\nconst getDb = async () => {\n if (!dbPromise) {\n dbPromise = createDb();\n }\n return dbPromise;\n};\n\nexport const insertBook = async (book: BookInsert): Promise<string> => {\n const db = await getDb();\n const statement = db.prepare(\n \"INSERT INTO books (id, title, author, cover_path, chapters, epub_path, chunk_count, indexed_at, progress_chapter, narrative_start_index, narrative_end_index) VALUES (@id, @title, @author, @coverPath, @chapters, @epubPath, @chunkCount, @indexedAt, @progressChapter, @narrativeStartIndex, @narrativeEndIndex)\"\n );\n statement.run({\n id: book.id,\n title: book.title,\n author: book.author,\n coverPath: book.coverPath,\n chapters: JSON.stringify(book.chapters ?? []),\n epubPath: book.epubPath,\n chunkCount: book.chunkCount ?? 0,\n indexedAt: book.indexedAt ?? null,\n progressChapter: book.progressChapter ?? null,\n narrativeStartIndex: book.narrativeStartIndex ?? null,\n narrativeEndIndex: book.narrativeEndIndex ?? null,\n });\n return book.id;\n};\n\nexport const updateBook = async (id: string, updates: Partial<BookInsert>) => {\n const fields: string[] = [];\n const params: Record<string, string | number | boolean | null> = { id };\n\n if (updates.title !== undefined) {\n fields.push(\"title = @title\");\n params.title = updates.title;\n }\n if (updates.author !== undefined) {\n fields.push(\"author = @author\");\n params.author = updates.author;\n }\n if (updates.coverPath !== undefined) {\n fields.push(\"cover_path = @coverPath\");\n params.coverPath = updates.coverPath;\n }\n if (updates.chapters !== undefined) {\n fields.push(\"chapters = @chapters\");\n params.chapters = JSON.stringify(updates.chapters);\n }\n if (updates.epubPath !== undefined) {\n fields.push(\"epub_path = @epubPath\");\n params.epubPath = updates.epubPath;\n }\n if (updates.chunkCount !== undefined) {\n fields.push(\"chunk_count = @chunkCount\");\n params.chunkCount = updates.chunkCount;\n }\n if (updates.indexedAt !== undefined) {\n fields.push(\"indexed_at = @indexedAt\");\n params.indexedAt = updates.indexedAt;\n }\n if (updates.progressChapter !== undefined) {\n fields.push(\"progress_chapter = @progressChapter\");\n params.progressChapter = updates.progressChapter;\n }\n if (updates.summaries !== undefined) {\n fields.push(\"summaries = @summaries\");\n params.summaries = updates.summaries;\n }\n if (updates.narrativeStartIndex !== undefined) {\n fields.push(\"narrative_start_index = @narrativeStartIndex\");\n params.narrativeStartIndex = updates.narrativeStartIndex;\n }\n if (updates.narrativeEndIndex !== undefined) {\n fields.push(\"narrative_end_index = @narrativeEndIndex\");\n params.narrativeEndIndex = updates.narrativeEndIndex;\n }\n\n if (fields.length === 0) return;\n\n const db = await getDb();\n db.prepare(`UPDATE books SET ${fields.join(\", \")} WHERE id = @id`).run(params);\n};\n\nexport const getBooks = async (): Promise<BookRecord[]> => {\n const db = await getDb();\n const rows = db.prepare(\"SELECT * FROM books ORDER BY created_at DESC\").all();\n return rows.map(mapRow);\n};\n\nexport const getBook = async (id: string): Promise<BookRecord | null> => {\n const db = await getDb();\n const row = db.prepare(\"SELECT * FROM books WHERE id = ?\").get(id);\n return row ? mapRow(row) : null;\n};\n\nexport const deleteBook = async (id: string) => {\n const db = await getDb();\n db.prepare(\"DELETE FROM books WHERE id = ?\").run(id);\n};\n","import { parseEpub } from \"../services/epub-parser\";\nimport { ingestEpub } from \"../services/ingest\";\nimport { ensureDataDirs, requireOpenAIKey } from \"../services/constants\";\nimport { access } from \"node:fs/promises\";\nimport { prompt } from \"./prompt\";\nimport { isInteractive, stdout } from \"./io\";\n\nconst parseIndexSelection = (input: string, max: number): number[] => {\n const trimmed = input.trim();\n if (!trimmed) return [];\n const tokens = trimmed.split(\",\").map((part) => part.trim()).filter(Boolean);\n const indices = new Set<number>();\n for (const token of tokens) {\n if (token.includes(\"-\")) {\n const [startRaw, endRaw] = token.split(\"-\");\n const start = Number(startRaw);\n const end = Number(endRaw);\n if (!Number.isFinite(start) || !Number.isFinite(end)) continue;\n for (let i = Math.min(start, end); i <= Math.max(start, end); i++) {\n if (i >= 0 && i < max) indices.add(i);\n }\n } else {\n const index = Number(token);\n if (Number.isFinite(index) && index >= 0 && index < max) indices.add(index);\n }\n }\n return Array.from(indices).sort((a, b) => a - b);\n};\n\nexport const ingestCommand = async (filePath: string, options: { manual?: boolean; summarize?: boolean }) => {\n requireOpenAIKey();\n await ensureDataDirs();\n try {\n await access(filePath);\n } catch {\n throw new Error(`File not found: ${filePath}`);\n }\n\n let selectedChapterIndices: number[] | undefined;\n if (options.manual) {\n if (!isInteractive()) {\n throw new Error(\"Manual chapter selection requires an interactive terminal.\");\n }\n const parsed = await parseEpub(filePath);\n if (parsed.chapterTitles.length === 0) {\n throw new Error(\"No chapters found in EPUB\");\n }\n\n stdout(\"Chapters:\");\n parsed.chapterTitles.forEach((title, index) => {\n const marker = index >= parsed.narrativeStartIndex && index <= parsed.narrativeEndIndex ? \"*\" : \" \";\n stdout(`${marker} [${index}] ${title}`);\n });\n stdout(\"\\nEnter chapter indices to ingest (e.g. 0-10,12). Press Enter for narrative range.\");\n const answer = await prompt(\"Selection: \");\n const indices = parseIndexSelection(answer, parsed.chapterTitles.length);\n if (indices.length > 0) {\n selectedChapterIndices = indices;\n } else {\n selectedChapterIndices = Array.from(\n { length: parsed.narrativeEndIndex - parsed.narrativeStartIndex + 1 },\n (_, i) => i + parsed.narrativeStartIndex\n );\n }\n }\n\n const result = await ingestEpub(filePath, selectedChapterIndices, { summarize: options.summarize ?? false });\n stdout(`\\nDone. Book indexed as ${result.id}`);\n};\n","import { createInterface } from \"node:readline/promises\";\nimport { handleSigint } from \"./io\";\n\nexport const prompt = async (question: string): Promise<string> => {\n const release = handleSigint();\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n const response = await rl.question(question);\n return response.trim();\n } finally {\n rl.close();\n release();\n }\n};\n\nexport const confirm = async (question: string): Promise<boolean> => {\n const response = await prompt(question);\n const normalized = response.trim().toLowerCase();\n return normalized === \"y\" || normalized === \"yes\";\n};\n","import { ingestCommand } from \"../ingest\";\n\nexport const registerBookIngest = (program: import(\"commander\").Command) => {\n program\n .command(\"ingest\")\n .description(\"Ingest an EPUB file\")\n .argument(\"<path>\", \"Path to the EPUB file\")\n .option(\"--manual\", \"Interactive chapter selection\")\n .option(\"--summary\", \"Enable AI chapter summaries\")\n .action(async (path: string, options: { manual?: boolean; summary?: boolean }) => {\n const summarize = Boolean(options.summary);\n await ingestCommand(path, { manual: options.manual, summarize });\n });\n};\n","import { getBooks } from \"../db/queries\";\nimport { ensureDataDirs } from \"../services/constants\";\nimport { stdout } from \"./io\";\n\nconst formatDate = (timestamp: number | null) => {\n if (!timestamp) return \"-\";\n return new Date(timestamp).toISOString().slice(0, 10);\n};\n\nexport const listCommand = async () => {\n await ensureDataDirs();\n const books = await getBooks();\n if (books.length === 0) {\n stdout(\"No books indexed yet.\");\n return;\n }\n\n stdout(\"ID | Title | Author | Chunks | Indexed | Status\");\n stdout(\"---------|-------|--------|--------|--------|-------\");\n for (const book of books) {\n const shortId = book.id.slice(0, 8);\n const title = book.title;\n const author = book.author || \"-\";\n const chunks = String(book.chunkCount ?? 0);\n const indexed = formatDate(book.indexedAt);\n const status = book.indexedAt ? \"[indexed]\" : \"[pending]\";\n stdout(`${shortId} | ${title} | ${author} | ${chunks} | ${indexed} | ${status}`);\n }\n};\n","import { listCommand } from \"../list\";\n\nexport const registerBookList = (program: import(\"commander\").Command) => {\n program\n .command(\"list\")\n .description(\"List indexed books\")\n .action(async () => {\n await listCommand();\n });\n};\n","import { getBooks } from \"../db/queries\";\n\nexport const resolveBookId = async (input: string): Promise<string | null> => {\n const books = await getBooks();\n const exact = books.find((book) => book.id === input);\n if (exact) return exact.id;\n const matches = books.filter((book) => book.id.startsWith(input));\n if (matches.length === 1) return matches[0]!.id;\n if (matches.length > 1) {\n throw new Error(`Ambiguous id prefix \"${input}\" (${matches.length} matches)`);\n }\n return null;\n};\n","import { getBook } from \"../db/queries\";\nimport { resolveBookId } from \"./utils\";\nimport { ensureDataDirs } from \"../services/constants\";\nimport { stdout } from \"./io\";\n\nexport const showCommand = async (id: string) => {\n await ensureDataDirs();\n const resolvedId = await resolveBookId(id);\n if (!resolvedId) {\n throw new Error(`Book not found: ${id}`);\n }\n const book = await getBook(resolvedId);\n if (!book) {\n throw new Error(`Book not found: ${id}`);\n }\n\n stdout(`Title: ${book.title}`);\n stdout(`Author: ${book.author ?? \"-\"}`);\n stdout(`ID: ${book.id}`);\n stdout(`Chunks: ${book.chunkCount}`);\n stdout(`Indexed: ${book.indexedAt ? new Date(book.indexedAt).toISOString() : \"-\"}`);\n stdout(`Narrative range: ${book.narrativeStartIndex ?? 0} to ${book.narrativeEndIndex ?? book.chapters.length - 1}`);\n stdout(`Progress chapter: ${book.progressChapter ?? \"-\"}`);\n stdout(\"\\nChapters:\");\n\n book.chapters.forEach((title: string, index: number) => {\n const marker = index === book.narrativeStartIndex ? \"[start]\" : index === book.narrativeEndIndex ? \"[end]\" : \"\";\n stdout(` [${index}] ${title} ${marker}`.trim());\n });\n};\n","import { showCommand } from \"../show\";\n\nexport const registerBookShow = (program: import(\"commander\").Command) => {\n program\n .command(\"show\")\n .description(\"Show full book metadata\")\n .argument(\"<id>\", \"Book id or prefix\")\n .action(async (id: string) => {\n await showCommand(id);\n });\n};\n","import { embed, streamText } from \"ai\";\nimport { openai } from \"@ai-sdk/openai\";\nimport { getBook } from \"../db/queries\";\nimport { resolveBookId } from \"./utils\";\nimport { queryBookIndex } from \"../services/vector-store\";\nimport { ensureDataDirs, getModels, isAskEnabled, requireOpenAIKey } from \"../services/constants\";\nimport { handleSigint } from \"./io\";\n\nconst formatContext = (chunks: Array<{ content: string; chapterTitle: string; chapterIndex: number }>) =>\n chunks\n .map(\n (chunk, index) =>\n `Excerpt [${index + 1}] (${chunk.chapterTitle || `Chapter ${chunk.chapterIndex + 1}`}):\\n${chunk.content}`\n )\n .join(\"\\n\\n\");\n\nexport const askCommand = async (\n id: string,\n question: string,\n options: { topK: number; maxChapter?: number }\n) => {\n if (!(await isAskEnabled())) {\n throw new Error(\"Ask is disabled in config (askEnabled: false). Enable it to use this command.\");\n }\n\n requireOpenAIKey();\n await ensureDataDirs();\n\n const resolvedId = await resolveBookId(id);\n if (!resolvedId) {\n throw new Error(`Book not found: ${id}`);\n }\n const book = await getBook(resolvedId);\n if (!book) {\n throw new Error(`Book not found: ${id}`);\n }\n\n const models = await getModels();\n const { embedding } = await embed({\n model: openai.embeddingModel(models.embedding),\n value: question,\n });\n\n const narrativeStart = book.narrativeStartIndex ?? 0;\n const userProgress = book.progressChapter ?? null;\n const maxChapterIndex = options.maxChapter !== undefined\n ? narrativeStart + options.maxChapter\n : userProgress !== null\n ? narrativeStart + userProgress\n : undefined;\n\n const retrievalLimit = options.topK * 3;\n const allMatches = await queryBookIndex(resolvedId, embedding, question, retrievalLimit, maxChapterIndex);\n\n const summaries = allMatches.filter((m) => m.type === \"summary\");\n const chunks = allMatches.filter((m) => m.type !== \"summary\");\n\n const topSummaries = summaries.slice(0, 2);\n const topChunks = chunks.slice(0, Math.max(0, options.topK - topSummaries.length));\n const selectedMatches = [...topSummaries, ...topChunks];\n\n const context = formatContext(selectedMatches);\n\n const releaseSigint = handleSigint();\n const stream = streamText({\n model: openai(models.chat),\n system: `You are a reading companion helping readers understand this book.\n\nGuidelines:\n- Use the provided chapter summaries and excerpts to answer questions\n- Chapter summaries provide high-level context about characters, events, and plot\n- Excerpts provide specific details and quotes\n- When asked for recaps or \"what happened\", synthesize from summaries\n- Don't cite table of contents, front matter, or structural elements\n- If truly unsure, briefly say so - but try to answer from available context first\n- Cite sources using [1], [2], etc. at the end of relevant sentences\n- The context may be limited to earlier chapters only - don't infer beyond what's provided`,\n prompt: `Question: ${question}\\n\\n${context}`,\n });\n\n try {\n for await (const part of stream.textStream) {\n process.stdout.write(part);\n }\n } finally {\n releaseSigint();\n }\n\n if (selectedMatches.length > 0) {\n process.stdout.write(\"\\n\\nSources:\\n\");\n selectedMatches.forEach((match, index) => {\n const title = match.chapterTitle || `Chapter ${match.chapterIndex + 1}`;\n const excerpt = match.content.slice(0, 120).replace(/\\s+/g, \" \");\n process.stdout.write(`[${index + 1}] ${title}: ${excerpt}\\n`);\n });\n }\n};\n","import { askCommand } from \"../ask\";\n\nexport const registerBookAsk = (program: import(\"commander\").Command) => {\n program\n .command(\"ask\")\n .description(\"Ask a question about a book\")\n .argument(\"<id>\", \"Book id or prefix\")\n .argument(\"<question>\", \"Question to ask\")\n .option(\"--top-k <n>\", \"Number of passages to retrieve\", \"5\")\n .option(\"--max-chapter <n>\", \"Spoiler-free limit (0-based within narrative)\")\n .action(async (\n id: string,\n question: string,\n options: { topK: string; maxChapter?: string }\n ) => {\n const topK = Number(options.topK);\n if (!Number.isFinite(topK) || topK <= 0) {\n throw new Error(\"--top-k must be a positive number.\");\n }\n let maxChapter: number | undefined;\n if (options.maxChapter !== undefined) {\n const parsed = Number(options.maxChapter);\n if (!Number.isFinite(parsed) || parsed < 0) {\n throw new Error(\"--max-chapter must be a non-negative number.\");\n }\n maxChapter = parsed;\n }\n await askCommand(id, question, { topK, maxChapter });\n });\n};\n","import { embed } from \"ai\";\nimport { openai } from \"@ai-sdk/openai\";\nimport { getBook } from \"../db/queries\";\nimport { resolveBookId } from \"./utils\";\nimport { queryBookIndex } from \"../services/vector-store\";\nimport { ensureDataDirs, getModels, requireOpenAIKey } from \"../services/constants\";\nimport { stdout } from \"./io\";\n\nexport const searchCommand = async (\n id: string,\n query: string,\n options: { topK: number; maxChapter?: number }\n) => {\n requireOpenAIKey();\n await ensureDataDirs();\n const resolvedId = await resolveBookId(id);\n if (!resolvedId) {\n throw new Error(`Book not found: ${id}`);\n }\n const book = await getBook(resolvedId);\n if (!book) {\n throw new Error(`Book not found: ${id}`);\n }\n\n const models = await getModels();\n const { embedding } = await embed({\n model: openai.embeddingModel(models.embedding),\n value: query,\n });\n\n const maxChapterIndex = options.maxChapter !== undefined\n ? (book.narrativeStartIndex ?? 0) + options.maxChapter\n : book.progressChapter !== null\n ? (book.narrativeStartIndex ?? 0) + (book.progressChapter ?? 0)\n : undefined;\n const results = await queryBookIndex(resolvedId, embedding, query, options.topK, maxChapterIndex);\n\n if (results.length === 0) {\n stdout(\"No results.\");\n return;\n }\n\n results.forEach((result, index) => {\n const chapterTitle = result.chapterTitle || `Chapter ${result.chapterIndex + 1}`;\n const excerpt = result.content.slice(0, 200).replace(/\\s+/g, \" \");\n stdout(`\\n#${index + 1} score=${result.score.toFixed(4)} type=${result.type || \"chunk\"}`);\n stdout(`${chapterTitle} (chapter ${result.chapterIndex})`);\n stdout(excerpt);\n });\n};\n","import { searchCommand } from \"../search\";\n\nexport const registerBookSearch = (program: import(\"commander\").Command) => {\n program\n .command(\"search\")\n .description(\"Vector search without LLM\")\n .argument(\"<id>\", \"Book id or prefix\")\n .argument(\"<query>\", \"Search query\")\n .option(\"--top-k <n>\", \"Number of passages to retrieve\", \"5\")\n .option(\"--max-chapter <n>\", \"Spoiler-free limit (0-based within narrative)\")\n .action(async (\n id: string,\n query: string,\n options: { topK: string; maxChapter?: string }\n ) => {\n const topK = Number(options.topK);\n if (!Number.isFinite(topK) || topK <= 0) {\n throw new Error(\"--top-k must be a positive number.\");\n }\n let maxChapter: number | undefined;\n if (options.maxChapter !== undefined) {\n const parsed = Number(options.maxChapter);\n if (!Number.isFinite(parsed) || parsed < 0) {\n throw new Error(\"--max-chapter must be a non-negative number.\");\n }\n maxChapter = parsed;\n }\n await searchCommand(id, query, { topK, maxChapter });\n });\n};\n","import { unlink } from \"node:fs/promises\";\nimport { deleteBook, getBook } from \"../db/queries\";\nimport { resolveBookId } from \"./utils\";\nimport { deleteBookIndex } from \"../services/vector-store\";\nimport { ensureDataDirs } from \"../services/constants\";\nimport { confirm } from \"./prompt\";\nimport { isInteractive, stdout } from \"./io\";\n\nexport const deleteCommand = async (id: string, options: { force?: boolean }) => {\n await ensureDataDirs();\n const resolvedId = await resolveBookId(id);\n if (!resolvedId) {\n throw new Error(`Book not found: ${id}`);\n }\n const book = await getBook(resolvedId);\n if (!book) {\n throw new Error(`Book not found: ${id}`);\n }\n\n if (!options.force) {\n if (!isInteractive()) {\n throw new Error(\"Delete confirmation requires an interactive terminal. Use --force to bypass.\");\n }\n const ok = await confirm(`Delete \"${book.title}\" (${book.id})? [y/N] `);\n if (!ok) {\n stdout(\"Cancelled.\");\n return;\n }\n }\n\n await deleteBook(resolvedId);\n await deleteBookIndex(resolvedId);\n if (book.epubPath) {\n await unlink(book.epubPath).catch(() => undefined);\n }\n stdout(`Deleted book ${book.id}`);\n};\n","import { deleteCommand } from \"../delete\";\n\nexport const registerBookDelete = (program: import(\"commander\").Command) => {\n program\n .command(\"delete\")\n .description(\"Remove book, EPUB, and vectors\")\n .argument(\"<id>\", \"Book id or prefix\")\n .option(\"--force\", \"Skip confirmation\")\n .action(async (id: string, options: { force?: boolean }) => {\n await deleteCommand(id, { force: options.force });\n });\n};\n","import { configPath } from \"../config\";\nimport { stdout } from \"./io\";\n\nexport const configCommand = async () => {\n const path = configPath();\n stdout(path);\n};\n","import { configCommand } from \"../config\";\n\nexport const registerConfigPath = (program: import(\"commander\").Command) => {\n program\n .command(\"path\")\n .description(\"Print config path\")\n .action(async () => {\n await configCommand();\n });\n};\n","import { ensureConfigDirs, configPath, loadConfig } from \"../config\";\nimport { mkdir, writeFile, access } from \"node:fs/promises\";\nimport { stdout } from \"./io\";\n\nexport const initConfigCommand = async () => {\n const path = configPath();\n await ensureConfigDirs(path);\n try {\n await access(path);\n stdout(`Config already exists: ${path}`);\n return;\n } catch {\n // file does not exist\n }\n const resolved = await loadConfig();\n const template = {\n dataDir: \"~/.local/share/mycroft\",\n askEnabled: resolved.askEnabled,\n models: resolved.models,\n };\n await writeFile(path, JSON.stringify(template, null, 2), \"utf-8\");\n await mkdir(resolved.dataDir, { recursive: true });\n stdout(`Created config at ${path}`);\n};\n","import { initConfigCommand } from \"../init-config\";\n\nexport const registerConfigInit = (program: import(\"commander\").Command) => {\n program\n .command(\"init\")\n .description(\"Create default config file\")\n .action(async () => {\n await initConfigCommand();\n });\n};\n","import { configPath, loadConfig } from \"../config\";\nimport { stdout } from \"./io\";\n\nexport const resolveConfigCommand = async () => {\n const path = configPath();\n const config = await loadConfig();\n stdout(`Config: ${path}`);\n stdout(`Data dir: ${config.dataDir}`);\n stdout(`Ask enabled: ${config.askEnabled}`);\n stdout(`Models: embedding=${config.models.embedding} summary=${config.models.summary} chat=${config.models.chat}`);\n};\n","import { resolveConfigCommand } from \"../resolve-config\";\n\nexport const registerConfigResolve = (program: import(\"commander\").Command) => {\n program\n .command(\"resolve\")\n .description(\"Print resolved config values\")\n .action(async () => {\n await resolveConfigCommand();\n });\n};\n","import { ensureConfigDirs, configPath, loadConfig } from \"../config\";\nimport { writeFile } from \"node:fs/promises\";\nimport { prompt } from \"./prompt\";\nimport { isInteractive, stdout } from \"./io\";\n\nconst isDefault = (input: string) => input === \"\" || input.toLowerCase() === \"-y\";\n\nconst parseBoolean = (input: string, fallback: boolean) => {\n if (isDefault(input)) return fallback;\n const normalized = input.toLowerCase();\n if ([\"y\", \"yes\", \"true\", \"1\"].includes(normalized)) return true;\n if ([\"n\", \"no\", \"false\", \"0\"].includes(normalized)) return false;\n return fallback;\n};\n\nexport const onboardCommand = async () => {\n if (!isInteractive()) {\n throw new Error(\"Onboarding requires an interactive terminal.\");\n }\n const defaults = await loadConfig();\n const path = configPath();\n\n stdout(\"\\nEPUB RAG setup\");\n stdout(\"Press Enter or type -y to accept defaults.\");\n\n const dataDirInput = await prompt(`Data directory [${defaults.dataDir}]: `);\n const dataDir = isDefault(dataDirInput) ? defaults.dataDir : dataDirInput;\n\n const askEnabledInput = await prompt(`Enable ask (LLM answers) [${defaults.askEnabled ? \"Y\" : \"N\"}]: `);\n const askEnabled = parseBoolean(askEnabledInput, defaults.askEnabled);\n\n const embeddingInput = await prompt(`Embedding model [${defaults.models.embedding}]: `);\n const embedding = isDefault(embeddingInput) ? defaults.models.embedding : embeddingInput;\n\n const summaryInput = await prompt(`Summary model [${defaults.models.summary}]: `);\n const summary = isDefault(summaryInput) ? defaults.models.summary : summaryInput;\n\n const chatInput = await prompt(`Chat model [${defaults.models.chat}]: `);\n const chat = isDefault(chatInput) ? defaults.models.chat : chatInput;\n\n await ensureConfigDirs(path);\n await writeFile(\n path,\n JSON.stringify(\n {\n dataDir,\n askEnabled,\n models: {\n embedding,\n summary,\n chat,\n },\n },\n null,\n 2\n ),\n \"utf-8\"\n );\n\n stdout(\"\\nSetup complete.\");\n stdout(`Config: ${path}`);\n stdout(`Data dir: ${dataDir}`);\n\n if (!process.env.OPENAI_API_KEY) {\n stdout(\"\\nOPENAI_API_KEY is not set.\");\n stdout(\"Export it to enable embeddings and chat:\");\n stdout(\" export OPENAI_API_KEY=\\\"...\\\"\");\n }\n\n stdout(\"\\nNext step:\");\n stdout(\" mycroft book ingest /path/to/book.epub\");\n};\n","import { onboardCommand } from \"../onboard\";\n\nexport const registerConfigOnboard = (program: import(\"commander\").Command) => {\n program\n .command(\"onboard\")\n .description(\"Initialize config and show next step\")\n .action(async () => {\n await onboardCommand();\n });\n};\n"],"mappings":"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","names":["configPath","readFile","dirname","resolve","mkdir","mkdir","mkdir","openai","CHARS_PER_TOKEN","openai","mkdir","program","program","program","openai","openai","program","embed","openai","embed","openai","program","unlink","unlink","program","program","mkdir","access","access","mkdir","program","program","writeFile","writeFile","program","dirname","resolve","readFile"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/config.ts","../src/commands/io.ts","../src/services/epub-parser.ts","../src/services/constants.ts","../src/services/ingest.ts","../src/services/chunker.ts","../src/services/embedder.ts","../src/services/vector-store.ts","../src/services/summarizer.ts","../src/db/queries.ts","../src/db/schema.ts","../src/commands/ingest.ts","../src/commands/prompt.ts","../src/commands/book/ingest.ts","../src/commands/list.ts","../src/commands/book/list.ts","../src/commands/utils.ts","../src/commands/show.ts","../src/commands/book/show.ts","../src/commands/ask.ts","../src/commands/query-options.ts","../src/commands/book/ask.ts","../src/commands/search.ts","../src/commands/book/search.ts","../src/commands/delete.ts","../src/commands/book/delete.ts","../src/commands/config.ts","../src/commands/config/path.ts","../src/commands/init-config.ts","../src/commands/config/init.ts","../src/commands/resolve-config.ts","../src/commands/config/resolve.ts","../src/commands/onboard.ts","../src/commands/config/onboard.ts","../src/services/chat.ts","../src/commands/chat/start.ts","../src/commands/chat/utils.ts","../src/commands/chat/ask.ts","../src/commands/chat/list.ts","../src/commands/chat/show.ts","../src/commands/chat/repl.ts","../src/commands/chat/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport { setConfigOverrides } from \"./config.js\";\nimport { printError } from \"./commands/io.js\";\nimport { readFile } from \"node:fs/promises\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { registerBookIngest } from \"./commands/book/ingest.js\";\nimport { registerBookList } from \"./commands/book/list.js\";\nimport { registerBookShow } from \"./commands/book/show.js\";\nimport { registerBookAsk } from \"./commands/book/ask.js\";\nimport { registerBookSearch } from \"./commands/book/search.js\";\nimport { registerBookDelete } from \"./commands/book/delete.js\";\nimport { registerConfigPath } from \"./commands/config/path.js\";\nimport { registerConfigInit } from \"./commands/config/init.js\";\nimport { registerConfigResolve } from \"./commands/config/resolve.js\";\nimport { registerConfigOnboard } from \"./commands/config/onboard.js\";\nimport { registerChatCommands } from \"./commands/chat/index.js\";\n\nconst resolveVersion = async () => {\n try {\n const currentDir = dirname(fileURLToPath(import.meta.url));\n const pkgPath = resolve(currentDir, \"../package.json\");\n const raw = await readFile(pkgPath, \"utf-8\");\n return JSON.parse(raw).version || \"0.1.0\";\n } catch {\n return \"0.1.0\";\n }\n};\n\nconst program = new Command();\nconst configureProgram = async () => {\n program\n .name(\"mycroft\")\n .description(\"Ingest EPUBs, build a local index, and answer questions\")\n .version(await resolveVersion())\n .option(\"--data-dir <path>\", \"Override data directory\")\n .hook(\"preAction\", (cmd) => {\n const opts = cmd.opts();\n if (opts.dataDir) {\n setConfigOverrides({ dataDir: opts.dataDir });\n }\n });\n};\n\nconst registerCommands = () => {\n const book = program.command(\"book\").description(\"Manage books and queries\");\n registerBookIngest(book);\n registerBookList(book);\n registerBookShow(book);\n registerBookAsk(book);\n registerBookSearch(book);\n registerBookDelete(book);\n\n const config = program.command(\"config\").description(\"Manage configuration\");\n registerConfigPath(config);\n registerConfigInit(config);\n registerConfigResolve(config);\n registerConfigOnboard(config);\n\n registerChatCommands(program);\n};\n\nprogram.exitOverride((error) => {\n if (error.code === \"commander.helpDisplayed\") {\n process.exit(0);\n }\n throw error;\n});\n\nconst main = async () => {\n try {\n await configureProgram();\n registerCommands();\n await program.parseAsync(process.argv);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n printError(message);\n process.exit(1);\n }\n};\n\nmain();\n","import { mkdir, readFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\n\nexport type ConfigModels = {\n embedding: string;\n summary: string;\n chat: string;\n};\n\nexport type AppConfig = {\n dataDir: string;\n askEnabled: boolean;\n models: ConfigModels;\n};\n\nconst DEFAULT_CONFIG: AppConfig = {\n dataDir: \"~/.local/share/mycroft\",\n askEnabled: true,\n models: {\n embedding: \"text-embedding-3-small\",\n summary: \"gpt-5-nano\",\n chat: \"gpt-5.1\",\n },\n};\n\nconst expandHome = (input: string): string => {\n if (!input.startsWith(\"~\")) return input;\n return join(homedir(), input.slice(1));\n};\n\nconst resolvePath = (input: string): string => resolve(expandHome(input));\n\nconst getConfigPath = (): string => {\n const override = process.env.MYCROFT_CONFIG;\n if (override) return resolvePath(override);\n return resolvePath(\"~/.config/mycroft/config.json\");\n};\n\nconst normalizeModels = (models?: Partial<ConfigModels>): ConfigModels => ({\n embedding: models?.embedding || DEFAULT_CONFIG.models.embedding,\n summary: models?.summary || DEFAULT_CONFIG.models.summary,\n chat: models?.chat || DEFAULT_CONFIG.models.chat,\n});\n\ntype ConfigOverrides = {\n dataDir?: string;\n};\n\nlet overrides: ConfigOverrides = {};\n\nexport const setConfigOverrides = (next: ConfigOverrides) => {\n overrides = { ...overrides, ...next };\n};\n\nconst normalizeConfig = (input: Partial<AppConfig> | null): AppConfig => {\n const dataDirEnv = process.env.MYCROFT_DATA_DIR;\n const dataDir = overrides.dataDir || dataDirEnv || input?.dataDir || DEFAULT_CONFIG.dataDir;\n return {\n dataDir,\n askEnabled: input?.askEnabled ?? DEFAULT_CONFIG.askEnabled,\n models: normalizeModels(input?.models),\n };\n};\n\nconst readConfigFile = async (path: string): Promise<Partial<AppConfig> | null> => {\n try {\n const contents = await readFile(path, \"utf-8\");\n return JSON.parse(contents) as Partial<AppConfig>;\n } catch {\n return null;\n }\n};\n\nexport const loadConfig = async (): Promise<AppConfig> => {\n const configPath = getConfigPath();\n const data = await readConfigFile(configPath);\n const normalized = normalizeConfig(data);\n return {\n ...normalized,\n dataDir: resolvePath(normalized.dataDir),\n };\n};\n\nexport const ensureConfigDirs = async (configPath?: string) => {\n const path = configPath || getConfigPath();\n await mkdir(dirname(path), { recursive: true });\n};\n\nexport const configPath = () => getConfigPath();\n","import chalk from \"chalk\";\n\nconst isTTY = () => Boolean(process.stdout.isTTY);\nexport const isInteractive = () => Boolean(process.stdin.isTTY && process.stdout.isTTY);\n\nexport const formatDim = (text: string) => (isTTY() ? chalk.dim(text) : text);\nexport const formatError = (text: string) => (isTTY() ? chalk.red(text) : text);\nexport const formatBold = (text: string) => (isTTY() ? chalk.bold(text) : text);\nexport const formatWarn = (text: string) => (isTTY() ? chalk.yellow(text) : text);\n\nexport const stdout = (message: string) => {\n process.stdout.write(message.endsWith(\"\\n\") ? message : `${message}\\n`);\n};\n\nexport const stderr = (message: string) => {\n process.stderr.write(message.endsWith(\"\\n\") ? message : `${message}\\n`);\n};\n\nexport const printError = (message: string) => {\n stderr(formatError(`Error: ${message}`));\n};\n\nexport const logInfo = (message: string) => {\n stderr(message);\n};\n\nexport const logWarn = (message: string) => {\n stderr(formatWarn(message));\n};\n\nexport const handleSigint = (onCancel?: () => void) => {\n const handler = () => {\n if (onCancel) onCancel();\n stderr(\"\\nCancelled.\");\n process.exit(130);\n };\n process.once(\"SIGINT\", handler);\n return () => process.off(\"SIGINT\", handler);\n};\n","import { initEpubFile } from \"@lingo-reader/epub-parser\";\nimport { basename } from \"node:path\";\nimport type { Chapter } from \"../shared/types.js\";\nimport { logInfo } from \"./constants.js\";\n\nexport type ParsedBook = {\n title: string;\n author: string | null;\n coverImagePath: string | null;\n chapters: Chapter[];\n chapterTitles: string[];\n narrativeStartIndex: number;\n narrativeEndIndex: number;\n};\n\nconst detectNarrativeBoundaries = (chapterTitles: string[]): { start: number; end: number } => {\n const frontMatterPattern = /^(about|contents|table of contents|dedication|preface|foreword|title|half.?title|copyright|epigraph|frontispiece|map)/i;\n const backMatterPattern = /^(acknowledgment|afterword|appendix|glossary|index|bibliography|about the author|also by|praise|copyright page|notes|bonus|preview|excerpt|major characters|locations)/i;\n const narrativePattern = /^(I|II|III|IV|V|VI|VII|VIII|IX|X|XI|XII|1|2|3|4|5|6|7|8|9|one|two|three|chapter|prologue|epilogue|part\\s)/i;\n\n let start = 0;\n let end = chapterTitles.length - 1;\n\n for (let i = 0; i < chapterTitles.length; i++) {\n const title = chapterTitles[i]?.trim() || \"\";\n\n if (narrativePattern.test(title) && !frontMatterPattern.test(title)) {\n start = i;\n break;\n }\n\n if (!frontMatterPattern.test(title) && title.length > 0) {\n if (title.length > 3) {\n start = i;\n break;\n }\n }\n }\n\n for (let i = chapterTitles.length - 1; i >= start; i--) {\n const title = chapterTitles[i]?.trim() || \"\";\n if (!backMatterPattern.test(title)) {\n end = i;\n break;\n }\n }\n\n logInfo(`[EPUB Parser] Detected narrative boundaries: chapters ${start} to ${end} (out of ${chapterTitles.length} total)`);\n if (start > 0) {\n logInfo(`[EPUB Parser] Front matter: ${chapterTitles.slice(0, start).join(\", \")}`);\n }\n if (end < chapterTitles.length - 1) {\n logInfo(`[EPUB Parser] Back matter: ${chapterTitles.slice(end + 1).join(\", \")}`);\n }\n\n return { start, end };\n};\n\nconst stripHtml = (html: string) =>\n html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, \" \")\n .replace(/<style[\\s\\S]*?<\\/style>/gi, \" \")\n .replace(/<[^>]+>/g, \" \")\n .replace(/&nbsp;/g, \" \")\n .replace(/&amp;/g, \"&\")\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .replace(/\\s+/g, \" \")\n .trim();\n\nconst originalWarn = console.warn;\nconst createWarnFilter = () => {\n const suppressedWarnings: string[] = [];\n console.warn = (msg: any, ...args: unknown[]) => {\n if (typeof msg === \"string\" && msg.includes(\"No element with id\") && msg.includes(\"parsing <metadata>\")) {\n suppressedWarnings.push(msg);\n return;\n }\n originalWarn(msg, ...args);\n };\n return suppressedWarnings;\n};\n\nexport const parseEpub = async (epubPath: string, resourceSaveDir?: string): Promise<ParsedBook> => {\n logInfo(`[EPUB Parser] Starting parse for: ${basename(epubPath)}`);\n\n const suppressedWarnings = createWarnFilter();\n\n try {\n const epubFile = await initEpubFile(epubPath, resourceSaveDir);\n await epubFile.loadEpub();\n logInfo(`[EPUB Parser] EPUB loaded successfully`);\n\n await epubFile.parse();\n\n if (suppressedWarnings.length > 0) {\n logInfo(`[EPUB Parser] Suppressed ${suppressedWarnings.length} metadata warnings (non-critical)`);\n }\n logInfo(`[EPUB Parser] Parse completed`);\n\n const fileBaseName = basename(epubPath, \".epub\");\n type EpubMetadata = ReturnType<typeof epubFile.getMetadata>;\n let metadata: EpubMetadata | null = null;\n try {\n metadata = epubFile.getMetadata();\n } catch {\n metadata = null;\n }\n const safeMetadata = metadata ?? ({} as EpubMetadata);\n const spine = epubFile.getSpine();\n const toc = epubFile.getToc();\n\n logInfo(`[EPUB Parser] Found ${spine.length} spine items, ${toc.length} TOC entries`);\n\n const titleById = new Map<string, string>();\n const walkToc = (items: typeof toc) => {\n items.forEach((item: (typeof toc)[number]) => {\n const resolved = epubFile.resolveHref(item.href);\n if (resolved?.id) titleById.set(resolved.id, item.label);\n if (item.children?.length) walkToc(item.children);\n });\n };\n walkToc(toc);\n const coverImagePath = epubFile.getCoverImage() || null;\n\n const chapters: Chapter[] = [];\n const chapterTitles: string[] = [];\n for (const [index, item] of spine.entries()) {\n const chapter = await epubFile.loadChapter(item.id);\n const content = stripHtml(chapter.html);\n if (!content) continue;\n const chapterTitle = titleById.get(item.id) || item.id || `Chapter ${index + 1}`;\n chapters.push({\n title: chapterTitle,\n content,\n });\n chapterTitles.push(chapterTitle);\n }\n\n epubFile.destroy();\n\n const author = safeMetadata.creator?.[0]?.contributor ?? null;\n\n logInfo(`[EPUB Parser] Extracted ${chapters.length} chapters with content`);\n logInfo(`[EPUB Parser] Title: \"${safeMetadata.title || fileBaseName || \"Untitled\"}\", Author: \"${author || \"Unknown\"}\"`);\n\n const { start: narrativeStartIndex, end: narrativeEndIndex } = detectNarrativeBoundaries(chapterTitles);\n\n return {\n title: safeMetadata.title || fileBaseName || \"Untitled\",\n author,\n coverImagePath,\n chapters,\n chapterTitles,\n narrativeStartIndex,\n narrativeEndIndex,\n };\n } finally {\n console.warn = originalWarn;\n }\n};\n","import { mkdir } from \"node:fs/promises\";\nimport { loadConfig } from \"../config.js\";\nimport { logInfo, logWarn } from \"../commands/io.js\";\n\nexport const CHUNK_SIZE: number = 1000;\nexport const CHUNK_OVERLAP: number = 100;\nexport const SEPARATORS = [\"\\n\\n\", \"\\n\", \". \", \" \", \"\"] as const;\n\nexport const SUMMARY_MAX_TOKENS = 30000;\nexport const SUMMARY_CONCURRENCY = 3;\nexport const SUMMARY_TARGET_WORDS = 250;\n\nexport type ResolvedPaths = {\n dataDir: string;\n booksDir: string;\n vectorsDir: string;\n dbPath: string;\n};\n\nexport const resolvePaths = async (): Promise<ResolvedPaths> => {\n const config = await loadConfig();\n const dataDir = config.dataDir;\n return {\n dataDir,\n booksDir: `${dataDir}/books`,\n vectorsDir: `${dataDir}/vectors`,\n dbPath: `${dataDir}/metadata.db`,\n };\n};\n\nexport const ensureDataDirs = async () => {\n const paths = await resolvePaths();\n await mkdir(paths.dataDir, { recursive: true });\n await mkdir(paths.booksDir, { recursive: true });\n await mkdir(paths.vectorsDir, { recursive: true });\n return paths;\n};\n\nexport const getModels = async () => {\n const config = await loadConfig();\n return config.models;\n};\n\nexport const isAskEnabled = async () => {\n const config = await loadConfig();\n return config.askEnabled;\n};\n\nexport const requireOpenAIKey = () => {\n if (!process.env.OPENAI_API_KEY) {\n throw new Error(\"OPENAI_API_KEY is not set. Export it to use embeddings and chat.\");\n }\n};\n\nexport { logInfo, logWarn };\n","import { randomUUID } from \"node:crypto\";\nimport { mkdir, unlink, copyFile } from \"node:fs/promises\";\nimport { parseEpub } from \"./epub-parser.js\";\nimport { chunkChapters } from \"./chunker.js\";\nimport { embedChunks } from \"./embedder.js\";\nimport { addChunksToIndex, deleteBookIndex } from \"./vector-store.js\";\nimport { summarizeAllChapters } from \"./summarizer.js\";\nimport { ensureDataDirs, logInfo, logWarn } from \"./constants.js\";\nimport { deleteBook, insertBook, updateBook } from \"../db/queries.js\";\nimport type { BookChunk } from \"../shared/types.js\";\n\nconst formatDuration = (ms: number) => {\n const seconds = Math.round(ms / 100) / 10;\n return `${seconds}s`;\n};\n\nexport const ingestEpub = async (\n filePath: string,\n selectedChapterIndices?: number[],\n options?: { summarize?: boolean }\n) => {\n const bookId = randomUUID();\n const paths = await ensureDataDirs();\n const fileName = `${bookId}.epub`;\n const bookPath = `${paths.booksDir}/${fileName}`;\n\n logInfo(`[Ingest] Starting ingestion for book ${bookId}`);\n\n await mkdir(paths.booksDir, { recursive: true });\n await copyFile(filePath, bookPath);\n logInfo(`[Ingest] EPUB file saved to ${bookPath}`);\n\n const parseStart = Date.now();\n const parsed = await parseEpub(bookPath);\n logInfo(`[Ingest] Parsed \"${parsed.title}\" with ${parsed.chapters.length} chapters (${formatDuration(Date.now() - parseStart)})`);\n logInfo(`[Ingest] Narrative chapters: ${parsed.narrativeStartIndex} to ${parsed.narrativeEndIndex}`);\n\n await insertBook({\n id: bookId,\n title: parsed.title,\n author: parsed.author,\n coverPath: parsed.coverImagePath,\n epubPath: bookPath,\n chapters: parsed.chapterTitles,\n narrativeStartIndex: parsed.narrativeStartIndex,\n narrativeEndIndex: parsed.narrativeEndIndex,\n });\n logInfo(`[Ingest] Book record inserted into database`);\n\n try {\n const chaptersToProcess = selectedChapterIndices\n ? parsed.chapters.filter((_, index) => selectedChapterIndices.includes(index))\n : parsed.chapters.slice(parsed.narrativeStartIndex, parsed.narrativeEndIndex + 1);\n\n const selectedIndices = selectedChapterIndices ||\n Array.from({ length: parsed.narrativeEndIndex - parsed.narrativeStartIndex + 1 },\n (_, i) => i + parsed.narrativeStartIndex);\n\n logInfo(`[Ingest] Processing ${chaptersToProcess.length} selected chapters (indices: ${selectedIndices.join(\", \")})`);\n\n let adjustedSummaries: BookChunk[] = [];\n if (options?.summarize !== false) {\n logInfo(`[Ingest] Generating summaries for ${chaptersToProcess.length} chapters...`);\n const summarizeStart = Date.now();\n const summaries = await summarizeAllChapters(chaptersToProcess);\n logInfo(`[Ingest] Generated ${summaries.length}/${chaptersToProcess.length} summaries (${formatDuration(Date.now() - summarizeStart)})`);\n\n const summaryRecords = summaries.map((s, idx) => ({\n ...s,\n chapterIndex: selectedIndices[idx] ?? s.chapterIndex,\n }));\n\n await updateBook(bookId, {\n summaries: JSON.stringify(summaryRecords),\n });\n\n adjustedSummaries = summaryRecords.map((s) => ({\n id: `${bookId}-summary-${s.chapterIndex}`,\n bookId,\n chapterIndex: s.chapterIndex,\n chapterTitle: s.chapterTitle,\n chunkIndex: -1,\n content: s.fullSummary,\n type: \"summary\" as const,\n }));\n logInfo(`[Ingest] Created ${adjustedSummaries.length} summary chunks`);\n }\n\n const chunksToProcess = parsed.chapters.map((chapter, index) =>\n selectedIndices.includes(index) ? chapter : { title: chapter.title, content: \"\" }\n );\n const chunks = chunkChapters(bookId, chunksToProcess).filter((chunk) => chunk.content.length > 0);\n logInfo(`[Ingest] Created ${chunks.length} chunks from selected chapters`);\n\n const allChunks = [...chunks, ...adjustedSummaries];\n const embedStart = Date.now();\n const embedded = await embedChunks(allChunks);\n logInfo(`[Ingest] Embedded ${embedded.length} total chunks (${formatDuration(Date.now() - embedStart)})`);\n\n await addChunksToIndex(bookId, embedded);\n logInfo(`[Ingest] Added chunks to vector index`);\n\n await updateBook(bookId, { chunkCount: embedded.length, indexedAt: Date.now() });\n logInfo(`[Ingest] Updated book record with chunk count: ${embedded.length}`);\n } catch (error) {\n logWarn(`[Ingest] Error during chunking/embedding: ${error instanceof Error ? error.message : String(error)}`);\n await deleteBookIndex(bookId);\n await unlink(bookPath).catch(() => undefined);\n await deleteBook(bookId).catch(() => undefined);\n throw error;\n }\n\n logInfo(`[Ingest] Ingestion complete for ${bookId}`);\n return { id: bookId };\n};\n","import type { Chapter, BookChunk } from \"../shared/types.js\";\nimport { CHUNK_OVERLAP, CHUNK_SIZE, SEPARATORS } from \"./constants.js\";\n\nconst splitRecursive = (text: string, separators: readonly string[]): string[] => {\n if (text.length <= CHUNK_SIZE || separators.length === 0) return [text];\n const [separator, ...rest] = separators;\n if (!separator) return [text];\n\n const parts = text.split(separator);\n if (parts.length === 1) return splitRecursive(text, rest);\n\n const chunks: string[] = [];\n let current = \"\";\n\n for (const part of parts) {\n const next = current ? `${current}${separator}${part}` : part;\n if (next.length <= CHUNK_SIZE) {\n current = next;\n continue;\n }\n\n if (current) chunks.push(current);\n current = part;\n }\n\n if (current) chunks.push(current);\n\n const refined: string[] = [];\n for (const chunk of chunks) {\n if (chunk.length <= CHUNK_SIZE) {\n refined.push(chunk);\n continue;\n }\n refined.push(...splitRecursive(chunk, rest));\n }\n\n return refined;\n};\n\nconst withOverlap = (chunks: string[]): string[] => {\n if (chunks.length <= 1 || CHUNK_OVERLAP === 0) return chunks;\n\n const merged: string[] = [];\n for (let i = 0; i < chunks.length; i += 1) {\n const current = chunks[i] ?? \"\";\n const previous = merged[merged.length - 1];\n if (!previous) {\n merged.push(current);\n continue;\n }\n\n const overlap = previous.slice(-CHUNK_OVERLAP);\n merged.push(`${overlap}${current}`);\n }\n\n return merged;\n};\n\nexport const chunkChapters = (bookId: string, chapters: Chapter[]): BookChunk[] => {\n const chunks: BookChunk[] = [];\n\n chapters.forEach((chapter, chapterIndex) => {\n const trimmed = chapter.content.trim();\n if (!trimmed) return;\n\n const rawChunks = splitRecursive(trimmed, SEPARATORS);\n const overlapped = withOverlap(rawChunks);\n overlapped.forEach((content, chunkIndex) => {\n const normalized = content.replace(/\\s+/g, \" \").trim();\n if (!normalized) return;\n chunks.push({\n id: `${bookId}-${chapterIndex}-${chunkIndex}`,\n bookId,\n chapterIndex,\n chapterTitle: chapter.title,\n chunkIndex,\n content: normalized,\n });\n });\n });\n\n return chunks;\n};\n","import { embedMany } from \"ai\";\nimport { openai } from \"@ai-sdk/openai\";\nimport type { BookChunk } from \"../shared/types.js\";\nimport { getModels, logInfo } from \"./constants.js\";\n\nexport type EmbeddedChunk = BookChunk & {\n vector: number[];\n};\n\nconst MAX_TOKENS_PER_BATCH = 250_000;\nconst CHARS_PER_TOKEN = 4;\n\nexport const embedChunks = async (chunks: BookChunk[]): Promise<EmbeddedChunk[]> => {\n if (chunks.length === 0) return [];\n\n const batches: BookChunk[][] = [];\n let currentBatch: BookChunk[] = [];\n let currentTokens = 0;\n\n for (const chunk of chunks) {\n const estimatedTokens = Math.ceil(chunk.content.length / CHARS_PER_TOKEN);\n\n if (currentTokens + estimatedTokens > MAX_TOKENS_PER_BATCH && currentBatch.length > 0) {\n batches.push(currentBatch);\n currentBatch = [];\n currentTokens = 0;\n }\n\n currentBatch.push(chunk);\n currentTokens += estimatedTokens;\n }\n\n if (currentBatch.length > 0) {\n batches.push(currentBatch);\n }\n\n logInfo(`[Embedder] Processing ${chunks.length} chunks in ${batches.length} batch(es)`);\n\n const allEmbedded: EmbeddedChunk[] = [];\n const models = await getModels();\n\n for (let i = 0; i < batches.length; i++) {\n const batch = batches[i]!;\n const estimatedTokens = batch.reduce((sum, c) => sum + Math.ceil(c.content.length / CHARS_PER_TOKEN), 0);\n\n logInfo(`[Embedder] Batch ${i + 1}/${batches.length}: ${batch.length} chunks (~${estimatedTokens.toLocaleString()} tokens)`);\n\n const { embeddings } = await embedMany({\n model: openai.embeddingModel(models.embedding),\n values: batch.map((chunk) => chunk.content),\n });\n\n for (let j = 0; j < batch.length; j++) {\n allEmbedded.push({\n ...batch[j]!,\n vector: embeddings[j] ?? [],\n });\n }\n }\n\n logInfo(`[Embedder] Successfully embedded all ${allEmbedded.length} chunks`);\n\n return allEmbedded;\n};\n","import { LocalIndex } from \"vectra\";\nimport type { EmbeddedChunk } from \"./embedder.js\";\nimport type { BookChunk } from \"../shared/types.js\";\nimport { ensureDataDirs } from \"./constants.js\";\n\nexport type VectorMetadata = {\n bookId: string;\n chapterIndex: number;\n chapterTitle: string;\n chunkIndex: number;\n content: string;\n type?: \"chunk\" | \"summary\";\n};\n\nconst indexPathForBook = async (bookId: string) => {\n const paths = await ensureDataDirs();\n return `${paths.vectorsDir}/${bookId}`;\n};\n\nexport const createBookIndex = async (bookId: string): Promise<LocalIndex<VectorMetadata>> => {\n const index = new LocalIndex<VectorMetadata>(await indexPathForBook(bookId));\n const exists = await index.isIndexCreated();\n if (!exists) {\n await index.createIndex({\n version: 1,\n metadata_config: {\n indexed: [\"bookId\"],\n },\n });\n }\n return index;\n};\n\nexport const addChunksToIndex = async (bookId: string, chunks: EmbeddedChunk[]) => {\n const index = await createBookIndex(bookId);\n await index.batchInsertItems(\n chunks.map((chunk) => ({\n id: chunk.id,\n vector: chunk.vector,\n metadata: {\n bookId: chunk.bookId,\n chapterIndex: chunk.chapterIndex,\n chapterTitle: chunk.chapterTitle,\n chunkIndex: chunk.chunkIndex,\n content: chunk.content,\n type: chunk.type || \"chunk\",\n },\n }))\n );\n};\n\nexport const queryBookIndex = async (\n bookId: string,\n queryVector: number[],\n queryText: string,\n topK: number,\n maxChapterIndex?: number\n): Promise<(BookChunk & { score: number })[]> => {\n const index = await createBookIndex(bookId);\n const expandedTopK =\n maxChapterIndex === undefined || maxChapterIndex === null ? topK : Math.max(topK * 4, topK);\n const results = await index.queryItems(queryVector, queryText, expandedTopK);\n\n const mapped = results.map((result: (typeof results)[number]) => ({\n id: result.item.id ?? \"\",\n bookId,\n chapterIndex: result.item.metadata?.chapterIndex ?? 0,\n chapterTitle: result.item.metadata?.chapterTitle ?? \"\",\n chunkIndex: result.item.metadata?.chunkIndex ?? 0,\n content: result.item.metadata?.content ?? \"\",\n type: result.item.metadata?.type as \"chunk\" | \"summary\" | undefined,\n score: result.score,\n }));\n\n if (maxChapterIndex === undefined || maxChapterIndex === null) {\n return mapped.slice(0, topK);\n }\n\n return mapped.filter((item: (typeof mapped)[number]) => item.chapterIndex <= maxChapterIndex).slice(0, topK);\n};\n\nexport const deleteBookIndex = async (bookId: string) => {\n const index = new LocalIndex<VectorMetadata>(await indexPathForBook(bookId));\n const exists = await index.isIndexCreated();\n if (!exists) return;\n await index.deleteIndex();\n};\n","import { generateText } from \"ai\";\nimport { openai } from \"@ai-sdk/openai\";\nimport type { Chapter, ChapterSummary } from \"../shared/types.js\";\nimport { SUMMARY_MAX_TOKENS, SUMMARY_CONCURRENCY, SUMMARY_TARGET_WORDS, getModels, logInfo, logWarn } from \"./constants.js\";\n\nconst CHARS_PER_TOKEN = 4;\n\nconst estimateTokens = (text: string): number => Math.ceil(text.length / CHARS_PER_TOKEN);\n\nconst SUMMARY_PROMPT = (title: string, chapterNum: number, content: string) => `You are analyzing a chapter from a book (fiction or nonfiction). Extract key information to help readers understand the chapter's content.\n\nChapter Title: ${title}\nChapter Number: ${chapterNum}\n\n---\n${content}\n---\n\nExtract the following information and respond ONLY with valid JSON (no markdown, no code blocks):\n\n{\n \"characters\": [\"Name - brief description (role, traits, first appearance)\", ...],\n \"events\": \"What happens in this chapter? (2-3 sentences)\",\n \"setting\": \"Where does this chapter take place?\",\n \"revelations\": \"Any important information revealed? (secrets, backstory, foreshadowing)\"\n}\n\nKeep the total response around ${SUMMARY_TARGET_WORDS} words.`;\n\ntype SummaryJSON = {\n characters: string[];\n events: string;\n setting: string;\n revelations: string;\n};\n\nconst splitIntoSections = (text: string, maxTokens: number): string[] => {\n const estimatedTokens = estimateTokens(text);\n\n if (estimatedTokens <= maxTokens) {\n return [text];\n }\n\n const numSections = Math.ceil(estimatedTokens / maxTokens);\n const charsPerSection = Math.floor(text.length / numSections);\n const sections: string[] = [];\n\n for (let i = 0; i < numSections; i++) {\n const start = i * charsPerSection;\n const end = i === numSections - 1 ? text.length : (i + 1) * charsPerSection;\n sections.push(text.slice(start, end));\n }\n\n return sections;\n};\n\nconst summarizeSection = async (text: string, title: string, sectionNum: number): Promise<string> => {\n const models = await getModels();\n const { text: summary } = await generateText({\n model: openai(models.summary),\n prompt: `Summarize this section from chapter \"${title}\" (Part ${sectionNum}). Focus on key events, characters, and revelations. Keep it concise (100-150 words):\\n\\n${text}`,\n temperature: 0.3,\n });\n\n return summary;\n};\n\nconst generateStructuredSummary = async (\n content: string,\n title: string,\n chapterIndex: number\n): Promise<ChapterSummary | null> => {\n try {\n const models = await getModels();\n const { text } = await generateText({\n model: openai(models.summary),\n prompt: SUMMARY_PROMPT(title, chapterIndex + 1, content),\n temperature: 0.3,\n });\n\n let jsonText = text.trim();\n if (jsonText.startsWith(\"```json\")) {\n jsonText = jsonText.slice(7, -3).trim();\n } else if (jsonText.startsWith(\"```\")) {\n jsonText = jsonText.slice(3, -3).trim();\n }\n\n const parsed: SummaryJSON = JSON.parse(jsonText);\n\n const fullSummary = `Chapter ${chapterIndex + 1}: ${title}\n\nCharacters: ${parsed.characters.join(\", \")}\n\nEvents: ${parsed.events}\n\nSetting: ${parsed.setting}\n\nRevelations: ${parsed.revelations}`;\n\n return {\n chapterIndex,\n chapterTitle: title,\n characters: parsed.characters,\n events: parsed.events,\n setting: parsed.setting,\n revelations: parsed.revelations,\n fullSummary,\n };\n } catch (error) {\n logWarn(`[Summarizer] Failed to parse summary JSON for \"${title}\": ${error instanceof Error ? error.message : String(error)}`);\n return null;\n }\n};\n\nexport const summarizeChapter = async (\n chapter: Chapter,\n chapterIndex: number\n): Promise<ChapterSummary | null> => {\n const tokens = estimateTokens(chapter.content);\n\n logInfo(`[Summarizer] Chapter ${chapterIndex + 1} \"${chapter.title}\": ~${tokens.toLocaleString()} tokens`);\n\n try {\n if (tokens < SUMMARY_MAX_TOKENS) {\n return await generateStructuredSummary(chapter.content, chapter.title, chapterIndex);\n }\n\n logInfo(`[Summarizer] Chapter ${chapterIndex + 1} exceeds token limit, using two-pass approach`);\n\n const sections = splitIntoSections(chapter.content, SUMMARY_MAX_TOKENS);\n logInfo(`[Summarizer] Split into ${sections.length} sections`);\n\n const sectionSummaries = await Promise.all(\n sections.map((section, i) => summarizeSection(section, chapter.title, i + 1))\n );\n\n const combined = sectionSummaries.join(\"\\n\\n\");\n\n return await generateStructuredSummary(combined, chapter.title, chapterIndex);\n } catch (error) {\n logWarn(`[Summarizer] Failed to summarize chapter ${chapterIndex + 1}: ${error instanceof Error ? error.message : String(error)}`);\n return null;\n }\n};\n\nexport const summarizeAllChapters = async (chapters: Chapter[]): Promise<ChapterSummary[]> => {\n const summaries: ChapterSummary[] = [];\n\n logInfo(`[Summarizer] Starting summarization of ${chapters.length} chapters (concurrency: ${SUMMARY_CONCURRENCY})`);\n\n for (let i = 0; i < chapters.length; i += SUMMARY_CONCURRENCY) {\n const batch = chapters.slice(i, i + SUMMARY_CONCURRENCY);\n const batchPromises = batch.map((chapter, batchIndex) => summarizeChapter(chapter, i + batchIndex));\n\n const batchResults = await Promise.all(batchPromises);\n\n for (const summary of batchResults) {\n if (summary) {\n summaries.push(summary);\n }\n }\n\n logInfo(`[Summarizer] Progress: ${Math.min(i + SUMMARY_CONCURRENCY, chapters.length)}/${chapters.length} chapters processed`);\n }\n\n logInfo(`[Summarizer] Completed: ${summaries.length}/${chapters.length} summaries generated`);\n\n return summaries;\n};\n","import Database from \"better-sqlite3\";\nimport type { BookRecord, ChatMessage, ChatMessageRole, ChatSession, ChatSessionSummary } from \"../shared/types.js\";\nimport { createDb } from \"./schema.js\";\n\nexport type BookInsert = Omit<\n BookRecord,\n \"createdAt\" | \"indexedAt\" | \"chunkCount\" | \"progressChapter\" | \"narrativeStartIndex\" | \"narrativeEndIndex\"\n> & {\n chunkCount?: number;\n indexedAt?: number | null;\n progressChapter?: number | null;\n summaries?: string;\n narrativeStartIndex?: number | null;\n narrativeEndIndex?: number | null;\n};\n\nconst mapRow = (row: any): BookRecord => ({\n id: row.id,\n title: row.title,\n author: row.author ?? null,\n coverPath: row.cover_path ?? null,\n epubPath: row.epub_path,\n chunkCount: row.chunk_count ?? 0,\n createdAt: row.created_at ?? 0,\n indexedAt: row.indexed_at ?? null,\n chapters: row.chapters ? JSON.parse(row.chapters) : [],\n progressChapter: row.progress_chapter ?? null,\n narrativeStartIndex: row.narrative_start_index ?? null,\n narrativeEndIndex: row.narrative_end_index ?? null,\n});\n\nlet dbPromise: Promise<ReturnType<typeof Database>> | null = null;\n\nconst getDb = async () => {\n if (!dbPromise) {\n dbPromise = createDb();\n }\n return dbPromise;\n};\n\nexport const insertBook = async (book: BookInsert): Promise<string> => {\n const db = await getDb();\n const statement = db.prepare(\n \"INSERT INTO books (id, title, author, cover_path, chapters, epub_path, chunk_count, indexed_at, progress_chapter, narrative_start_index, narrative_end_index) VALUES (@id, @title, @author, @coverPath, @chapters, @epubPath, @chunkCount, @indexedAt, @progressChapter, @narrativeStartIndex, @narrativeEndIndex)\"\n );\n statement.run({\n id: book.id,\n title: book.title,\n author: book.author,\n coverPath: book.coverPath,\n chapters: JSON.stringify(book.chapters ?? []),\n epubPath: book.epubPath,\n chunkCount: book.chunkCount ?? 0,\n indexedAt: book.indexedAt ?? null,\n progressChapter: book.progressChapter ?? null,\n narrativeStartIndex: book.narrativeStartIndex ?? null,\n narrativeEndIndex: book.narrativeEndIndex ?? null,\n });\n return book.id;\n};\n\nexport const updateBook = async (id: string, updates: Partial<BookInsert>) => {\n const fields: string[] = [];\n const params: Record<string, string | number | boolean | null> = { id };\n\n if (updates.title !== undefined) {\n fields.push(\"title = @title\");\n params.title = updates.title;\n }\n if (updates.author !== undefined) {\n fields.push(\"author = @author\");\n params.author = updates.author;\n }\n if (updates.coverPath !== undefined) {\n fields.push(\"cover_path = @coverPath\");\n params.coverPath = updates.coverPath;\n }\n if (updates.chapters !== undefined) {\n fields.push(\"chapters = @chapters\");\n params.chapters = JSON.stringify(updates.chapters);\n }\n if (updates.epubPath !== undefined) {\n fields.push(\"epub_path = @epubPath\");\n params.epubPath = updates.epubPath;\n }\n if (updates.chunkCount !== undefined) {\n fields.push(\"chunk_count = @chunkCount\");\n params.chunkCount = updates.chunkCount;\n }\n if (updates.indexedAt !== undefined) {\n fields.push(\"indexed_at = @indexedAt\");\n params.indexedAt = updates.indexedAt;\n }\n if (updates.progressChapter !== undefined) {\n fields.push(\"progress_chapter = @progressChapter\");\n params.progressChapter = updates.progressChapter;\n }\n if (updates.summaries !== undefined) {\n fields.push(\"summaries = @summaries\");\n params.summaries = updates.summaries;\n }\n if (updates.narrativeStartIndex !== undefined) {\n fields.push(\"narrative_start_index = @narrativeStartIndex\");\n params.narrativeStartIndex = updates.narrativeStartIndex;\n }\n if (updates.narrativeEndIndex !== undefined) {\n fields.push(\"narrative_end_index = @narrativeEndIndex\");\n params.narrativeEndIndex = updates.narrativeEndIndex;\n }\n\n if (fields.length === 0) return;\n\n const db = await getDb();\n db.prepare(`UPDATE books SET ${fields.join(\", \")} WHERE id = @id`).run(params);\n};\n\nexport const getBooks = async (): Promise<BookRecord[]> => {\n const db = await getDb();\n const rows = db.prepare(\"SELECT * FROM books ORDER BY created_at DESC\").all();\n return rows.map(mapRow);\n};\n\nexport const getBook = async (id: string): Promise<BookRecord | null> => {\n const db = await getDb();\n const row = db.prepare(\"SELECT * FROM books WHERE id = ?\").get(id);\n return row ? mapRow(row) : null;\n};\n\nexport const deleteBook = async (id: string) => {\n const db = await getDb();\n db.prepare(\"DELETE FROM chat_messages WHERE session_id IN (SELECT id FROM chat_sessions WHERE book_id = ?)\").run(id);\n db.prepare(\"DELETE FROM chat_sessions WHERE book_id = ?\").run(id);\n db.prepare(\"DELETE FROM books WHERE id = ?\").run(id);\n};\n\nexport type ChatSessionInsert = Omit<ChatSession, \"createdAt\" | \"updatedAt\"> & {\n createdAt?: number;\n updatedAt?: number;\n};\n\nexport type ChatMessageInsert = Omit<ChatMessage, \"createdAt\"> & {\n createdAt?: number;\n};\n\nconst mapSession = (row: any): ChatSession => ({\n id: row.id,\n bookId: row.book_id,\n title: row.title ?? null,\n summary: row.summary ?? null,\n createdAt: row.created_at ?? 0,\n updatedAt: row.updated_at ?? 0,\n});\n\nconst mapSessionSummary = (row: any): ChatSessionSummary => ({\n ...mapSession(row),\n bookTitle: row.book_title ?? null,\n});\n\nconst mapMessage = (row: any): ChatMessage => ({\n id: row.id,\n sessionId: row.session_id,\n role: row.role as ChatMessageRole,\n content: row.content,\n tokenCount: row.token_count ?? null,\n createdAt: row.created_at ?? 0,\n});\n\nexport const insertChatSession = async (session: ChatSessionInsert): Promise<string> => {\n const db = await getDb();\n db.prepare(\n \"INSERT INTO chat_sessions (id, book_id, title, summary, created_at, updated_at) VALUES (@id, @bookId, @title, @summary, @createdAt, @updatedAt)\"\n ).run({\n id: session.id,\n bookId: session.bookId,\n title: session.title ?? null,\n summary: session.summary ?? null,\n createdAt: session.createdAt ?? Date.now(),\n updatedAt: session.updatedAt ?? Date.now(),\n });\n return session.id;\n};\n\nexport const updateChatSession = async (id: string, updates: Partial<ChatSessionInsert>) => {\n const fields: string[] = [];\n const params: Record<string, string | number | null> = { id };\n\n if (updates.title !== undefined) {\n fields.push(\"title = @title\");\n params.title = updates.title;\n }\n if (updates.summary !== undefined) {\n fields.push(\"summary = @summary\");\n params.summary = updates.summary;\n }\n if (updates.updatedAt !== undefined) {\n fields.push(\"updated_at = @updatedAt\");\n params.updatedAt = updates.updatedAt;\n }\n\n if (fields.length === 0) return;\n\n const db = await getDb();\n db.prepare(`UPDATE chat_sessions SET ${fields.join(\", \")} WHERE id = @id`).run(params);\n};\n\nexport const getChatSession = async (id: string): Promise<ChatSession | null> => {\n const db = await getDb();\n const row = db.prepare(\"SELECT * FROM chat_sessions WHERE id = ?\").get(id);\n return row ? mapSession(row) : null;\n};\n\nexport const listChatSessions = async (): Promise<ChatSessionSummary[]> => {\n const db = await getDb();\n const rows = db\n .prepare(\n \"SELECT chat_sessions.*, books.title as book_title FROM chat_sessions LEFT JOIN books ON books.id = chat_sessions.book_id ORDER BY chat_sessions.updated_at DESC\"\n )\n .all();\n return rows.map(mapSessionSummary);\n};\n\nexport const insertChatMessage = async (message: ChatMessageInsert): Promise<string> => {\n const db = await getDb();\n db.prepare(\n \"INSERT INTO chat_messages (id, session_id, role, content, token_count, created_at) VALUES (@id, @sessionId, @role, @content, @tokenCount, @createdAt)\"\n ).run({\n id: message.id,\n sessionId: message.sessionId,\n role: message.role,\n content: message.content,\n tokenCount: message.tokenCount ?? null,\n createdAt: message.createdAt ?? Date.now(),\n });\n return message.id;\n};\n\nexport const getChatMessages = async (sessionId: string, limit?: number): Promise<ChatMessage[]> => {\n const db = await getDb();\n const rows = limit !== undefined\n ? db\n .prepare(\"SELECT * FROM chat_messages WHERE session_id = ? ORDER BY created_at DESC LIMIT ?\")\n .all(sessionId, limit)\n : db.prepare(\"SELECT * FROM chat_messages WHERE session_id = ? ORDER BY created_at ASC\").all(sessionId);\n\n const mapped = rows.map(mapMessage);\n return limit !== undefined ? mapped.reverse() : mapped;\n};\n","import Database from \"better-sqlite3\";\nimport { resolvePaths } from \"../services/constants.js\";\n\nconst resolveDbPath = async () => {\n const paths = await resolvePaths();\n return paths.dbPath;\n};\n\nexport const createDb = async (): Promise<ReturnType<typeof Database>> => {\n const db = new Database(await resolveDbPath());\n\n db.exec(`\n CREATE TABLE IF NOT EXISTS books (\n id TEXT PRIMARY KEY,\n title TEXT NOT NULL,\n author TEXT,\n cover_path TEXT,\n chapters TEXT,\n epub_path TEXT NOT NULL,\n chunk_count INTEGER DEFAULT 0,\n created_at INTEGER DEFAULT (strftime('%s','now')),\n indexed_at INTEGER,\n progress_chapter INTEGER\n );\n `);\n\n db.exec(`\n CREATE TABLE IF NOT EXISTS chat_sessions (\n id TEXT PRIMARY KEY,\n book_id TEXT NOT NULL,\n title TEXT,\n summary TEXT,\n created_at INTEGER DEFAULT (strftime('%s','now')),\n updated_at INTEGER DEFAULT (strftime('%s','now'))\n );\n `);\n\n db.exec(`\n CREATE TABLE IF NOT EXISTS chat_messages (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL,\n role TEXT NOT NULL,\n content TEXT NOT NULL,\n token_count INTEGER,\n created_at INTEGER DEFAULT (strftime('%s','now'))\n );\n `);\n\n db.exec(\"CREATE INDEX IF NOT EXISTS chat_messages_session_idx ON chat_messages(session_id, created_at)\");\n\n const columns = db\n .prepare(\"PRAGMA table_info(books)\")\n .all()\n .map((col: { name: string }) => col.name);\n\n const ensureColumn = (name: string, definition: string) => {\n if (!columns.includes(name)) {\n db.exec(`ALTER TABLE books ADD COLUMN ${definition}`);\n }\n };\n\n ensureColumn(\"chapters\", \"chapters TEXT\");\n ensureColumn(\"progress_chapter\", \"progress_chapter INTEGER\");\n ensureColumn(\"summaries\", \"summaries TEXT\");\n ensureColumn(\"narrative_start_index\", \"narrative_start_index INTEGER DEFAULT 0\");\n ensureColumn(\"narrative_end_index\", \"narrative_end_index INTEGER\");\n\n return db;\n};\n","import { parseEpub } from \"../services/epub-parser.js\";\nimport { ingestEpub } from \"../services/ingest.js\";\nimport { ensureDataDirs, requireOpenAIKey } from \"../services/constants.js\";\nimport { access } from \"node:fs/promises\";\nimport { prompt } from \"./prompt.js\";\nimport { isInteractive, stdout } from \"./io.js\";\n\nconst parseIndexSelection = (input: string, max: number): number[] => {\n const trimmed = input.trim();\n if (!trimmed) return [];\n const tokens = trimmed.split(\",\").map((part) => part.trim()).filter(Boolean);\n const indices = new Set<number>();\n for (const token of tokens) {\n if (token.includes(\"-\")) {\n const [startRaw, endRaw] = token.split(\"-\");\n const start = Number(startRaw);\n const end = Number(endRaw);\n if (!Number.isFinite(start) || !Number.isFinite(end)) continue;\n for (let i = Math.min(start, end); i <= Math.max(start, end); i++) {\n if (i >= 0 && i < max) indices.add(i);\n }\n } else {\n const index = Number(token);\n if (Number.isFinite(index) && index >= 0 && index < max) indices.add(index);\n }\n }\n return Array.from(indices).sort((a, b) => a - b);\n};\n\nexport const ingestCommand = async (filePath: string, options: { manual?: boolean; summarize?: boolean }) => {\n requireOpenAIKey();\n await ensureDataDirs();\n try {\n await access(filePath);\n } catch {\n throw new Error(`File not found: ${filePath}`);\n }\n\n let selectedChapterIndices: number[] | undefined;\n if (options.manual) {\n if (!isInteractive()) {\n throw new Error(\"Manual chapter selection requires an interactive terminal.\");\n }\n const parsed = await parseEpub(filePath);\n if (parsed.chapterTitles.length === 0) {\n throw new Error(\"No chapters found in EPUB\");\n }\n\n stdout(\"Chapters:\");\n parsed.chapterTitles.forEach((title, index) => {\n const marker = index >= parsed.narrativeStartIndex && index <= parsed.narrativeEndIndex ? \"*\" : \" \";\n stdout(`${marker} [${index}] ${title}`);\n });\n stdout(\"\\nEnter chapter indices to ingest (e.g. 0-10,12). Press Enter for narrative range.\");\n const answer = await prompt(\"Selection: \");\n const indices = parseIndexSelection(answer, parsed.chapterTitles.length);\n if (indices.length > 0) {\n selectedChapterIndices = indices;\n } else {\n selectedChapterIndices = Array.from(\n { length: parsed.narrativeEndIndex - parsed.narrativeStartIndex + 1 },\n (_, i) => i + parsed.narrativeStartIndex\n );\n }\n }\n\n const result = await ingestEpub(filePath, selectedChapterIndices, { summarize: options.summarize ?? false });\n stdout(`\\nDone. Book indexed as ${result.id}`);\n};\n","import { createInterface } from \"node:readline/promises\";\nimport { handleSigint } from \"./io.js\";\n\nexport const prompt = async (question: string): Promise<string> => {\n const release = handleSigint();\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n const response = await rl.question(question);\n return response.trim();\n } finally {\n rl.close();\n release();\n }\n};\n\nexport const confirm = async (question: string): Promise<boolean> => {\n const response = await prompt(question);\n const normalized = response.trim().toLowerCase();\n return normalized === \"y\" || normalized === \"yes\";\n};\n","import { ingestCommand } from \"../ingest.js\";\n\nexport const registerBookIngest = (program: import(\"commander\").Command) => {\n program\n .command(\"ingest\")\n .description(\"Ingest an EPUB file\")\n .argument(\"<path>\", \"Path to the EPUB file\")\n .option(\"--manual\", \"Interactive chapter selection\")\n .option(\"--summary\", \"Enable AI chapter summaries\")\n .action(async (path: string, options: { manual?: boolean; summary?: boolean }) => {\n const summarize = Boolean(options.summary);\n await ingestCommand(path, { manual: options.manual, summarize });\n });\n};\n","import { getBooks } from \"../db/queries.js\";\nimport { ensureDataDirs } from \"../services/constants.js\";\nimport { stdout } from \"./io.js\";\n\nconst formatDate = (timestamp: number | null) => {\n if (!timestamp) return \"-\";\n return new Date(timestamp).toISOString().slice(0, 10);\n};\n\nexport const listCommand = async () => {\n await ensureDataDirs();\n const books = await getBooks();\n if (books.length === 0) {\n stdout(\"No books indexed yet.\");\n return;\n }\n\n stdout(\"ID | Title | Author | Chunks | Indexed | Status\");\n stdout(\"---------|-------|--------|--------|--------|-------\");\n for (const book of books) {\n const shortId = book.id.slice(0, 8);\n const title = book.title;\n const author = book.author || \"-\";\n const chunks = String(book.chunkCount ?? 0);\n const indexed = formatDate(book.indexedAt);\n const status = book.indexedAt ? \"[indexed]\" : \"[pending]\";\n stdout(`${shortId} | ${title} | ${author} | ${chunks} | ${indexed} | ${status}`);\n }\n};\n","import { listCommand } from \"../list.js\";\n\nexport const registerBookList = (program: import(\"commander\").Command) => {\n program\n .command(\"list\")\n .description(\"List indexed books\")\n .action(async () => {\n await listCommand();\n });\n};\n","import { getBooks } from \"../db/queries.js\";\n\nexport const resolveBookId = async (input: string): Promise<string | null> => {\n const books = await getBooks();\n const exact = books.find((book) => book.id === input);\n if (exact) return exact.id;\n const matches = books.filter((book) => book.id.startsWith(input));\n if (matches.length === 1) return matches[0]!.id;\n if (matches.length > 1) {\n throw new Error(`Ambiguous id prefix \"${input}\" (${matches.length} matches)`);\n }\n return null;\n};\n","import { getBook } from \"../db/queries.js\";\nimport { resolveBookId } from \"./utils.js\";\nimport { ensureDataDirs } from \"../services/constants.js\";\nimport { stdout } from \"./io.js\";\n\nexport const showCommand = async (id: string) => {\n await ensureDataDirs();\n const resolvedId = await resolveBookId(id);\n if (!resolvedId) {\n throw new Error(`Book not found: ${id}`);\n }\n const book = await getBook(resolvedId);\n if (!book) {\n throw new Error(`Book not found: ${id}`);\n }\n\n stdout(`Title: ${book.title}`);\n stdout(`Author: ${book.author ?? \"-\"}`);\n stdout(`ID: ${book.id}`);\n stdout(`Chunks: ${book.chunkCount}`);\n stdout(`Indexed: ${book.indexedAt ? new Date(book.indexedAt).toISOString() : \"-\"}`);\n stdout(`Narrative range: ${book.narrativeStartIndex ?? 0} to ${book.narrativeEndIndex ?? book.chapters.length - 1}`);\n stdout(`Progress chapter: ${book.progressChapter ?? \"-\"}`);\n stdout(\"\\nChapters:\");\n\n book.chapters.forEach((title: string, index: number) => {\n const marker = index === book.narrativeStartIndex ? \"[start]\" : index === book.narrativeEndIndex ? \"[end]\" : \"\";\n stdout(` [${index}] ${title} ${marker}`.trim());\n });\n};\n","import { showCommand } from \"../show.js\";\n\nexport const registerBookShow = (program: import(\"commander\").Command) => {\n program\n .command(\"show\")\n .description(\"Show full book metadata\")\n .argument(\"<id>\", \"Book id or prefix\")\n .action(async (id: string) => {\n await showCommand(id);\n });\n};\n","import { embed, streamText } from \"ai\";\nimport { openai } from \"@ai-sdk/openai\";\nimport { getBook } from \"../db/queries.js\";\nimport { resolveBookId } from \"./utils.js\";\nimport { queryBookIndex } from \"../services/vector-store.js\";\nimport { ensureDataDirs, getModels, isAskEnabled, requireOpenAIKey } from \"../services/constants.js\";\nimport { handleSigint } from \"./io.js\";\n\nconst formatContext = (chunks: Array<{ content: string; chapterTitle: string; chapterIndex: number }>) =>\n chunks\n .map(\n (chunk, index) =>\n `Excerpt [${index + 1}] (${chunk.chapterTitle || `Chapter ${chunk.chapterIndex + 1}`}):\\n${chunk.content}`\n )\n .join(\"\\n\\n\");\n\nexport const askCommand = async (\n id: string,\n question: string,\n options: { topK: number; maxChapter?: number }\n) => {\n if (!(await isAskEnabled())) {\n throw new Error(\"Ask is disabled in config (askEnabled: false). Enable it to use this command.\");\n }\n\n requireOpenAIKey();\n await ensureDataDirs();\n\n const resolvedId = await resolveBookId(id);\n if (!resolvedId) {\n throw new Error(`Book not found: ${id}`);\n }\n const book = await getBook(resolvedId);\n if (!book) {\n throw new Error(`Book not found: ${id}`);\n }\n\n const models = await getModels();\n const { embedding } = await embed({\n model: openai.embeddingModel(models.embedding),\n value: question,\n });\n\n const narrativeStart = book.narrativeStartIndex ?? 0;\n const userProgress = book.progressChapter ?? null;\n const maxChapterIndex = options.maxChapter !== undefined\n ? narrativeStart + options.maxChapter\n : userProgress !== null\n ? narrativeStart + userProgress\n : undefined;\n\n const retrievalLimit = options.topK * 3;\n const allMatches = await queryBookIndex(resolvedId, embedding, question, retrievalLimit, maxChapterIndex);\n\n const summaries = allMatches.filter((m) => m.type === \"summary\");\n const chunks = allMatches.filter((m) => m.type !== \"summary\");\n\n const topSummaries = summaries.slice(0, 2);\n const topChunks = chunks.slice(0, Math.max(0, options.topK - topSummaries.length));\n const selectedMatches = [...topSummaries, ...topChunks];\n\n const context = formatContext(selectedMatches);\n\n const releaseSigint = handleSigint();\n const stream = streamText({\n model: openai(models.chat),\n system: `You are a reading companion helping readers understand this book.\n\nGuidelines:\n- Use the provided chapter summaries and excerpts to answer questions\n- Chapter summaries provide high-level context about characters, events, and plot\n- Excerpts provide specific details and quotes\n- When asked for recaps or \"what happened\", synthesize from summaries\n- Don't cite table of contents, front matter, or structural elements\n- If truly unsure, briefly say so - but try to answer from available context first\n- Cite sources using [1], [2], etc. at the end of relevant sentences\n- The context may be limited to earlier chapters only - don't infer beyond what's provided`,\n prompt: `Question: ${question}\\n\\n${context}`,\n });\n\n try {\n for await (const part of stream.textStream) {\n process.stdout.write(part);\n }\n } finally {\n releaseSigint();\n }\n\n if (selectedMatches.length > 0) {\n process.stdout.write(\"\\n\\nSources:\\n\");\n selectedMatches.forEach((match, index) => {\n const title = match.chapterTitle || `Chapter ${match.chapterIndex + 1}`;\n const excerpt = match.content.slice(0, 120).replace(/\\s+/g, \" \");\n process.stdout.write(`[${index + 1}] ${title}: ${excerpt}\\n`);\n });\n }\n};\n","export type QueryOptions = {\n topK: number;\n maxChapter?: number;\n};\n\nexport const parseQueryOptions = (options: { topK: string; maxChapter?: string }): QueryOptions => {\n const topK = Number(options.topK);\n if (!Number.isFinite(topK) || topK <= 0) {\n throw new Error(\"--top-k must be a positive number.\");\n }\n let maxChapter: number | undefined;\n if (options.maxChapter !== undefined) {\n const parsed = Number(options.maxChapter);\n if (!Number.isFinite(parsed) || parsed < 0) {\n throw new Error(\"--max-chapter must be a non-negative number.\");\n }\n maxChapter = parsed;\n }\n\n return { topK, maxChapter };\n};\n","import { askCommand } from \"../ask.js\";\nimport { parseQueryOptions } from \"../query-options.js\";\n\nexport const registerBookAsk = (program: import(\"commander\").Command) => {\n program\n .command(\"ask\")\n .description(\"Ask a question about a book\")\n .argument(\"<id>\", \"Book id or prefix\")\n .argument(\"<question>\", \"Question to ask\")\n .option(\"--top-k <n>\", \"Number of passages to retrieve\", \"5\")\n .option(\"--max-chapter <n>\", \"Spoiler-free limit (0-based within narrative)\")\n .action(async (\n id: string,\n question: string,\n options: { topK: string; maxChapter?: string }\n ) => {\n const { topK, maxChapter } = parseQueryOptions(options);\n await askCommand(id, question, { topK, maxChapter });\n });\n};\n","import { embed } from \"ai\";\nimport { openai } from \"@ai-sdk/openai\";\nimport { getBook } from \"../db/queries.js\";\nimport { resolveBookId } from \"./utils.js\";\nimport { queryBookIndex } from \"../services/vector-store.js\";\nimport { ensureDataDirs, getModels, requireOpenAIKey } from \"../services/constants.js\";\nimport { stdout } from \"./io.js\";\n\nexport const searchCommand = async (\n id: string,\n query: string,\n options: { topK: number; maxChapter?: number }\n) => {\n requireOpenAIKey();\n await ensureDataDirs();\n const resolvedId = await resolveBookId(id);\n if (!resolvedId) {\n throw new Error(`Book not found: ${id}`);\n }\n const book = await getBook(resolvedId);\n if (!book) {\n throw new Error(`Book not found: ${id}`);\n }\n\n const models = await getModels();\n const { embedding } = await embed({\n model: openai.embeddingModel(models.embedding),\n value: query,\n });\n\n const maxChapterIndex = options.maxChapter !== undefined\n ? (book.narrativeStartIndex ?? 0) + options.maxChapter\n : book.progressChapter !== null\n ? (book.narrativeStartIndex ?? 0) + (book.progressChapter ?? 0)\n : undefined;\n const results = await queryBookIndex(resolvedId, embedding, query, options.topK, maxChapterIndex);\n\n if (results.length === 0) {\n stdout(\"No results.\");\n return;\n }\n\n results.forEach((result, index) => {\n const chapterTitle = result.chapterTitle || `Chapter ${result.chapterIndex + 1}`;\n const excerpt = result.content.slice(0, 200).replace(/\\s+/g, \" \");\n stdout(`\\n#${index + 1} score=${result.score.toFixed(4)} type=${result.type || \"chunk\"}`);\n stdout(`${chapterTitle} (chapter ${result.chapterIndex})`);\n stdout(excerpt);\n });\n};\n","import { searchCommand } from \"../search.js\";\nimport { parseQueryOptions } from \"../query-options.js\";\n\nexport const registerBookSearch = (program: import(\"commander\").Command) => {\n program\n .command(\"search\")\n .description(\"Vector search without LLM\")\n .argument(\"<id>\", \"Book id or prefix\")\n .argument(\"<query>\", \"Search query\")\n .option(\"--top-k <n>\", \"Number of passages to retrieve\", \"5\")\n .option(\"--max-chapter <n>\", \"Spoiler-free limit (0-based within narrative)\")\n .action(async (\n id: string,\n query: string,\n options: { topK: string; maxChapter?: string }\n ) => {\n const { topK, maxChapter } = parseQueryOptions(options);\n await searchCommand(id, query, { topK, maxChapter });\n });\n};\n","import { unlink } from \"node:fs/promises\";\nimport { deleteBook, getBook } from \"../db/queries.js\";\nimport { resolveBookId } from \"./utils.js\";\nimport { deleteBookIndex } from \"../services/vector-store.js\";\nimport { ensureDataDirs } from \"../services/constants.js\";\nimport { confirm } from \"./prompt.js\";\nimport { isInteractive, stdout } from \"./io.js\";\n\nexport const deleteCommand = async (id: string, options: { force?: boolean }) => {\n await ensureDataDirs();\n const resolvedId = await resolveBookId(id);\n if (!resolvedId) {\n throw new Error(`Book not found: ${id}`);\n }\n const book = await getBook(resolvedId);\n if (!book) {\n throw new Error(`Book not found: ${id}`);\n }\n\n if (!options.force) {\n if (!isInteractive()) {\n throw new Error(\"Delete confirmation requires an interactive terminal. Use --force to bypass.\");\n }\n const ok = await confirm(`Delete \"${book.title}\" (${book.id})? [y/N] `);\n if (!ok) {\n stdout(\"Cancelled.\");\n return;\n }\n }\n\n await deleteBook(resolvedId);\n await deleteBookIndex(resolvedId);\n if (book.epubPath) {\n await unlink(book.epubPath).catch(() => undefined);\n }\n stdout(`Deleted book ${book.id}`);\n};\n","import { deleteCommand } from \"../delete.js\";\n\nexport const registerBookDelete = (program: import(\"commander\").Command) => {\n program\n .command(\"delete\")\n .description(\"Remove book, EPUB, and vectors\")\n .argument(\"<id>\", \"Book id or prefix\")\n .option(\"--force\", \"Skip confirmation\")\n .action(async (id: string, options: { force?: boolean }) => {\n await deleteCommand(id, { force: options.force });\n });\n};\n","import { configPath } from \"../config.js\";\nimport { stdout } from \"./io.js\";\n\nexport const configCommand = async () => {\n const path = configPath();\n stdout(path);\n};\n","import { configCommand } from \"../config.js\";\n\nexport const registerConfigPath = (program: import(\"commander\").Command) => {\n program\n .command(\"path\")\n .description(\"Print config path\")\n .action(async () => {\n await configCommand();\n });\n};\n","import { ensureConfigDirs, configPath, loadConfig } from \"../config.js\";\nimport { mkdir, writeFile, access } from \"node:fs/promises\";\nimport { stdout } from \"./io.js\";\n\nexport const initConfigCommand = async () => {\n const path = configPath();\n await ensureConfigDirs(path);\n try {\n await access(path);\n stdout(`Config already exists: ${path}`);\n return;\n } catch {\n // file does not exist\n }\n const resolved = await loadConfig();\n const template = {\n dataDir: \"~/.local/share/mycroft\",\n askEnabled: resolved.askEnabled,\n models: resolved.models,\n };\n await writeFile(path, JSON.stringify(template, null, 2), \"utf-8\");\n await mkdir(resolved.dataDir, { recursive: true });\n stdout(`Created config at ${path}`);\n};\n","import { initConfigCommand } from \"../init-config.js\";\n\nexport const registerConfigInit = (program: import(\"commander\").Command) => {\n program\n .command(\"init\")\n .description(\"Create default config file\")\n .action(async () => {\n await initConfigCommand();\n });\n};\n","import { configPath, loadConfig } from \"../config.js\";\nimport { stdout } from \"./io.js\";\n\nexport const resolveConfigCommand = async () => {\n const path = configPath();\n const config = await loadConfig();\n stdout(`Config: ${path}`);\n stdout(`Data dir: ${config.dataDir}`);\n stdout(`Ask enabled: ${config.askEnabled}`);\n stdout(`Models: embedding=${config.models.embedding} summary=${config.models.summary} chat=${config.models.chat}`);\n};\n","import { resolveConfigCommand } from \"../resolve-config.js\";\n\nexport const registerConfigResolve = (program: import(\"commander\").Command) => {\n program\n .command(\"resolve\")\n .description(\"Print resolved config values\")\n .action(async () => {\n await resolveConfigCommand();\n });\n};\n","import { ensureConfigDirs, configPath, loadConfig } from \"../config.js\";\nimport { writeFile } from \"node:fs/promises\";\nimport { prompt } from \"./prompt.js\";\nimport { isInteractive, stdout } from \"./io.js\";\n\nconst isDefault = (input: string) => input === \"\" || input.toLowerCase() === \"-y\";\n\nconst parseBoolean = (input: string, fallback: boolean) => {\n if (isDefault(input)) return fallback;\n const normalized = input.toLowerCase();\n if ([\"y\", \"yes\", \"true\", \"1\"].includes(normalized)) return true;\n if ([\"n\", \"no\", \"false\", \"0\"].includes(normalized)) return false;\n return fallback;\n};\n\nexport const onboardCommand = async () => {\n if (!isInteractive()) {\n throw new Error(\"Onboarding requires an interactive terminal.\");\n }\n const defaults = await loadConfig();\n const path = configPath();\n\n stdout(\"\\nmycroft\");\n stdout(\"Press Enter or type -y to accept defaults.\");\n\n const dataDirInput = await prompt(`Data directory [${defaults.dataDir}]: `);\n const dataDir = isDefault(dataDirInput) ? defaults.dataDir : dataDirInput;\n\n const askEnabledInput = await prompt(`Enable ask (LLM answers) [${defaults.askEnabled ? \"Y\" : \"N\"}]: `);\n const askEnabled = parseBoolean(askEnabledInput, defaults.askEnabled);\n\n const embeddingInput = await prompt(`Embedding model [${defaults.models.embedding}]: `);\n const embedding = isDefault(embeddingInput) ? defaults.models.embedding : embeddingInput;\n\n const summaryInput = await prompt(`Summary model [${defaults.models.summary}]: `);\n const summary = isDefault(summaryInput) ? defaults.models.summary : summaryInput;\n\n const chatInput = await prompt(`Chat model [${defaults.models.chat}]: `);\n const chat = isDefault(chatInput) ? defaults.models.chat : chatInput;\n\n await ensureConfigDirs(path);\n await writeFile(\n path,\n JSON.stringify(\n {\n dataDir,\n askEnabled,\n models: {\n embedding,\n summary,\n chat,\n },\n },\n null,\n 2\n ),\n \"utf-8\"\n );\n\n stdout(\"\\nSetup complete.\");\n stdout(`Config: ${path}`);\n stdout(`Data dir: ${dataDir}`);\n\n if (!process.env.OPENAI_API_KEY) {\n stdout(\"\\nOPENAI_API_KEY is not set.\");\n stdout(\"Export it to enable embeddings and chat:\");\n stdout(\" export OPENAI_API_KEY=\\\"...\\\"\");\n }\n\n stdout(\"\\nNext step:\");\n stdout(\" mycroft book ingest /path/to/book.epub\");\n};\n","import { onboardCommand } from \"../onboard.js\";\n\nexport const registerConfigOnboard = (program: import(\"commander\").Command) => {\n program\n .command(\"onboard\")\n .description(\"Initialize config and show next step\")\n .action(async () => {\n await onboardCommand();\n });\n};\n","import { randomUUID } from \"node:crypto\";\nimport { embed, generateText } from \"ai\";\nimport { openai } from \"@ai-sdk/openai\";\nimport { getBook, getChatMessages, getChatSession, insertChatMessage, insertChatSession, listChatSessions, updateChatSession } from \"../db/queries.js\";\nimport type { ChatMessage, ChatSession, ChatSessionSummary } from \"../shared/types.js\";\nimport { ensureDataDirs, getModels, isAskEnabled, requireOpenAIKey } from \"./constants.js\";\nimport { queryBookIndex } from \"./vector-store.js\";\nimport { resolveBookId } from \"../commands/utils.js\";\n\nconst MAX_RECENT_MESSAGES = 12;\nconst SUMMARY_TRIGGER_MESSAGES = 24;\nconst SUMMARY_TARGET_WORDS = 160;\n\ntype ChatAskOptions = {\n topK: number;\n maxChapter?: number;\n};\n\nconst formatContext = (chunks: Array<{ content: string; chapterTitle: string; chapterIndex: number }>) =>\n chunks\n .map(\n (chunk, index) =>\n `Excerpt [${index + 1}] (${chunk.chapterTitle || `Chapter ${chunk.chapterIndex + 1}`}):\\n${chunk.content}`\n )\n .join(\"\\n\\n\");\n\nconst estimateTokens = (text: string): number => Math.ceil(text.length / 4);\n\nconst summarizeMessages = async (messages: ChatMessage[]): Promise<string> => {\n const transcript = messages\n .map((message) => `${message.role.toUpperCase()}: ${message.content}`)\n .join(\"\\n\\n\");\n const models = await getModels();\n const { text } = await generateText({\n model: openai(models.summary),\n prompt: `Summarize this conversation so far in ~${SUMMARY_TARGET_WORDS} words. Focus on facts, decisions, and unresolved questions.\\n\\n${transcript}`,\n temperature: 0.3,\n });\n return text.trim();\n};\n\nconst buildConversationContext = (session: ChatSession, messages: ChatMessage[]) => {\n const summary = session.summary ? `Conversation summary:\\n${session.summary}` : \"\";\n const recent = messages\n .slice(-MAX_RECENT_MESSAGES)\n .map((message) => `${message.role.toUpperCase()}: ${message.content}`)\n .join(\"\\n\\n\");\n return [summary, recent].filter(Boolean).join(\"\\n\\n\");\n};\n\nconst maybeSummarizeSession = async (session: ChatSession, messages: ChatMessage[], updatedAt: number) => {\n if (messages.length < SUMMARY_TRIGGER_MESSAGES) return;\n const summary = await summarizeMessages(messages.slice(0, -MAX_RECENT_MESSAGES));\n await updateChatSession(session.id, { summary, updatedAt });\n};\n\nexport const listSessions = async (): Promise<ChatSessionSummary[]> => listChatSessions();\n\nexport const getSession = async (id: string): Promise<ChatSession | null> => getChatSession(id);\n\nexport const getSessionMessages = async (sessionId: string, limit?: number) => getChatMessages(sessionId, limit);\n\nexport const startSession = async (bookId: string, title?: string): Promise<ChatSession> => {\n await ensureDataDirs();\n const resolvedId = await resolveBookId(bookId);\n if (!resolvedId) {\n throw new Error(`Book not found: ${bookId}`);\n }\n const sessionId = randomUUID();\n await insertChatSession({\n id: sessionId,\n bookId: resolvedId,\n title: title ?? null,\n summary: null,\n });\n const session = await getChatSession(sessionId);\n if (!session) {\n throw new Error(\"Failed to create chat session.\");\n }\n return session;\n};\n\nexport const chatAsk = async (sessionId: string, question: string, options: ChatAskOptions) => {\n if (!(await isAskEnabled())) {\n throw new Error(\"Ask is disabled in config (askEnabled: false). Enable it to use this command.\");\n }\n requireOpenAIKey();\n await ensureDataDirs();\n\n const session = await getChatSession(sessionId);\n if (!session) {\n throw new Error(`Chat session not found: ${sessionId}`);\n }\n const book = await getBook(session.bookId);\n if (!book) {\n throw new Error(`Book not found: ${session.bookId}`);\n }\n\n const models = await getModels();\n const { embedding } = await embed({\n model: openai.embeddingModel(models.embedding),\n value: question,\n });\n\n const narrativeStart = book.narrativeStartIndex ?? 0;\n const userProgress = book.progressChapter ?? null;\n const maxChapterIndex = options.maxChapter !== undefined\n ? narrativeStart + options.maxChapter\n : userProgress !== null\n ? narrativeStart + userProgress\n : undefined;\n\n const retrievalLimit = options.topK * 3;\n const allMatches = await queryBookIndex(session.bookId, embedding, question, retrievalLimit, maxChapterIndex);\n const summaries = allMatches.filter((m) => m.type === \"summary\");\n const chunks = allMatches.filter((m) => m.type !== \"summary\");\n const topSummaries = summaries.slice(0, 2);\n const topChunks = chunks.slice(0, Math.max(0, options.topK - topSummaries.length));\n const selectedMatches = [...topSummaries, ...topChunks];\n const context = formatContext(selectedMatches);\n\n const messages = await getChatMessages(sessionId);\n const conversation = buildConversationContext(session, messages);\n\n const now = Date.now();\n const userMessage: ChatMessage = {\n id: randomUUID(),\n sessionId,\n role: \"user\",\n content: question,\n tokenCount: estimateTokens(question),\n createdAt: now,\n };\n await insertChatMessage(userMessage);\n\n const prompt = [\n conversation ? `Conversation:\\n${conversation}` : \"\",\n `Question: ${question}`,\n context,\n ].filter(Boolean).join(\"\\n\\n\");\n\n const { text } = await generateText({\n model: openai(models.chat),\n system: `You are a reading companion helping readers understand this book.\\n\\nGuidelines:\\n- Use the provided chapter summaries and excerpts to answer questions\\n- Chapter summaries provide high-level context about characters, events, and plot\\n- Excerpts provide specific details and quotes\\n- When asked for recaps or \\\"what happened\\\", synthesize from summaries\\n- Don't cite table of contents, front matter, or structural elements\\n- If truly unsure, briefly say so - but try to answer from available context first\\n- Cite sources using [1], [2], etc. at the end of relevant sentences\\n- The context may be limited to earlier chapters only - don't infer beyond what's provided`,\n prompt,\n });\n\n const assistantMessage: ChatMessage = {\n id: randomUUID(),\n sessionId,\n role: \"assistant\",\n content: text,\n tokenCount: estimateTokens(text),\n createdAt: now,\n };\n await insertChatMessage(assistantMessage);\n const updatedAt = Date.now();\n await updateChatSession(sessionId, { updatedAt });\n await maybeSummarizeSession(session, [...messages, userMessage, assistantMessage], updatedAt);\n\n return { answer: text, sources: selectedMatches };\n};\n","import { startSession } from \"../../services/chat.js\";\nimport { stdout } from \"../io.js\";\n\nexport const registerChatStart = (program: import(\"commander\").Command) => {\n program\n .command(\"start\")\n .description(\"Start a chat session for a book\")\n .argument(\"<id>\", \"Book id or prefix\")\n .option(\"--title <title>\", \"Session title\")\n .action(async (id: string, options: { title?: string }) => {\n const session = await startSession(id, options.title);\n stdout(`Started chat session ${session.id} for book ${session.bookId}`);\n });\n};\n","import { listChatSessions } from \"../../db/queries.js\";\nimport type { ChatSessionSummary } from \"../../shared/types.js\";\n\nexport const resolveChatSessionId = async (input: string): Promise<string | null> => {\n const sessions: ChatSessionSummary[] = await listChatSessions();\n const exact = sessions.find((session) => session.id === input);\n if (exact) return exact.id;\n const matches = sessions.filter((session) => session.id.startsWith(input));\n if (matches.length === 1) return matches[0]!.id;\n if (matches.length > 1) {\n throw new Error(`Ambiguous session id prefix \"${input}\" (${matches.length} matches)`);\n }\n return null;\n};\n","import { chatAsk } from \"../../services/chat.js\";\nimport { resolveChatSessionId } from \"./utils.js\";\nimport { stdout } from \"../io.js\";\nimport { parseQueryOptions } from \"../query-options.js\";\n\nexport const registerChatAsk = (program: import(\"commander\").Command) => {\n program\n .command(\"ask\")\n .description(\"Ask a question in a chat session\")\n .argument(\"<session>\", \"Chat session id or prefix\")\n .argument(\"<question>\", \"Question to ask\")\n .option(\"--top-k <n>\", \"Number of passages to retrieve\", \"5\")\n .option(\"--max-chapter <n>\", \"Spoiler-free limit (0-based within narrative)\")\n .action(async (\n sessionId: string,\n question: string,\n options: { topK: string; maxChapter?: string }\n ) => {\n const { topK, maxChapter } = parseQueryOptions(options);\n\n const resolvedId = await resolveChatSessionId(sessionId);\n if (!resolvedId) {\n throw new Error(`Chat session not found: ${sessionId}`);\n }\n const { answer, sources } = await chatAsk(resolvedId, question, { topK, maxChapter });\n stdout(answer);\n\n if (sources.length > 0) {\n stdout(\"\\nSources:\");\n sources.forEach((match, index) => {\n const title = match.chapterTitle || `Chapter ${match.chapterIndex + 1}`;\n const excerpt = match.content.slice(0, 120).replace(/\\s+/g, \" \");\n stdout(`[${index + 1}] ${title}: ${excerpt}`);\n });\n }\n });\n};\n","import { listSessions } from \"../../services/chat.js\";\nimport { stdout } from \"../io.js\";\n\nconst formatDate = (timestamp: number | null) => {\n if (!timestamp) return \"-\";\n return new Date(timestamp).toISOString().slice(0, 10);\n};\n\nexport const registerChatList = (program: import(\"commander\").Command) => {\n program\n .command(\"list\")\n .description(\"List chat sessions\")\n .action(async () => {\n const sessions = await listSessions();\n if (sessions.length === 0) {\n stdout(\"No chat sessions yet.\");\n return;\n }\n\n stdout(\"ID | Book | Updated | Title\");\n stdout(\"---------|------|---------|------\");\n for (const session of sessions) {\n const shortId = session.id.slice(0, 8);\n const book = session.bookTitle || session.bookId.slice(0, 8);\n const updated = formatDate(session.updatedAt);\n const title = session.title || \"-\";\n stdout(`${shortId} | ${book} | ${updated} | ${title}`);\n }\n });\n};\n","import { getSession, getSessionMessages } from \"../../services/chat.js\";\nimport { resolveChatSessionId } from \"./utils.js\";\nimport { stdout } from \"../io.js\";\n\nexport const registerChatShow = (program: import(\"commander\").Command) => {\n program\n .command(\"show\")\n .description(\"Show chat session details\")\n .argument(\"<session>\", \"Chat session id or prefix\")\n .option(\"--tail <n>\", \"Show last N messages\", \"10\")\n .action(async (sessionId: string, options: { tail: string }) => {\n const tail = Number(options.tail);\n if (!Number.isFinite(tail) || tail <= 0) {\n throw new Error(\"--tail must be a positive number.\");\n }\n\n const resolvedId = await resolveChatSessionId(sessionId);\n if (!resolvedId) {\n throw new Error(`Chat session not found: ${sessionId}`);\n }\n const session = await getSession(resolvedId);\n if (!session) {\n throw new Error(`Chat session not found: ${sessionId}`);\n }\n stdout(`ID: ${session.id}`);\n stdout(`Book ID: ${session.bookId}`);\n stdout(`Title: ${session.title ?? \"-\"}`);\n const updated = session.updatedAt ? new Date(session.updatedAt).toISOString() : \"-\";\n stdout(`Updated: ${updated}`);\n\n const messages = await getSessionMessages(resolvedId, tail);\n if (messages.length === 0) {\n stdout(\"\\nNo messages yet.\");\n return;\n }\n\n stdout(\"\\nMessages:\");\n messages.forEach((message) => {\n stdout(`[${message.role}] ${message.content}`);\n });\n });\n};\n","import { chatAsk, getSession } from \"../../services/chat.js\";\nimport { resolveChatSessionId } from \"./utils.js\";\nimport { isInteractive, stdout } from \"../io.js\";\nimport { prompt } from \"../prompt.js\";\nimport { parseQueryOptions } from \"../query-options.js\";\n\nconst shouldExit = (input: string) => {\n const normalized = input.trim().toLowerCase();\n return normalized === \"exit\" || normalized === \"quit\" || normalized === \":q\";\n};\n\nexport const registerChatRepl = (program: import(\"commander\").Command) => {\n program\n .command(\"repl\")\n .description(\"Start interactive chat session\")\n .argument(\"<session>\", \"Chat session id or prefix\")\n .option(\"--top-k <n>\", \"Number of passages to retrieve\", \"5\")\n .option(\"--max-chapter <n>\", \"Spoiler-free limit (0-based within narrative)\")\n .action(async (\n sessionId: string,\n options: { topK: string; maxChapter?: string }\n ) => {\n if (!isInteractive()) {\n throw new Error(\"Chat repl requires an interactive terminal.\");\n }\n const { topK, maxChapter } = parseQueryOptions(options);\n\n const resolvedId = await resolveChatSessionId(sessionId);\n if (!resolvedId) {\n throw new Error(`Chat session not found: ${sessionId}`);\n }\n const session = await getSession(resolvedId);\n if (!session) {\n throw new Error(`Chat session not found: ${sessionId}`);\n }\n\n stdout(`Chatting in session ${session.id}. Type 'exit' to quit.`);\n\n while (true) {\n const question = await prompt(\"You: \");\n if (!question.trim()) continue;\n if (shouldExit(question)) break;\n const { answer, sources } = await chatAsk(session.id, question, { topK, maxChapter });\n stdout(`\\n${answer}`);\n if (sources.length > 0) {\n stdout(\"\\nSources:\");\n sources.forEach((match, index) => {\n const title = match.chapterTitle || `Chapter ${match.chapterIndex + 1}`;\n const excerpt = match.content.slice(0, 120).replace(/\\s+/g, \" \");\n stdout(`[${index + 1}] ${title}: ${excerpt}`);\n });\n }\n stdout(\"\");\n }\n });\n};\n","import { registerChatStart } from \"./start.js\";\nimport { registerChatAsk } from \"./ask.js\";\nimport { registerChatList } from \"./list.js\";\nimport { registerChatShow } from \"./show.js\";\nimport { registerChatRepl } from \"./repl.js\";\n\nexport const registerChatCommands = (program: import(\"commander\").Command) => {\n const chat = program.command(\"chat\").description(\"Run multi-turn chat sessions\");\n registerChatStart(chat);\n registerChatAsk(chat);\n registerChatList(chat);\n registerChatShow(chat);\n registerChatRepl(chat);\n};\n"],"mappings":"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","names":["configPath","readFile","dirname","resolve","mkdir","mkdir","mkdir","openai","CHARS_PER_TOKEN","openai","mkdir","program","program","program","openai","openai","program","embed","openai","embed","openai","program","unlink","unlink","program","program","mkdir","access","access","mkdir","program","program","writeFile","writeFile","program","randomUUID","embed","generateText","openai","SUMMARY_TARGET_WORDS","formatContext","estimateTokens","generateText","openai","randomUUID","embed","prompt","program","program","formatDate","program","program","program","program","dirname","resolve","readFile"]}
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@fs/mycroft",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
+ "license": "MIT",
4
5
  "type": "module",
5
6
  "bin": {
6
7
  "mycroft": "./bin/mycroft.js"
@@ -9,6 +10,7 @@
9
10
  "bin",
10
11
  "dist",
11
12
  "README.md",
13
+ "LICENSE",
12
14
  "completions"
13
15
  ],
14
16
  "publishConfig": {