mulmocast-preprocessor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # mulmocast-preprocessor
2
+
3
+ Preprocessor for MulmoScript that enables generating multiple variations (full, summary, teaser, etc.) from a single script.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install mulmocast-preprocessor
9
+ ```
10
+
11
+ ## Features
12
+
13
+ - **Profile-based variants**: Generate different versions (summary, teaser) from one script
14
+ - **Section filtering**: Extract beats by section
15
+ - **Tag filtering**: Extract beats by tags
16
+ - **Profile listing**: List available profiles with beat counts
17
+ - **CLI tool**: Command-line interface for processing scripts
18
+
19
+ ## CLI Usage
20
+
21
+ ```bash
22
+ # Process script with profile
23
+ mulmocast-preprocessor script.json --profile summary -o summary.json
24
+
25
+ # Output to stdout (for piping)
26
+ mulmocast-preprocessor script.json --profile teaser
27
+
28
+ # List available profiles
29
+ mulmocast-preprocessor profiles script.json
30
+ ```
31
+
32
+ ### CLI Options
33
+
34
+ | Option | Alias | Description |
35
+ |--------|-------|-------------|
36
+ | `--profile <name>` | `-p` | Profile name to apply (default: "default") |
37
+ | `--output <path>` | `-o` | Output file path (default: stdout) |
38
+ | `--help` | `-h` | Show help |
39
+ | `--version` | `-v` | Show version |
40
+
41
+ ## Programmatic Usage
42
+
43
+ ### Basic Example
44
+
45
+ ```typescript
46
+ import { processScript, listProfiles, applyProfile } from "mulmocast-preprocessor";
47
+ import type { ExtendedScript } from "mulmocast-preprocessor";
48
+
49
+ const script: ExtendedScript = {
50
+ title: "My Presentation",
51
+ beats: [
52
+ {
53
+ text: "Full introduction text here...",
54
+ variants: {
55
+ summary: { text: "Brief intro" },
56
+ teaser: { skip: true }
57
+ },
58
+ meta: { section: "intro", tags: ["important"] }
59
+ },
60
+ {
61
+ text: "Main content...",
62
+ meta: { section: "main", tags: ["core"] }
63
+ }
64
+ ]
65
+ };
66
+
67
+ // List available profiles
68
+ const profiles = listProfiles(script);
69
+ // [{ name: "default", beatCount: 2 }, { name: "summary", beatCount: 2 }, { name: "teaser", beatCount: 1, skippedCount: 1 }]
70
+
71
+ // Generate summary version
72
+ const summary = applyProfile(script, "summary");
73
+ // First beat's text is replaced with "Brief intro"
74
+
75
+ // Process with multiple options
76
+ const result = processScript(script, {
77
+ profile: "summary",
78
+ section: "intro"
79
+ });
80
+ ```
81
+
82
+ ## API
83
+
84
+ ### `processScript(script, options)`
85
+
86
+ Main processing function that applies profile and filters.
87
+
88
+ **Parameters:**
89
+ - `script: ExtendedScript` - Input script with variants/meta
90
+ - `options: ProcessOptions` - Processing options
91
+ - `profile?: string` - Profile name to apply
92
+ - `section?: string` - Filter by section
93
+ - `tags?: string[]` - Filter by tags (OR logic)
94
+
95
+ **Returns:** `MulmoScript` - Standard MulmoScript with variants/meta stripped
96
+
97
+ ### `applyProfile(script, profileName)`
98
+
99
+ Apply a profile to the script, replacing text/image and skipping marked beats.
100
+
101
+ **Parameters:**
102
+ - `script: ExtendedScript` - Input script
103
+ - `profileName: string` - Profile name
104
+
105
+ **Returns:** `MulmoScript` - Processed script
106
+
107
+ ### `listProfiles(script)`
108
+
109
+ Get list of available profiles from script.
110
+
111
+ **Parameters:**
112
+ - `script: ExtendedScript` - Input script
113
+
114
+ **Returns:** `ProfileInfo[]` - Array of profile info with beat counts
115
+
116
+ ### `filterBySection(script, section)`
117
+
118
+ Filter beats by section.
119
+
120
+ ### `filterByTags(script, tags)`
121
+
122
+ Filter beats by tags (extracts beats that have any of the specified tags).
123
+
124
+ ## Extended Schema
125
+
126
+ ### ExtendedBeat
127
+
128
+ ```typescript
129
+ interface ExtendedBeat extends MulmoBeat {
130
+ variants?: Record<string, BeatVariant>;
131
+ meta?: BeatMeta;
132
+ }
133
+ ```
134
+
135
+ ### BeatVariant
136
+
137
+ ```typescript
138
+ interface BeatVariant {
139
+ text?: string; // Override text
140
+ skip?: boolean; // Skip this beat
141
+ image?: MulmoImage; // Override image
142
+ imagePrompt?: string; // Override imagePrompt
143
+ }
144
+ ```
145
+
146
+ ### BeatMeta
147
+
148
+ ```typescript
149
+ interface BeatMeta {
150
+ tags?: string[];
151
+ section?: string;
152
+ context?: string;
153
+ keywords?: string[];
154
+ expectedQuestions?: string[];
155
+ }
156
+ ```
157
+
158
+ ## License
159
+
160
+ MIT
@@ -0,0 +1 @@
1
+ export declare const preprocessorVersion = "0.0.1";
@@ -0,0 +1,3 @@
1
+ // Preprocessor actions
2
+ // TODO: implement preprocessor functionality
3
+ export const preprocessorVersion = "0.0.1";
@@ -0,0 +1,9 @@
1
+ interface ProcessOptions {
2
+ profile?: string;
3
+ output?: string;
4
+ }
5
+ /**
6
+ * Process script with profile and output result
7
+ */
8
+ export declare const processCommand: (scriptPath: string, options: ProcessOptions) => void;
9
+ export {};
@@ -0,0 +1,32 @@
1
+ import { readFileSync, writeFileSync } from "fs";
2
+ import { GraphAILogger } from "graphai";
3
+ import { processScript } from "../../core/process.js";
4
+ /**
5
+ * Process script with profile and output result
6
+ */
7
+ export const processCommand = (scriptPath, options) => {
8
+ try {
9
+ const content = readFileSync(scriptPath, "utf-8");
10
+ const script = JSON.parse(content);
11
+ const result = processScript(script, {
12
+ profile: options.profile,
13
+ });
14
+ const output = JSON.stringify(result, null, 2);
15
+ if (options.output) {
16
+ writeFileSync(options.output, output);
17
+ GraphAILogger.info(`Output written to ${options.output}`);
18
+ }
19
+ else {
20
+ process.stdout.write(output + "\n");
21
+ }
22
+ }
23
+ catch (error) {
24
+ if (error instanceof Error) {
25
+ GraphAILogger.error(`Error: ${error.message}`);
26
+ }
27
+ else {
28
+ GraphAILogger.error("Unknown error occurred");
29
+ }
30
+ process.exit(1);
31
+ }
32
+ };
@@ -0,0 +1,4 @@
1
+ /**
2
+ * List available profiles in script
3
+ */
4
+ export declare const profilesCommand: (scriptPath: string) => void;
@@ -0,0 +1,32 @@
1
+ import { readFileSync } from "fs";
2
+ import { GraphAILogger } from "graphai";
3
+ import { listProfiles } from "../../core/profiles.js";
4
+ /**
5
+ * List available profiles in script
6
+ */
7
+ export const profilesCommand = (scriptPath) => {
8
+ try {
9
+ const content = readFileSync(scriptPath, "utf-8");
10
+ const script = JSON.parse(content);
11
+ const profiles = listProfiles(script);
12
+ GraphAILogger.log("\nAvailable profiles:");
13
+ profiles.forEach((profile) => {
14
+ const displayName = profile.displayName ? ` (${profile.displayName})` : "";
15
+ const skipped = profile.skippedCount > 0 ? `, ${profile.skippedCount} skipped` : "";
16
+ GraphAILogger.log(` ${profile.name}${displayName}: ${profile.beatCount} beats${skipped}`);
17
+ if (profile.description) {
18
+ GraphAILogger.log(` ${profile.description}`);
19
+ }
20
+ });
21
+ GraphAILogger.log("");
22
+ }
23
+ catch (error) {
24
+ if (error instanceof Error) {
25
+ GraphAILogger.error(`Error: ${error.message}`);
26
+ }
27
+ else {
28
+ GraphAILogger.error("Unknown error occurred");
29
+ }
30
+ process.exit(1);
31
+ }
32
+ };
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+ import yargs from "yargs";
3
+ import { hideBin } from "yargs/helpers";
4
+ import { processCommand } from "./commands/process.js";
5
+ import { profilesCommand } from "./commands/profiles.js";
6
+ yargs(hideBin(process.argv))
7
+ .command("$0 <script>", "Process MulmoScript with profile", (builder) => builder
8
+ .positional("script", {
9
+ describe: "Path to MulmoScript JSON file",
10
+ type: "string",
11
+ demandOption: true,
12
+ })
13
+ .option("profile", {
14
+ alias: "p",
15
+ describe: "Profile name to apply",
16
+ type: "string",
17
+ default: "default",
18
+ })
19
+ .option("output", {
20
+ alias: "o",
21
+ describe: "Output file path (default: stdout)",
22
+ type: "string",
23
+ }), (argv) => {
24
+ processCommand(argv.script, {
25
+ profile: argv.profile,
26
+ output: argv.output,
27
+ });
28
+ })
29
+ .command("profiles <script>", "List available profiles in script", (builder) => builder.positional("script", {
30
+ describe: "Path to MulmoScript JSON file",
31
+ type: "string",
32
+ demandOption: true,
33
+ }), (argv) => {
34
+ profilesCommand(argv.script);
35
+ })
36
+ .example("$0 script.json --profile summary -o summary.json", "Apply summary profile and save to file")
37
+ .example("$0 script.json -p teaser", "Apply teaser profile and output to stdout")
38
+ .example("$0 profiles script.json", "List all available profiles")
39
+ .help()
40
+ .alias("h", "help")
41
+ .version()
42
+ .alias("v", "version")
43
+ .strict()
44
+ .parse();
@@ -0,0 +1,14 @@
1
+ import type { MulmoScript } from "mulmocast";
2
+ import type { ExtendedScript } from "../types/index.js";
3
+ /**
4
+ * Filter beats by section
5
+ */
6
+ export declare const filterBySection: (script: ExtendedScript, section: string) => MulmoScript;
7
+ /**
8
+ * Filter beats by tags (extract beats that have any of the specified tags)
9
+ */
10
+ export declare const filterByTags: (script: ExtendedScript, tags: string[]) => MulmoScript;
11
+ /**
12
+ * Strip variants and meta fields, converting to standard MulmoScript
13
+ */
14
+ export declare const stripExtendedFields: (script: ExtendedScript) => MulmoScript;
@@ -0,0 +1,26 @@
1
+ const stripBeatExtendedFields = (beat) => {
2
+ const { variants: __variants, meta: __meta, ...baseBeat } = beat;
3
+ return baseBeat;
4
+ };
5
+ const filterBeats = (script, predicate) => {
6
+ const { outputProfiles: __outputProfiles, ...baseScript } = script;
7
+ return {
8
+ ...baseScript,
9
+ beats: script.beats.filter(predicate).map(stripBeatExtendedFields),
10
+ };
11
+ };
12
+ /**
13
+ * Filter beats by section
14
+ */
15
+ export const filterBySection = (script, section) => filterBeats(script, (beat) => beat.meta?.section === section);
16
+ /**
17
+ * Filter beats by tags (extract beats that have any of the specified tags)
18
+ */
19
+ export const filterByTags = (script, tags) => {
20
+ const tagSet = new Set(tags);
21
+ return filterBeats(script, (beat) => (beat.meta?.tags ?? []).some((tag) => tagSet.has(tag)));
22
+ };
23
+ /**
24
+ * Strip variants and meta fields, converting to standard MulmoScript
25
+ */
26
+ export const stripExtendedFields = (script) => filterBeats(script, () => true);
@@ -0,0 +1,7 @@
1
+ import type { MulmoScript } from "mulmocast";
2
+ import type { ExtendedScript, ProcessOptions } from "../types/index.js";
3
+ /**
4
+ * Main processing function
5
+ * Applies profile and filters in a single call
6
+ */
7
+ export declare const processScript: (script: ExtendedScript, options?: ProcessOptions) => MulmoScript;
@@ -0,0 +1,16 @@
1
+ import { applyProfile } from "./variant.js";
2
+ import { filterBySection, filterByTags, stripExtendedFields } from "./filter.js";
3
+ const toExtendedScript = (script) => ({
4
+ ...script,
5
+ beats: script.beats.map((beat) => ({ ...beat })),
6
+ });
7
+ /**
8
+ * Main processing function
9
+ * Applies profile and filters in a single call
10
+ */
11
+ export const processScript = (script, options = {}) => {
12
+ const afterProfile = options.profile && options.profile !== "default" ? applyProfile(script, options.profile) : stripExtendedFields(script);
13
+ const afterSection = options.section ? filterBySection(toExtendedScript(afterProfile), options.section) : afterProfile;
14
+ const afterTags = options.tags && options.tags.length > 0 ? filterByTags(toExtendedScript(afterSection), options.tags) : afterSection;
15
+ return afterTags;
16
+ };
@@ -0,0 +1,5 @@
1
+ import type { ExtendedScript, ProfileInfo } from "../types/index.js";
2
+ /**
3
+ * Get list of available profiles from script
4
+ */
5
+ export declare const listProfiles: (script: ExtendedScript) => ProfileInfo[];
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Get list of available profiles from script
3
+ */
4
+ export const listProfiles = (script) => {
5
+ const profileNames = new Set(["default"]);
6
+ script.beats
7
+ .filter((beat) => beat.variants)
8
+ .flatMap((beat) => Object.keys(beat.variants))
9
+ .forEach((name) => profileNames.add(name));
10
+ const buildProfileInfo = (profileName) => {
11
+ const outputProfile = script.outputProfiles?.[profileName];
12
+ const { beatCount, skippedCount } = profileName === "default"
13
+ ? { beatCount: script.beats.length, skippedCount: 0 }
14
+ : script.beats.reduce((acc, beat) => {
15
+ const isSkipped = beat.variants?.[profileName]?.skip === true;
16
+ return {
17
+ beatCount: acc.beatCount + (isSkipped ? 0 : 1),
18
+ skippedCount: acc.skippedCount + (isSkipped ? 1 : 0),
19
+ };
20
+ }, { beatCount: 0, skippedCount: 0 });
21
+ return {
22
+ name: profileName,
23
+ displayName: outputProfile?.name,
24
+ description: outputProfile?.description,
25
+ beatCount,
26
+ skippedCount,
27
+ };
28
+ };
29
+ return Array.from(profileNames)
30
+ .map(buildProfileInfo)
31
+ .sort((a, b) => {
32
+ if (a.name === "default")
33
+ return -1;
34
+ if (b.name === "default")
35
+ return 1;
36
+ return a.name.localeCompare(b.name);
37
+ });
38
+ };
@@ -0,0 +1,6 @@
1
+ import type { MulmoScript } from "mulmocast";
2
+ import type { ExtendedScript } from "../types/index.js";
3
+ /**
4
+ * Apply profile to script and return standard MulmoScript
5
+ */
6
+ export declare const applyProfile: (script: ExtendedScript, profileName: string) => MulmoScript;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Apply profile variant to a single beat
3
+ * @returns Processed beat, or null if skip is set
4
+ */
5
+ const applyVariantToBeat = (beat, profileName) => {
6
+ const variant = beat.variants?.[profileName];
7
+ if (variant?.skip) {
8
+ return null;
9
+ }
10
+ const { variants: __variants, meta: __meta, ...baseBeat } = beat;
11
+ if (!variant) {
12
+ return baseBeat;
13
+ }
14
+ const { skip: __skip, ...overrides } = variant;
15
+ return { ...baseBeat, ...overrides };
16
+ };
17
+ /**
18
+ * Apply profile to script and return standard MulmoScript
19
+ */
20
+ export const applyProfile = (script, profileName) => {
21
+ const { outputProfiles: __outputProfiles, ...baseScript } = script;
22
+ return {
23
+ ...baseScript,
24
+ beats: script.beats.map((beat) => applyVariantToBeat(beat, profileName)).filter((beat) => beat !== null),
25
+ };
26
+ };
package/lib/index.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ export { processScript } from "./core/process.js";
2
+ export { applyProfile } from "./core/variant.js";
3
+ export { filterBySection, filterByTags, stripExtendedFields } from "./core/filter.js";
4
+ export { listProfiles } from "./core/profiles.js";
5
+ export type { BeatVariant, BeatMeta, ExtendedBeat, ExtendedScript, OutputProfile, ProcessOptions, ProfileInfo } from "./types/index.js";
6
+ export { beatVariantSchema, beatMetaSchema, extendedBeatSchema, extendedScriptSchema, outputProfileSchema } from "./types/index.js";
package/lib/index.js ADDED
@@ -0,0 +1,7 @@
1
+ // Core API
2
+ export { processScript } from "./core/process.js";
3
+ export { applyProfile } from "./core/variant.js";
4
+ export { filterBySection, filterByTags, stripExtendedFields } from "./core/filter.js";
5
+ export { listProfiles } from "./core/profiles.js";
6
+ // Schemas (for validation)
7
+ export { beatVariantSchema, beatMetaSchema, extendedBeatSchema, extendedScriptSchema, outputProfileSchema } from "./types/index.js";