mulmocast-preprocessor 0.2.0 → 0.2.1

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/README.md CHANGED
@@ -14,6 +14,8 @@ npm install mulmocast-preprocessor
14
14
  - **Section filtering**: Extract beats by section
15
15
  - **Tag filtering**: Extract beats by tags
16
16
  - **Profile listing**: List available profiles with beat counts
17
+ - **AI Summarization**: Generate summaries using LLM (OpenAI, Anthropic, Groq, Gemini)
18
+ - **AI Query**: Ask questions about script content with interactive mode
17
19
  - **CLI tool**: Command-line interface for processing scripts
18
20
 
19
21
  ## CLI Usage
@@ -36,9 +38,23 @@ mulmocast-preprocessor script.json --profile summary --section chapter1
36
38
 
37
39
  # List available profiles
38
40
  mulmocast-preprocessor profiles script.json
41
+
42
+ # Summarize script content
43
+ mulmocast-preprocessor summarize script.json
44
+ mulmocast-preprocessor summarize script.json --format markdown
45
+ mulmocast-preprocessor summarize script.json -l ja # Output in Japanese
46
+ mulmocast-preprocessor summarize https://example.com/script.json # From URL
47
+
48
+ # Query script content
49
+ mulmocast-preprocessor query script.json "What is the main topic?"
50
+ mulmocast-preprocessor query script.json "登場人物は?" -l ja
51
+
52
+ # Interactive query mode
53
+ mulmocast-preprocessor query script.json -i
54
+ mulmocast-preprocessor query script.json # Omit question for interactive mode
39
55
  ```
40
56
 
41
- ### CLI Options
57
+ ### CLI Options (process command)
42
58
 
43
59
  | Option | Alias | Description |
44
60
  |--------|-------|-------------|
@@ -49,6 +65,41 @@ mulmocast-preprocessor profiles script.json
49
65
  | `--help` | `-h` | Show help |
50
66
  | `--version` | `-v` | Show version |
51
67
 
68
+ ### CLI Options (summarize command)
69
+
70
+ | Option | Alias | Description |
71
+ |--------|-------|-------------|
72
+ | `--provider` | | LLM provider: openai, anthropic, groq, gemini (default: openai) |
73
+ | `--model` | `-m` | Model name |
74
+ | `--format` | `-f` | Output format: text, markdown (default: text) |
75
+ | `--lang` | `-l` | Output language (e.g., ja, en, zh) |
76
+ | `--target-length` | | Target summary length in characters |
77
+ | `--system-prompt` | | Custom system prompt |
78
+ | `--verbose` | | Show detailed progress |
79
+ | `--section` | `-s` | Filter by section name |
80
+ | `--tags` | `-t` | Filter by tags (comma-separated) |
81
+
82
+ ### CLI Options (query command)
83
+
84
+ | Option | Alias | Description |
85
+ |--------|-------|-------------|
86
+ | `--interactive` | `-i` | Start interactive query mode |
87
+ | `--provider` | | LLM provider: openai, anthropic, groq, gemini (default: openai) |
88
+ | `--model` | `-m` | Model name |
89
+ | `--lang` | `-l` | Output language (e.g., ja, en, zh) |
90
+ | `--system-prompt` | | Custom system prompt |
91
+ | `--verbose` | | Show detailed progress |
92
+ | `--section` | `-s` | Filter by section name |
93
+ | `--tags` | `-t` | Filter by tags (comma-separated) |
94
+
95
+ ### Interactive Query Commands
96
+
97
+ | Command | Description |
98
+ |---------|-------------|
99
+ | `/clear` | Clear conversation history |
100
+ | `/history` | Show conversation history |
101
+ | `/exit` | Exit interactive mode |
102
+
52
103
  ## Programmatic Usage
53
104
 
54
105
  ### Basic Example
@@ -132,6 +183,54 @@ Filter beats by section.
132
183
 
133
184
  Filter beats by tags (extracts beats that have any of the specified tags).
134
185
 
186
+ ### `summarizeScript(script, options)`
187
+
188
+ Generate a summary of the script content using LLM.
189
+
190
+ **Parameters:**
191
+ - `script: ExtendedScript` - Input script
192
+ - `options: SummarizeOptions` - Summarization options
193
+ - `provider?: LLMProvider` - LLM provider (default: "openai")
194
+ - `model?: string` - Model name
195
+ - `format?: "text" | "markdown"` - Output format
196
+ - `lang?: string` - Output language code
197
+ - `targetLengthChars?: number` - Target length
198
+ - `systemPrompt?: string` - Custom system prompt
199
+
200
+ **Returns:** `Promise<SummarizeResult>` - Summary result with text and metadata
201
+
202
+ ### `queryScript(script, question, options)`
203
+
204
+ Ask a question about the script content.
205
+
206
+ **Parameters:**
207
+ - `script: ExtendedScript` - Input script
208
+ - `question: string` - Question to ask
209
+ - `options: QueryOptions` - Query options (same as summarize)
210
+
211
+ **Returns:** `Promise<QueryResult>` - Answer with question and metadata
212
+
213
+ ### `createInteractiveSession(script, options)`
214
+
215
+ Create an interactive query session for follow-up questions.
216
+
217
+ **Parameters:**
218
+ - `script: ExtendedScript` - Input script
219
+ - `options: QueryOptions` - Query options
220
+
221
+ **Returns:** Session object with `sendInteractiveQuery()` method
222
+
223
+ ## Environment Variables
224
+
225
+ For AI features (summarize, query), set the API key for your LLM provider:
226
+
227
+ | Provider | Environment Variable |
228
+ |----------|---------------------|
229
+ | OpenAI | `OPENAI_API_KEY` |
230
+ | Anthropic | `ANTHROPIC_API_KEY` |
231
+ | Groq | `GROQ_API_KEY` |
232
+ | Gemini | `GEMINI_API_KEY` |
233
+
135
234
  ## Extended Schema
136
235
 
137
236
  ### ExtendedBeat
@@ -7,9 +7,10 @@ interface QueryCommandOptions {
7
7
  verbose?: boolean;
8
8
  section?: string;
9
9
  tags?: string[];
10
+ interactive?: boolean;
10
11
  }
11
12
  /**
12
13
  * Query command handler - outputs answer to stdout
13
14
  */
14
- export declare const queryCommand: (scriptPath: string, question: string, options: QueryCommandOptions) => Promise<void>;
15
+ export declare const queryCommand: (scriptPath: string, question: string | undefined, options: QueryCommandOptions) => Promise<void>;
15
16
  export {};
@@ -1,5 +1,7 @@
1
+ import { createInterface } from "node:readline";
1
2
  import { GraphAILogger } from "graphai";
2
3
  import { queryScript } from "../../core/ai/command/query/index.js";
4
+ import { createInteractiveSession, sendInteractiveQuery, clearHistory } from "../../core/ai/command/query/interactive.js";
3
5
  import { loadScript } from "../utils.js";
4
6
  /**
5
7
  * Query command handler - outputs answer to stdout
@@ -7,6 +9,12 @@ import { loadScript } from "../utils.js";
7
9
  export const queryCommand = async (scriptPath, question, options) => {
8
10
  try {
9
11
  const script = await loadScript(scriptPath);
12
+ // Interactive mode
13
+ if (options.interactive || question === undefined) {
14
+ await runInteractiveMode(scriptPath, script, options);
15
+ return;
16
+ }
17
+ // Single query mode
10
18
  const result = await queryScript(script, question, {
11
19
  provider: options.provider ?? "openai",
12
20
  model: options.model,
@@ -29,3 +37,83 @@ export const queryCommand = async (scriptPath, question, options) => {
29
37
  process.exit(1);
30
38
  }
31
39
  };
40
+ /**
41
+ * Run interactive query mode
42
+ */
43
+ const runInteractiveMode = async (scriptPath, script, options) => {
44
+ const { session, filteredScript, validatedOptions } = createInteractiveSession(script, {
45
+ provider: options.provider ?? "openai",
46
+ model: options.model,
47
+ lang: options.lang,
48
+ systemPrompt: options.systemPrompt,
49
+ verbose: options.verbose ?? false,
50
+ section: options.section,
51
+ tags: options.tags,
52
+ });
53
+ if (filteredScript.beats.length === 0) {
54
+ GraphAILogger.error("No content available to query.");
55
+ process.exit(1);
56
+ }
57
+ const rl = createInterface({
58
+ input: process.stdin,
59
+ output: process.stdout,
60
+ });
61
+ GraphAILogger.info(`Interactive query mode for "${session.scriptTitle}" (${session.beatCount} beats)`);
62
+ GraphAILogger.info("Commands: /clear (clear history), /history (show history), /exit or Ctrl+C (quit)");
63
+ GraphAILogger.info("");
64
+ const prompt = () => {
65
+ rl.question("You: ", async (input) => {
66
+ const trimmedInput = input.trim();
67
+ if (!trimmedInput) {
68
+ prompt();
69
+ return;
70
+ }
71
+ // Handle commands
72
+ if (trimmedInput === "/exit" || trimmedInput === "/quit") {
73
+ GraphAILogger.info("Goodbye!");
74
+ rl.close();
75
+ return;
76
+ }
77
+ if (trimmedInput === "/clear") {
78
+ clearHistory(session);
79
+ GraphAILogger.info("Conversation history cleared.\n");
80
+ prompt();
81
+ return;
82
+ }
83
+ if (trimmedInput === "/history") {
84
+ if (session.history.length === 0) {
85
+ GraphAILogger.info("No conversation history.\n");
86
+ }
87
+ else {
88
+ GraphAILogger.info("Conversation history:");
89
+ session.history.forEach((msg) => {
90
+ const prefix = msg.role === "user" ? "Q" : "A";
91
+ GraphAILogger.info(`${prefix}: ${msg.content}`);
92
+ });
93
+ GraphAILogger.info("");
94
+ }
95
+ prompt();
96
+ return;
97
+ }
98
+ // Send query
99
+ try {
100
+ const answer = await sendInteractiveQuery(filteredScript, trimmedInput, session, validatedOptions);
101
+ GraphAILogger.info(`\nAssistant: ${answer}\n`);
102
+ }
103
+ catch (error) {
104
+ if (error instanceof Error) {
105
+ GraphAILogger.error(`Error: ${error.message}\n`);
106
+ }
107
+ else {
108
+ GraphAILogger.error("Unknown error occurred\n");
109
+ }
110
+ }
111
+ prompt();
112
+ });
113
+ };
114
+ // Handle Ctrl+C
115
+ rl.on("close", () => {
116
+ process.exit(0);
117
+ });
118
+ prompt();
119
+ };
package/lib/cli/index.js CHANGED
@@ -111,16 +111,21 @@ yargs(hideBin(process.argv))
111
111
  tags,
112
112
  });
113
113
  })
114
- .command("query <script> <question>", "Ask a question about the script content", (builder) => builder
114
+ .command("query <script> [question]", "Ask a question about the script content", (builder) => builder
115
115
  .positional("script", {
116
116
  describe: "Path or URL to MulmoScript JSON file",
117
117
  type: "string",
118
118
  demandOption: true,
119
119
  })
120
120
  .positional("question", {
121
- describe: "Question to ask about the script",
121
+ describe: "Question to ask about the script (omit for interactive mode)",
122
122
  type: "string",
123
- demandOption: true,
123
+ })
124
+ .option("interactive", {
125
+ alias: "i",
126
+ describe: "Start interactive query mode",
127
+ type: "boolean",
128
+ default: false,
124
129
  })
125
130
  .option("provider", {
126
131
  describe: "LLM provider (openai, anthropic, groq, gemini)",
@@ -165,6 +170,7 @@ yargs(hideBin(process.argv))
165
170
  verbose: argv.verbose,
166
171
  section: argv.section,
167
172
  tags,
173
+ interactive: argv.interactive,
168
174
  });
169
175
  })
170
176
  .example("$0 script.json --profile summary -o summary.json", "Apply summary profile and save to file")
@@ -179,6 +185,8 @@ yargs(hideBin(process.argv))
179
185
  .example("$0 summarize https://example.com/script.json", "Summarize from URL")
180
186
  .example('$0 query script.json "What is the main topic?"', "Ask a question about the script")
181
187
  .example('$0 query script.json "登場人物は?" -l ja', "Query in Japanese")
188
+ .example("$0 query script.json -i", "Start interactive query mode")
189
+ .example("$0 query script.json", "Interactive mode (question omitted)")
182
190
  .help()
183
191
  .alias("h", "help")
184
192
  .version()
@@ -0,0 +1,22 @@
1
+ import type { ExtendedScript } from "../../../../types/index.js";
2
+ import type { QueryOptions, InteractiveQuerySession, ConversationMessage } from "../../../../types/query.js";
3
+ /**
4
+ * Create an interactive query session
5
+ */
6
+ export declare const createInteractiveSession: (script: ExtendedScript, options?: Partial<QueryOptions>) => {
7
+ session: InteractiveQuerySession;
8
+ filteredScript: ExtendedScript;
9
+ validatedOptions: QueryOptions;
10
+ };
11
+ /**
12
+ * Send a question in an interactive session
13
+ */
14
+ export declare const sendInteractiveQuery: (filteredScript: ExtendedScript, question: string, session: InteractiveQuerySession, options: QueryOptions) => Promise<string>;
15
+ /**
16
+ * Clear conversation history
17
+ */
18
+ export declare const clearHistory: (session: InteractiveQuerySession) => void;
19
+ /**
20
+ * Get conversation history
21
+ */
22
+ export declare const getHistory: (session: InteractiveQuerySession) => ConversationMessage[];
@@ -0,0 +1,44 @@
1
+ import { queryOptionsSchema } from "../../../../types/query.js";
2
+ import { executeLLM, filterScript } from "../../llm.js";
3
+ import { buildInteractiveUserPrompt, getInteractiveSystemPrompt } from "./prompts.js";
4
+ /**
5
+ * Create an interactive query session
6
+ */
7
+ export const createInteractiveSession = (script, options = {}) => {
8
+ const validatedOptions = queryOptionsSchema.parse(options);
9
+ const filteredScript = filterScript(script, validatedOptions);
10
+ const scriptTitle = script.title || "Untitled";
11
+ const session = {
12
+ scriptTitle,
13
+ beatCount: filteredScript.beats.length,
14
+ history: [],
15
+ };
16
+ return { session, filteredScript, validatedOptions };
17
+ };
18
+ /**
19
+ * Send a question in an interactive session
20
+ */
21
+ export const sendInteractiveQuery = async (filteredScript, question, session, options) => {
22
+ if (filteredScript.beats.length === 0) {
23
+ return "No content available to answer the question.";
24
+ }
25
+ const systemPrompt = getInteractiveSystemPrompt(options);
26
+ const userPrompt = buildInteractiveUserPrompt(filteredScript, question, session.history);
27
+ const answer = await executeLLM(systemPrompt, userPrompt, options, options.verbose ? `Interactive query: ${question}` : undefined);
28
+ // Add to history
29
+ session.history.push({ role: "user", content: question });
30
+ session.history.push({ role: "assistant", content: answer });
31
+ return answer;
32
+ };
33
+ /**
34
+ * Clear conversation history
35
+ */
36
+ export const clearHistory = (session) => {
37
+ session.history = [];
38
+ };
39
+ /**
40
+ * Get conversation history
41
+ */
42
+ export const getHistory = (session) => {
43
+ return [...session.history];
44
+ };
@@ -1,4 +1,4 @@
1
- import type { QueryOptions } from "../../../../types/query.js";
1
+ import type { QueryOptions, ConversationMessage } from "../../../../types/query.js";
2
2
  import type { ExtendedScript } from "../../../../types/index.js";
3
3
  /**
4
4
  * Default system prompt for query
@@ -12,3 +12,15 @@ export declare const getSystemPrompt: (options: QueryOptions) => string;
12
12
  * Build user prompt from script and question
13
13
  */
14
14
  export declare const buildUserPrompt: (script: ExtendedScript, question: string) => string;
15
+ /**
16
+ * Default system prompt for interactive query
17
+ */
18
+ export declare const DEFAULT_INTERACTIVE_SYSTEM_PROMPT = "You are answering questions based on the content provided.\n- Answer based ONLY on the information in the provided content\n- If the answer cannot be found in the content, say so clearly\n- Be concise and direct in your answers\n- Do not make up information that is not in the content\n- You may reference previous conversation when answering follow-up questions";
19
+ /**
20
+ * Get system prompt for interactive mode
21
+ */
22
+ export declare const getInteractiveSystemPrompt: (options: QueryOptions) => string;
23
+ /**
24
+ * Build user prompt with conversation history for interactive mode
25
+ */
26
+ export declare const buildInteractiveUserPrompt: (script: ExtendedScript, question: string, history: ConversationMessage[]) => string;
@@ -1,4 +1,4 @@
1
- import { getLanguageName } from "../../llm.js";
1
+ import { getLanguageName, buildScriptContent } from "../../llm.js";
2
2
  /**
3
3
  * Default system prompt for query
4
4
  */
@@ -27,32 +27,61 @@ export const getSystemPrompt = (options) => {
27
27
  */
28
28
  export const buildUserPrompt = (script, question) => {
29
29
  const parts = [];
30
- // Add script metadata
31
- parts.push(`# Script: ${script.title}`);
32
- parts.push(`Language: ${script.lang}`);
30
+ // Add common script content (title, language, sections with beats)
31
+ parts.push(buildScriptContent(script));
32
+ parts.push("---");
33
33
  parts.push("");
34
- // Collect all text from beats
35
- const sections = new Map();
36
- script.beats.forEach((beat, index) => {
37
- const text = beat.text || "";
38
- if (!text.trim())
39
- return;
40
- const section = beat.meta?.section || "main";
41
- if (!sections.has(section)) {
42
- sections.set(section, []);
43
- }
44
- sections.get(section).push(`[${index}] ${text}`);
45
- });
46
- // Output by section
47
- sections.forEach((texts, section) => {
48
- parts.push(`## Section: ${section}`);
49
- texts.forEach((t) => parts.push(t));
50
- parts.push("");
51
- });
34
+ parts.push(`Question: ${question}`);
52
35
  parts.push("");
36
+ parts.push("Answer:");
37
+ return parts.join("\n");
38
+ };
39
+ /**
40
+ * Default system prompt for interactive query
41
+ */
42
+ export const DEFAULT_INTERACTIVE_SYSTEM_PROMPT = `You are answering questions based on the content provided.
43
+ - Answer based ONLY on the information in the provided content
44
+ - If the answer cannot be found in the content, say so clearly
45
+ - Be concise and direct in your answers
46
+ - Do not make up information that is not in the content
47
+ - You may reference previous conversation when answering follow-up questions`;
48
+ /**
49
+ * Get system prompt for interactive mode
50
+ */
51
+ export const getInteractiveSystemPrompt = (options) => {
52
+ if (options.systemPrompt) {
53
+ return options.systemPrompt;
54
+ }
55
+ const basePrompt = DEFAULT_INTERACTIVE_SYSTEM_PROMPT;
56
+ if (options.lang) {
57
+ const langName = getLanguageName(options.lang);
58
+ return `${basePrompt}\n- IMPORTANT: Write the answer in ${langName}`;
59
+ }
60
+ return basePrompt;
61
+ };
62
+ /**
63
+ * Build user prompt with conversation history for interactive mode
64
+ */
65
+ export const buildInteractiveUserPrompt = (script, question, history) => {
66
+ const parts = [];
67
+ // Add common script content (title, language, sections with beats)
68
+ parts.push(buildScriptContent(script));
53
69
  parts.push("---");
54
70
  parts.push("");
55
- parts.push(`Question: ${question}`);
71
+ // Add conversation history if exists
72
+ if (history.length > 0) {
73
+ parts.push("Previous conversation:");
74
+ history.forEach((msg) => {
75
+ if (msg.role === "user") {
76
+ parts.push(`Q: ${msg.content}`);
77
+ }
78
+ else {
79
+ parts.push(`A: ${msg.content}`);
80
+ }
81
+ });
82
+ parts.push("");
83
+ }
84
+ parts.push(`Current question: ${question}`);
56
85
  parts.push("");
57
86
  parts.push("Answer:");
58
87
  return parts.join("\n");
@@ -1,4 +1,4 @@
1
- import { getLanguageName } from "../../llm.js";
1
+ import { getLanguageName, buildScriptContent } from "../../llm.js";
2
2
  /**
3
3
  * Default system prompt for text summary
4
4
  */
@@ -23,28 +23,8 @@ export const DEFAULT_SYSTEM_PROMPT_MARKDOWN = `You are creating a summary based
23
23
  */
24
24
  export const buildUserPrompt = (script, options) => {
25
25
  const parts = [];
26
- // Add script metadata
27
- parts.push(`# Script: ${script.title}`);
28
- parts.push(`Language: ${script.lang}`);
29
- parts.push("");
30
- // Collect all text from beats
31
- const sections = new Map();
32
- script.beats.forEach((beat, index) => {
33
- const text = beat.text || "";
34
- if (!text.trim())
35
- return;
36
- const section = beat.meta?.section || "main";
37
- if (!sections.has(section)) {
38
- sections.set(section, []);
39
- }
40
- sections.get(section).push(`[${index}] ${text}`);
41
- });
42
- // Output by section
43
- sections.forEach((texts, section) => {
44
- parts.push(`## Section: ${section}`);
45
- texts.forEach((t) => parts.push(t));
46
- parts.push("");
47
- });
26
+ // Add common script content (title, language, sections with beats)
27
+ parts.push(buildScriptContent(script));
48
28
  // Add target length if specified
49
29
  if (options.targetLengthChars) {
50
30
  parts.push(`Target summary length: approximately ${options.targetLengthChars} characters`);
@@ -39,6 +39,22 @@ export declare const filterScript: (script: ExtendedScript, options: BaseLLMOpti
39
39
  * Get language name from code
40
40
  */
41
41
  export declare const getLanguageName: (langCode: string) => string;
42
+ /**
43
+ * Build script content for user prompt (common part)
44
+ */
45
+ export declare const buildScriptContent: (script: ExtendedScript) => string;
46
+ /**
47
+ * Command execution result
48
+ */
49
+ export interface CommandResult {
50
+ text: string;
51
+ scriptTitle: string;
52
+ beatCount: number;
53
+ }
54
+ /**
55
+ * Execute a command (summarize, query, etc.) with common logic
56
+ */
57
+ export declare const executeCommand: <T extends BaseLLMOptions>(script: ExtendedScript, options: T, getSystemPrompt: (opts: T) => string, buildUserPrompt: (script: ExtendedScript) => string, verboseMessage: string) => Promise<CommandResult | null>;
42
58
  /**
43
59
  * Execute LLM call with GraphAI
44
60
  */
@@ -103,6 +103,53 @@ export const getLanguageName = (langCode) => {
103
103
  };
104
104
  return langMap[langCode] || langCode;
105
105
  };
106
+ /**
107
+ * Build script content for user prompt (common part)
108
+ */
109
+ export const buildScriptContent = (script) => {
110
+ const parts = [];
111
+ // Add script metadata
112
+ parts.push(`# Script: ${script.title}`);
113
+ parts.push(`Language: ${script.lang}`);
114
+ parts.push("");
115
+ // Collect all text from beats grouped by section
116
+ const sections = new Map();
117
+ script.beats.forEach((beat, index) => {
118
+ const text = beat.text || "";
119
+ if (!text.trim())
120
+ return;
121
+ const section = beat.meta?.section || "main";
122
+ if (!sections.has(section)) {
123
+ sections.set(section, []);
124
+ }
125
+ sections.get(section).push(`[${index}] ${text}`);
126
+ });
127
+ // Output by section
128
+ sections.forEach((texts, section) => {
129
+ parts.push(`## Section: ${section}`);
130
+ texts.forEach((t) => parts.push(t));
131
+ parts.push("");
132
+ });
133
+ return parts.join("\n");
134
+ };
135
+ /**
136
+ * Execute a command (summarize, query, etc.) with common logic
137
+ */
138
+ export const executeCommand = async (script, options, getSystemPrompt, buildUserPrompt, verboseMessage) => {
139
+ const filteredScript = filterScript(script, options);
140
+ const scriptTitle = script.title || "Untitled";
141
+ if (filteredScript.beats.length === 0) {
142
+ return null;
143
+ }
144
+ const systemPrompt = getSystemPrompt(options);
145
+ const userPrompt = buildUserPrompt(filteredScript);
146
+ const text = await executeLLM(systemPrompt, userPrompt, options, verboseMessage);
147
+ return {
148
+ text,
149
+ scriptTitle,
150
+ beatCount: filteredScript.beats.length,
151
+ };
152
+ };
106
153
  /**
107
154
  * Execute LLM call with GraphAI
108
155
  */
package/lib/index.d.ts CHANGED
@@ -4,9 +4,10 @@ export { filterBySection, filterByTags, stripExtendedFields } from "./core/prepr
4
4
  export { listProfiles } from "./core/preprocessing/profiles.js";
5
5
  export { summarizeScript } from "./core/ai/command/summarize/index.js";
6
6
  export { queryScript } from "./core/ai/command/query/index.js";
7
+ export { createInteractiveSession, sendInteractiveQuery, clearHistory, getHistory } from "./core/ai/command/query/interactive.js";
7
8
  export type { BeatVariant, BeatMeta, ExtendedBeat, ExtendedScript, OutputProfile, ProcessOptions, ProfileInfo } from "./types/index.js";
8
9
  export type { SummarizeOptions, SummarizeResult, LLMProvider, SummarizeFormat, ProviderConfig } from "./types/summarize.js";
9
- export type { QueryOptions, QueryResult } from "./types/query.js";
10
+ export type { QueryOptions, QueryResult, ConversationMessage, InteractiveQuerySession } from "./types/query.js";
10
11
  export { beatVariantSchema, beatMetaSchema, extendedBeatSchema, extendedScriptSchema, outputProfileSchema } from "./types/index.js";
11
12
  export { summarizeOptionsSchema, llmProviderSchema, summarizeFormatSchema } from "./types/summarize.js";
12
13
  export { queryOptionsSchema } from "./types/query.js";
package/lib/index.js CHANGED
@@ -6,6 +6,7 @@ export { listProfiles } from "./core/preprocessing/profiles.js";
6
6
  // AI API
7
7
  export { summarizeScript } from "./core/ai/command/summarize/index.js";
8
8
  export { queryScript } from "./core/ai/command/query/index.js";
9
+ export { createInteractiveSession, sendInteractiveQuery, clearHistory, getHistory } from "./core/ai/command/query/interactive.js";
9
10
  // Schemas (for validation)
10
11
  export { beatVariantSchema, beatMetaSchema, extendedBeatSchema, extendedScriptSchema, outputProfileSchema } from "./types/index.js";
11
12
  export { summarizeOptionsSchema, llmProviderSchema, summarizeFormatSchema } from "./types/summarize.js";
@@ -28,3 +28,18 @@ export interface QueryResult {
28
28
  scriptTitle: string;
29
29
  beatCount: number;
30
30
  }
31
+ /**
32
+ * Conversation message for interactive query
33
+ */
34
+ export interface ConversationMessage {
35
+ role: "user" | "assistant";
36
+ content: string;
37
+ }
38
+ /**
39
+ * Interactive query session state
40
+ */
41
+ export interface InteractiveQuerySession {
42
+ scriptTitle: string;
43
+ beatCount: number;
44
+ history: ConversationMessage[];
45
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mulmocast-preprocessor",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Preprocessor for MulmoScript",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",