@tryhamster/gerbil 1.0.0-rc.4 → 1.0.0-rc.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/browser/index.d.ts.map +1 -1
  2. package/dist/browser/index.js +6 -6
  3. package/dist/browser/index.js.map +1 -1
  4. package/dist/cli.mjs +7 -7
  5. package/dist/cli.mjs.map +1 -1
  6. package/dist/frameworks/express.mjs +1 -1
  7. package/dist/frameworks/fastify.mjs +1 -1
  8. package/dist/frameworks/hono.mjs +1 -1
  9. package/dist/frameworks/next.mjs +1 -1
  10. package/dist/frameworks/trpc.mjs +1 -1
  11. package/dist/{gerbil-Dq039a6V.mjs → gerbil-CgLjZy0K.mjs} +5 -5
  12. package/dist/{gerbil-Dq039a6V.mjs.map → gerbil-CgLjZy0K.mjs.map} +1 -1
  13. package/dist/gerbil-IwhB_Sip.mjs +4 -0
  14. package/dist/index.d.mts.map +1 -1
  15. package/dist/index.mjs +4 -4
  16. package/dist/integrations/ai-sdk.mjs +3 -3
  17. package/dist/integrations/langchain.mjs +1 -1
  18. package/dist/integrations/llamaindex.mjs +1 -1
  19. package/dist/integrations/mcp.mjs +4 -4
  20. package/dist/kokoro-DFRQ1OeM.js +20212 -0
  21. package/dist/kokoro-DFRQ1OeM.js.map +1 -0
  22. package/dist/{mcp-DY57Whwj.mjs → mcp-DGK69gbU.mjs} +3 -3
  23. package/dist/{mcp-DY57Whwj.mjs.map → mcp-DGK69gbU.mjs.map} +1 -1
  24. package/dist/{one-liner-CgRVfe5K.mjs → one-liner-BJ7hDVlY.mjs} +2 -2
  25. package/dist/{one-liner-CgRVfe5K.mjs.map → one-liner-BJ7hDVlY.mjs.map} +1 -1
  26. package/dist/{repl-BEusmMZs.mjs → repl-BTq1JyH7.mjs} +3 -3
  27. package/dist/skills/index.d.mts +1 -1
  28. package/dist/skills/index.d.mts.map +1 -1
  29. package/dist/skills/index.mjs +3 -3
  30. package/dist/{skills-BGS20rGK.mjs → skills-Hf3iEkq4.mjs} +2 -2
  31. package/dist/{skills-BGS20rGK.mjs.map → skills-Hf3iEkq4.mjs.map} +1 -1
  32. package/dist/{stt-CkfJswka.mjs → stt-CpLYbGFd.mjs} +2 -8
  33. package/dist/stt-CpLYbGFd.mjs.map +1 -0
  34. package/dist/stt-DRPLEEHB.mjs +3 -0
  35. package/dist/{stt-BtklAjR2.js → stt-Te8Qz-Ay.js} +2 -8
  36. package/dist/stt-Te8Qz-Ay.js.map +1 -0
  37. package/dist/transformers.web-DokyH3rP.js +3 -0
  38. package/dist/transformers.web-M6mCnEYJ.js +30382 -0
  39. package/dist/transformers.web-M6mCnEYJ.js.map +1 -0
  40. package/dist/{tts-DKIOWafo.js → tts-C0xx3CtE.js} +3 -10
  41. package/dist/tts-C0xx3CtE.js.map +1 -0
  42. package/dist/{tts-BFL984rV.mjs → tts-D5hrNcGD.mjs} +2 -9
  43. package/dist/tts-D5hrNcGD.mjs.map +1 -0
  44. package/dist/{tts-Cuu1TOkM.mjs → tts-DzJDBsFR.mjs} +1 -1
  45. package/package.json +1 -1
  46. package/dist/gerbil-DyTEWXLy.mjs +0 -4
  47. package/dist/stt-BT4Rt49f.mjs +0 -3
  48. package/dist/stt-BtklAjR2.js.map +0 -1
  49. package/dist/stt-CkfJswka.mjs.map +0 -1
  50. package/dist/tts-BFL984rV.mjs.map +0 -1
  51. package/dist/tts-DKIOWafo.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"skills-BGS20rGK.mjs","names":["ctx: SkillContext<TInput>","path","fs","loaded: string[]","analysisPrompts: Record<string, string>","stylePrompts: Record<string, string>","saveWav","join","tmpdir","captionPrompts: Record<string, string>","lengthGuides: Record<string, number>","platformGuides: Record<string, string>","execSync","comparisonPrompts: Record<string, string>","focusPrompts: Record<string, string>","detailLengths: Record<string, number>","saveWav","playAudio","speak","extractionPrompts: Record<string, string>","formatInstructions: Record<string, string>","extract","saveWav","existsSync","textToRead: string","readFileSync","join","tmpdir","join","tmpdir","existsSync","readFileSync","text: string"],"sources":["../src/skills/registry.ts","../src/skills/loader.ts","../src/skills/builtin/analyze-screenshot.ts","../src/skills/builtin/announce.ts","../src/skills/builtin/caption-image.ts","../src/skills/builtin/commit.ts","../src/skills/builtin/compare-images.ts","../src/skills/builtin/describe-image.ts","../src/skills/builtin/explain.ts","../src/skills/builtin/extract.ts","../src/skills/builtin/extract-from-image.ts","../src/skills/builtin/read-aloud.ts","../src/skills/builtin/review.ts","../src/skills/builtin/speak.ts","../src/skills/builtin/summarize.ts","../src/skills/builtin/test.ts","../src/skills/builtin/title.ts","../src/skills/builtin/transcribe.ts","../src/skills/builtin/translate.ts"],"sourcesContent":["/**\n * Skill Registry\n *\n * Central registry for all skills (built-in and custom).\n */\n\nimport type { Gerbil } from \"../core/gerbil.js\";\nimport { getInstance } from \"../core/one-liner.js\";\nimport type { Skill, SkillContext, SkillDefinition, SkillInfo } from \"./types.js\";\n\n// ============================================\n// Registry Storage\n// ============================================\n\nconst registry = new Map<string, Skill>();\nconst skillSources = new Map<string, string>(); // name -> source file\n\n// Reserved names that cannot be used for custom skills (CLI commands/views)\n// Note: Built-in skills like \"commit\", \"summarize\" ARE allowed because they\n// have dedicated CLI commands, but new skills cannot use these view names\nconst RESERVED_NAMES = new Set([\n // REPL views (would conflict with CLI shortcuts)\n \"repl\",\n \"chat\",\n \"skills\",\n \"tools\",\n \"model\",\n \"integrate\",\n \"benchmark\",\n \"info\",\n \"serve\",\n \"cache\",\n // CLI commands\n \"generate\",\n \"models\",\n \"bench\",\n // Aliases\n \"r\",\n \"c\",\n \"g\",\n // Other reserved\n \"help\",\n \"version\",\n \"gerbil\",\n]);\n\n// ============================================\n// Define Skill\n// ============================================\n\n/**\n * Define and register a skill\n *\n * @example\n * ```ts\n * const sentiment = defineSkill({\n * name: \"sentiment\",\n * description: \"Analyze sentiment of text\",\n * input: z.object({ text: z.string() }),\n * output: z.object({ sentiment: z.enum([\"positive\", \"negative\", \"neutral\"]) }),\n * async run({ input, gerbil }) {\n * return gerbil.json(`Analyze sentiment: ${input.text}`, { schema: this.output });\n * }\n * });\n * ```\n */\nexport function defineSkill<TInput, TOutput>(\n definition: SkillDefinition<TInput, TOutput>,\n): Skill<TInput, TOutput> {\n // Validate name format\n if (!/^[a-z][a-z0-9-]*$/.test(definition.name)) {\n throw new Error(`Skill name must be kebab-case starting with a letter: ${definition.name}`);\n }\n\n // Check for reserved names (CLI commands)\n if (RESERVED_NAMES.has(definition.name)) {\n throw new Error(\n `Skill name \"${definition.name}\" is reserved for CLI commands. Choose a different name.`,\n );\n }\n\n // Create the skill function\n const execute = async (input: TInput): Promise<TOutput> => skill.run(input);\n\n // Create the skill object\n const skill = Object.assign(execute, {\n definition,\n\n async run(input: TInput, gerbil?: Gerbil): Promise<TOutput> {\n // Get or create Gerbil instance\n const g = gerbil ?? (await getInstance(definition.model));\n\n // Validate input if schema provided\n let validatedInput = input;\n if (definition.input) {\n const parsed = definition.input.safeParse(input);\n if (!parsed.success) {\n throw new Error(`Invalid input for skill \"${definition.name}\": ${parsed.error.message}`);\n }\n validatedInput = parsed.data;\n }\n\n // Create context\n const ctx: SkillContext<TInput> = {\n input: validatedInput,\n gerbil: g,\n rawInput: input,\n definition: definition as SkillDefinition<TInput, unknown>,\n };\n\n // Run the skill\n const result = await definition.run.call(definition, ctx);\n\n // Validate output if schema provided\n if (definition.output && typeof result !== \"string\") {\n const parsed = definition.output.safeParse(result);\n if (!parsed.success) {\n throw new Error(\n `Invalid output from skill \"${definition.name}\": ${parsed.error.message}`,\n );\n }\n return parsed.data;\n }\n\n return result as TOutput;\n },\n }) as Skill<TInput, TOutput>;\n\n // Register the skill\n registry.set(definition.name, skill as Skill);\n\n return skill;\n}\n\n// ============================================\n// Use Skill\n// ============================================\n\n/**\n * Get a skill by name\n *\n * @example\n * ```ts\n * const sentiment = useSkill(\"sentiment\");\n * const result = await sentiment({ text: \"I love this!\" });\n * ```\n */\nexport function useSkill<TInput = unknown, TOutput = unknown>(\n name: string,\n): Skill<TInput, TOutput> {\n const skill = registry.get(name);\n if (!skill) {\n throw new Error(`Skill not found: \"${name}\". Available: ${listSkills().join(\", \") || \"none\"}`);\n }\n return skill as Skill<TInput, TOutput>;\n}\n\n// ============================================\n// Registry Operations\n// ============================================\n\n/**\n * List all registered skill names\n */\nexport function listSkills(): string[] {\n return Array.from(registry.keys()).sort();\n}\n\n/**\n * Get skill metadata\n */\nexport function getSkillInfo(name: string): SkillInfo | undefined {\n const skill = registry.get(name);\n if (!skill) {\n return;\n }\n\n return {\n name: skill.definition.name,\n description: skill.definition.description,\n version: skill.definition.version,\n author: skill.definition.author,\n builtin: !skillSources.has(name),\n source: skillSources.get(name),\n };\n}\n\n/**\n * Check if a skill exists\n */\nexport function hasSkill(name: string): boolean {\n return registry.has(name);\n}\n\n/**\n * Remove a skill from registry\n */\nexport function removeSkill(name: string): boolean {\n skillSources.delete(name);\n return registry.delete(name);\n}\n\n/**\n * Clear all skills from registry\n */\nexport function clearSkills(): void {\n registry.clear();\n skillSources.clear();\n}\n\n/**\n * Get all skill info\n */\nexport function getAllSkillInfo(): SkillInfo[] {\n return listSkills().map((name) => getSkillInfo(name)!);\n}\n\n/**\n * Check if a name is reserved (cannot be used for custom skills)\n */\nexport function isReservedName(name: string): boolean {\n return RESERVED_NAMES.has(name);\n}\n\n/**\n * Get list of reserved names\n */\nexport function getReservedNames(): string[] {\n return Array.from(RESERVED_NAMES).sort();\n}\n\n// ============================================\n// Internal: Register with source\n// ============================================\n\n/**\n * Register a skill with its source file (used by loader)\n * @internal\n */\nexport function registerSkillWithSource(skill: Skill, source: string): void {\n registry.set(skill.definition.name, skill);\n skillSources.set(skill.definition.name, source);\n}\n","/**\n * Skill Loader\n *\n * Load skills from files, directories, and packages.\n */\n\nimport { pathToFileURL } from \"node:url\";\nimport { registerSkillWithSource } from \"./registry.js\";\nimport type { LoadSkillsOptions, Skill } from \"./types.js\";\n\n// ============================================\n// Auto-Load Project Skills\n// ============================================\n\n/**\n * Load skills from the project's .gerbil/skills/ directory\n *\n * This is the standard location for project-specific skills.\n * Creates the directory structure if it doesn't exist.\n *\n * @example\n * ```ts\n * // Load from .gerbil/skills/ in current working directory\n * const loaded = await loadProjectSkills();\n * console.log(`Loaded ${loaded.length} project skills`);\n * ```\n */\nexport async function loadProjectSkills(cwd: string = process.cwd()): Promise<string[]> {\n const path = await import(\"node:path\");\n const fs = await import(\"node:fs\");\n\n const gerbilDir = path.join(cwd, \".gerbil\");\n const skillsDir = path.join(gerbilDir, \"skills\");\n\n // If .gerbil/skills doesn't exist, that's fine - just return empty\n if (!fs.existsSync(skillsDir)) {\n return [];\n }\n\n return loadSkills(skillsDir);\n}\n\n// ============================================\n// Load from Directory\n// ============================================\n\n/**\n * Load all skills from a directory\n *\n * @example\n * ```ts\n * // Load all *.skill.ts and *.skill.js files\n * const loaded = await loadSkills(\"./skills\");\n * console.log(`Loaded ${loaded.length} skills`);\n * ```\n */\nexport async function loadSkills(dir: string, options: LoadSkillsOptions = {}): Promise<string[]> {\n const { patterns = [\"*.skill.ts\", \"*.skill.js\"] } = options;\n const loaded: string[] = [];\n\n try {\n const fs = await import(\"node:fs\");\n const path = await import(\"node:path\");\n\n const resolvedDir = path.resolve(dir);\n\n if (!fs.existsSync(resolvedDir)) {\n throw new Error(`Skills directory not found: ${dir}`);\n }\n\n const files = fs.readdirSync(resolvedDir);\n\n for (const file of files) {\n const matches = patterns.some((pattern) => {\n const regex = new RegExp(`^${pattern.replace(/\\*/g, \".*\").replace(/\\./g, \"\\\\.\")}$`);\n return regex.test(file);\n });\n\n if (matches) {\n const filePath = path.join(resolvedDir, file);\n const skill = await loadSkill(filePath);\n if (skill) {\n loaded.push(skill.definition.name);\n }\n }\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"MODULE_NOT_FOUND\") {\n throw new Error(`Skills directory not found: ${dir}`);\n }\n throw error;\n }\n\n return loaded;\n}\n\n// ============================================\n// Load Single Skill\n// ============================================\n\n/**\n * Load a single skill from a file\n *\n * @example\n * ```ts\n * const skill = await loadSkill(\"./skills/sentiment.skill.ts\");\n * if (skill) {\n * const result = await skill({ text: \"Hello\" });\n * }\n * ```\n */\nexport async function loadSkill(filePath: string): Promise<Skill | null> {\n try {\n const path = await import(\"node:path\");\n const resolvedPath = path.resolve(filePath);\n\n // Use dynamic import with file URL for ESM compatibility\n const fileUrl = pathToFileURL(resolvedPath).href;\n const module = await import(fileUrl);\n\n // Get the default export\n const skill = module.default as Skill;\n\n if (!skill?.definition) {\n return null;\n }\n\n // Register with source tracking\n registerSkillWithSource(skill, resolvedPath);\n\n return skill;\n } catch (_error) {\n return null;\n }\n}\n\n// ============================================\n// Load from Package\n// ============================================\n\n/**\n * Load skills from an npm package\n *\n * @example\n * ```ts\n * // Load skills from a package\n * const loaded = await loadSkillPackage(\"gerbil-skills-extra\");\n * ```\n */\nexport async function loadSkillPackage(packageName: string): Promise<string[]> {\n try {\n const module = await import(packageName);\n const loaded: string[] = [];\n\n // Look for exported skills\n for (const [_key, value] of Object.entries(module)) {\n if (isSkill(value)) {\n const skill = value as Skill;\n registerSkillWithSource(skill, `npm:${packageName}`);\n loaded.push(skill.definition.name);\n }\n }\n\n // Also check default export if it's an object of skills\n if (module.default && typeof module.default === \"object\") {\n for (const [_key, value] of Object.entries(module.default)) {\n if (isSkill(value)) {\n const skill = value as Skill;\n registerSkillWithSource(skill, `npm:${packageName}`);\n loaded.push(skill.definition.name);\n }\n }\n }\n\n return loaded;\n } catch (error) {\n throw new Error(`Failed to load skill package \"${packageName}\": ${error}`);\n }\n}\n\n// ============================================\n// Helpers\n// ============================================\n\n/**\n * Check if a value is a Skill\n */\nfunction isSkill(value: unknown): value is Skill {\n return (\n typeof value === \"function\" &&\n typeof (value as Skill).definition === \"object\" &&\n typeof (value as Skill).definition.name === \"string\" &&\n typeof (value as Skill).definition.run === \"function\"\n );\n}\n","/**\n * Analyze Screenshot Skill\n *\n * Analyze a UI screenshot for design, accessibility, or QA purposes.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst AnalyzeScreenshotInput = z.object({\n /** Screenshot URL or data URI */\n image: z.string(),\n\n /** Analysis type */\n type: z\n .enum([\"ui-review\", \"accessibility\", \"suggestions\", \"qa\", \"ux-audit\", \"mobile-check\"])\n .default(\"ui-review\"),\n\n /** Specific areas to focus on (optional) */\n focus: z.array(z.string()).optional(),\n});\n\nexport type AnalyzeScreenshotInput = z.infer<typeof AnalyzeScreenshotInput>;\n\nconst analysisPrompts: Record<string, string> = {\n \"ui-review\": `Review this UI screenshot as a design expert. Analyze:\n- Visual hierarchy and layout\n- Typography and readability\n- Color scheme and contrast\n- Spacing and alignment\n- Component consistency\n- Overall design quality\n\nProvide specific observations and suggestions.`,\n\n accessibility: `Analyze this screenshot for accessibility issues. Check for:\n- Color contrast ratios\n- Text size and readability\n- Touch target sizes\n- Visual affordances for interactive elements\n- Potential issues for users with visual impairments\n- Missing alt text indicators\n\nList specific issues found and how to fix them.`,\n\n suggestions: `As a senior UX designer, review this UI and suggest improvements:\n- Modern design patterns that could be applied\n- Usability improvements\n- Visual polish opportunities\n- User experience enhancements\n- Ways to make it more engaging\n\nBe specific and actionable.`,\n\n qa: `Perform QA analysis on this screenshot. Look for:\n- Visual bugs or glitches\n- Alignment and spacing issues\n- Broken or missing elements\n- Inconsistencies with design standards\n- Text overflow or truncation issues\n- Responsive design problems\n\nReport each issue with its location and severity.`,\n\n \"ux-audit\": `Conduct a UX audit of this interface:\n- User flow clarity\n- Call-to-action visibility\n- Information architecture\n- Cognitive load assessment\n- Error prevention\n- User feedback mechanisms\n\nProvide a structured audit report.`,\n\n \"mobile-check\": `Evaluate this UI for mobile usability:\n- Touch target sizes (minimum 44x44px)\n- Thumb-zone accessibility\n- Content priority on small screens\n- Gesture affordances\n- Loading indicators\n- Mobile-specific patterns\n\nIdentify mobile-specific issues and recommendations.`,\n};\n\nexport const analyzeScreenshot = defineSkill({\n name: \"analyze-screenshot\",\n description: \"Analyze a UI screenshot for design, accessibility, or QA\",\n version: \"1.0.0\",\n model: \"ministral-3b\",\n input: AnalyzeScreenshotInput,\n maxTokens: 1200,\n temperature: 0.3,\n\n async run({ input, gerbil }) {\n const { image, type = \"ui-review\", focus } = input;\n\n if (!gerbil.supportsVision()) {\n throw new Error(\n `Current model doesn't support vision. Load a vision model like \"ministral-3b\" first.`,\n );\n }\n\n let prompt = analysisPrompts[type];\n\n if (focus && focus.length > 0) {\n prompt += `\\n\\nPay special attention to: ${focus.join(\", \")}.`;\n }\n\n const result = await gerbil.generate(prompt, {\n images: [{ source: image }],\n maxTokens: this.maxTokens,\n temperature: this.temperature,\n });\n\n return result.text;\n },\n});\n","/**\n * Announce Skill\n *\n * Generate and speak an announcement using AI.\n * Useful for scripts that need to give voice updates.\n */\n\nimport { execSync } from \"child_process\";\nimport { unlinkSync, writeFileSync } from \"fs\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst AnnounceInput = z.object({\n /** What to announce (will be rephrased by AI) */\n message: z.string(),\n\n /** Announcement style */\n style: z.enum([\"casual\", \"formal\", \"excited\", \"calm\", \"urgent\"]).default(\"casual\"),\n\n /** Voice ID */\n voice: z\n .enum([\"af_heart\", \"af_bella\", \"af_nicole\", \"am_fenrir\", \"am_michael\", \"bf_emma\", \"bm_george\"])\n .default(\"af_heart\"),\n\n /** Speech speed */\n speed: z.number().min(0.5).max(2.0).default(1.0),\n\n /** Just generate text, don't speak */\n textOnly: z.boolean().default(false),\n});\n\nexport type AnnounceInput = z.infer<typeof AnnounceInput>;\n\nconst stylePrompts: Record<string, string> = {\n casual: \"Rephrase this as a casual, friendly announcement. Keep it natural and conversational.\",\n formal: \"Rephrase this as a professional, formal announcement. Be clear and dignified.\",\n excited: \"Rephrase this as an excited, enthusiastic announcement! Add energy and positivity.\",\n calm: \"Rephrase this as a calm, soothing announcement. Be gentle and reassuring.\",\n urgent: \"Rephrase this as an urgent announcement. Be direct and emphasize importance.\",\n};\n\nfunction saveWav(filename: string, audio: Float32Array, sampleRate: number): void {\n const buffer = Buffer.alloc(44 + audio.length * 2);\n buffer.write(\"RIFF\", 0);\n buffer.writeUInt32LE(36 + audio.length * 2, 4);\n buffer.write(\"WAVE\", 8);\n buffer.write(\"fmt \", 12);\n buffer.writeUInt32LE(16, 16);\n buffer.writeUInt16LE(1, 20);\n buffer.writeUInt16LE(1, 22);\n buffer.writeUInt32LE(sampleRate, 24);\n buffer.writeUInt32LE(sampleRate * 2, 28);\n buffer.writeUInt16LE(2, 32);\n buffer.writeUInt16LE(16, 34);\n buffer.write(\"data\", 36);\n buffer.writeUInt32LE(audio.length * 2, 40);\n\n for (let i = 0; i < audio.length; i++) {\n const s = Math.max(-1, Math.min(1, audio[i]));\n buffer.writeInt16LE(Math.round(s * 32767), 44 + i * 2);\n }\n\n writeFileSync(filename, buffer);\n}\n\nexport const announce = defineSkill({\n name: \"announce\",\n description: \"Generate and speak an AI-crafted announcement\",\n version: \"1.0.0\",\n input: AnnounceInput,\n temperature: 0.7,\n maxTokens: 150,\n\n async run({ input, gerbil }) {\n const { message, style = \"casual\", voice = \"af_heart\", speed = 1.0, textOnly = false } = input;\n\n // Generate announcement text using AI\n const result = await gerbil.generate(message, {\n system: `${stylePrompts[style]}\nKeep it brief (1-2 sentences max). \nOutput only the announcement text, nothing else.`,\n temperature: this.temperature,\n maxTokens: this.maxTokens,\n });\n\n const announcementText = result.text.trim();\n\n if (textOnly) {\n return announcementText;\n }\n\n // Speak the announcement\n const speechResult = await gerbil.speak(announcementText, { voice, speed });\n\n // Play audio\n const tempFile = join(tmpdir(), `gerbil-announce-${Date.now()}.wav`);\n saveWav(tempFile, speechResult.audio, speechResult.sampleRate);\n\n try {\n const platform = process.platform;\n if (platform === \"darwin\") {\n execSync(`afplay \"${tempFile}\"`, { stdio: \"inherit\" });\n } else if (platform === \"linux\") {\n try {\n execSync(`aplay \"${tempFile}\"`, { stdio: \"inherit\" });\n } catch {\n execSync(`paplay \"${tempFile}\"`, { stdio: \"inherit\" });\n }\n } else if (platform === \"win32\") {\n execSync(`powershell -c \"(New-Object Media.SoundPlayer '${tempFile}').PlaySync()\"`, {\n stdio: \"inherit\",\n });\n }\n } finally {\n try {\n unlinkSync(tempFile);\n } catch {\n // Ignore\n }\n }\n\n return announcementText;\n },\n});\n","/**\n * Caption Image Skill\n *\n * Generate captions, alt text, or social media descriptions for images.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst CaptionImageInput = z.object({\n /** Image URL or data URI */\n image: z.string(),\n\n /** Caption type */\n type: z\n .enum([\"alt-text\", \"caption\", \"social-media\", \"seo\", \"artistic\", \"technical\"])\n .default(\"caption\"),\n\n /** Tone/style for the caption */\n tone: z\n .enum([\"professional\", \"casual\", \"playful\", \"formal\", \"descriptive\"])\n .default(\"professional\"),\n\n /** Maximum length hint */\n maxLength: z.enum([\"short\", \"medium\", \"long\"]).default(\"medium\"),\n\n /** Platform hint for social media captions */\n platform: z.enum([\"twitter\", \"instagram\", \"linkedin\", \"facebook\"]).optional(),\n});\n\nexport type CaptionImageInput = z.infer<typeof CaptionImageInput>;\n\nconst captionPrompts: Record<string, string> = {\n \"alt-text\": `Generate accessible alt text for this image.\n- Describe the essential content concisely\n- Include relevant details for screen reader users\n- Avoid starting with \"Image of\" or \"Picture of\"\n- Be factual and objective`,\n\n caption: `Write a caption for this image.\n- Capture the essence of the image\n- Make it engaging and informative\n- Consider the context and mood`,\n\n \"social-media\": `Create a social media caption for this image.\n- Make it engaging and shareable\n- Include relevant emoji where appropriate\n- Suggest hashtags if applicable\n- Optimize for engagement`,\n\n seo: `Write SEO-optimized alt text and description for this image.\n- Include relevant keywords naturally\n- Be descriptive but concise\n- Focus on searchable terms\n- Maintain readability`,\n\n artistic: `Write an artistic, evocative caption for this image.\n- Capture the mood and emotion\n- Use creative language\n- Tell a micro-story if appropriate\n- Be memorable and unique`,\n\n technical: `Write a technical description of this image.\n- Describe components and elements precisely\n- Use appropriate technical terminology\n- Note measurements or specifications if visible\n- Be objective and detailed`,\n};\n\nconst lengthGuides: Record<string, number> = {\n short: 100,\n medium: 200,\n long: 400,\n};\n\nconst platformGuides: Record<string, string> = {\n twitter: \"Keep under 280 characters. Make it punchy and tweetable.\",\n instagram: \"Include relevant hashtags. Can be longer and more storytelling.\",\n linkedin: \"Keep it professional. Focus on value and insights.\",\n facebook: \"Can be conversational. Ask questions to encourage engagement.\",\n};\n\nexport const captionImage = defineSkill({\n name: \"caption-image\",\n description: \"Generate captions, alt text, or social media descriptions for images\",\n version: \"1.0.0\",\n model: \"ministral-3b\",\n input: CaptionImageInput,\n temperature: 0.7,\n\n async run({ input, gerbil }) {\n const {\n image,\n type = \"caption\",\n tone = \"professional\",\n maxLength = \"medium\",\n platform,\n } = input;\n\n if (!gerbil.supportsVision()) {\n throw new Error(\n `Current model doesn't support vision. Load a vision model like \"ministral-3b\" first.`,\n );\n }\n\n let prompt = captionPrompts[type];\n prompt += `\\n\\nTone: ${tone}`;\n\n if (platform && type === \"social-media\") {\n prompt += `\\n\\nPlatform: ${platform}. ${platformGuides[platform]}`;\n }\n\n const result = await gerbil.generate(prompt, {\n images: [{ source: image }],\n maxTokens: lengthGuides[maxLength],\n temperature: this.temperature,\n });\n\n return result.text;\n },\n});\n","/**\n * Commit Skill\n *\n * Generate git commit messages from staged changes.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst CommitInput = z.object({\n /** Git diff (auto-detected if not provided) */\n diff: z.string().optional(),\n\n /** Commit style */\n type: z.enum([\"conventional\", \"simple\", \"detailed\"]).default(\"conventional\"),\n\n /** Max length for commit message */\n maxLength: z.number().default(72),\n});\n\nexport type CommitInput = z.infer<typeof CommitInput>;\n\nasync function getGitDiff(): Promise<string> {\n try {\n const { execSync } = await import(\"node:child_process\");\n return execSync(\"git diff --staged\", { encoding: \"utf-8\" });\n } catch {\n return \"\";\n }\n}\n\nfunction getSystemPrompt(type: CommitInput[\"type\"]): string {\n switch (type) {\n case \"conventional\":\n return `Generate a git commit message following the Conventional Commits format.\nUse one of these types: feat, fix, docs, style, refactor, perf, test, chore.\nFormat: type(scope): description\nKeep it under 72 characters.\nNo period at the end.\nOnly output the commit message, nothing else.`;\n\n case \"detailed\":\n return `Generate a detailed git commit message with a subject line and body.\nSubject: under 72 chars, imperative mood\nBody: explain what and why\nOnly output the commit message, nothing else.`;\n\n default:\n return `Generate a concise git commit message.\nUse imperative mood (e.g., \"Add\", \"Fix\", \"Update\").\nKeep it under 72 characters.\nNo period at the end.\nOnly output the commit message, nothing else.`;\n }\n}\n\nexport const commit = defineSkill({\n name: \"commit\",\n description: \"Generate a git commit message from staged changes\",\n version: \"1.0.0\",\n input: CommitInput,\n temperature: 0.3,\n maxTokens: 100,\n\n async run({ input, gerbil }) {\n const { diff, type = \"conventional\", maxLength = 72 } = input;\n\n // Get diff from git if not provided\n const actualDiff = diff || (await getGitDiff());\n\n if (!actualDiff || actualDiff.trim().length === 0) {\n throw new Error(\"No changes staged. Run `git add` first.\");\n }\n\n const result = await gerbil.generate(\n `Generate a commit message for the following diff:\\n\\n${actualDiff}`,\n {\n system: getSystemPrompt(type),\n maxTokens: this.maxTokens,\n temperature: this.temperature,\n },\n );\n\n // Clean and truncate\n let message = result.text.trim();\n message = message.replace(/<think>[\\s\\S]*?<\\/think>/g, \"\").trim(); // Remove think tags\n message = message.replace(/<\\/?think>/g, \"\").trim(); // Remove unclosed tags\n message = message.replace(/^[\"']|[\"']$/g, \"\"); // Remove quotes\n message = message.split(\"\\n\")[0]; // First line only\n if (message.length > maxLength) {\n message = `${message.substring(0, maxLength - 3)}...`;\n }\n\n return message;\n },\n});\n","/**\n * Compare Images Skill\n *\n * Compare two images and describe their differences.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst CompareImagesInput = z.object({\n /** First image URL or data URI */\n image1: z.string(),\n\n /** Second image URL or data URI */\n image2: z.string(),\n\n /** Comparison type */\n type: z\n .enum([\"visual-diff\", \"design-comparison\", \"before-after\", \"a-b-test\", \"version-diff\"])\n .default(\"visual-diff\"),\n\n /** Specific aspects to compare */\n focus: z.array(z.string()).optional(),\n});\n\nexport type CompareImagesInput = z.infer<typeof CompareImagesInput>;\n\nconst comparisonPrompts: Record<string, string> = {\n \"visual-diff\": `Compare these two images and identify all visual differences.\n- List specific elements that differ\n- Note position, color, size, and content changes\n- Identify additions and removals\n- Rate the significance of each change`,\n\n \"design-comparison\": `Compare these two designs as a design expert.\n- Analyze layout differences\n- Compare typography and colors\n- Evaluate which design is more effective\n- Suggest which elements work better in each`,\n\n \"before-after\": `Analyze these before and after images.\n- Describe what changed\n- Evaluate if the changes are improvements\n- Note any unintended consequences\n- Summarize the overall transformation`,\n\n \"a-b-test\": `Analyze these two variants for A/B testing purposes.\n- Identify the key differences being tested\n- Predict which might perform better and why\n- Note potential user experience impacts\n- Suggest what metrics to measure`,\n\n \"version-diff\": `Compare these two versions of the UI.\n- Document all changes between versions\n- Categorize changes (bug fix, feature, style)\n- Identify breaking changes\n- Note any regressions`,\n};\n\nexport const compareImages = defineSkill({\n name: \"compare-images\",\n description: \"Compare two images and describe their differences\",\n version: \"1.0.0\",\n model: \"ministral-3b\",\n input: CompareImagesInput,\n maxTokens: 1500,\n temperature: 0.3,\n\n async run({ input, gerbil }) {\n const { image1, image2, type = \"visual-diff\", focus } = input;\n\n if (!gerbil.supportsVision()) {\n throw new Error(\n `Current model doesn't support vision. Load a vision model like \"ministral-3b\" first.`,\n );\n }\n\n let prompt = `I'm showing you two images for comparison.\\n\\n${comparisonPrompts[type]}`;\n\n if (focus && focus.length > 0) {\n prompt += `\\n\\nFocus specifically on: ${focus.join(\", \")}.`;\n }\n\n prompt += \"\\n\\nThe first image is shown first, followed by the second image.\";\n\n const result = await gerbil.generate(prompt, {\n images: [{ source: image1 }, { source: image2 }],\n maxTokens: this.maxTokens,\n temperature: this.temperature,\n });\n\n return result.text;\n },\n});\n","/**\n * Describe Image Skill\n *\n * Describe an image using vision AI.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst DescribeImageInput = z.object({\n /** Image URL or data URI */\n image: z.string(),\n\n /** What to focus on */\n focus: z.enum([\"general\", \"details\", \"text\", \"objects\", \"scene\", \"colors\"]).default(\"general\"),\n\n /** Output format */\n format: z.enum([\"paragraph\", \"bullets\", \"structured\"]).default(\"paragraph\"),\n\n /** Maximum detail level */\n detail: z.enum([\"brief\", \"normal\", \"comprehensive\"]).default(\"normal\"),\n});\n\nexport type DescribeImageInput = z.infer<typeof DescribeImageInput>;\n\nconst focusPrompts: Record<string, string> = {\n general:\n \"Describe this image comprehensively, covering the main subject, context, and notable elements.\",\n details:\n \"Describe this image in detail, including colors, textures, lighting, and small elements.\",\n text: \"Extract and describe any text visible in this image. Include the text content and its context.\",\n objects:\n \"List and describe all objects you can identify in this image, including their positions.\",\n scene: \"Describe the scene, setting, and atmosphere of this image. What story does it tell?\",\n colors: \"Analyze the color palette and visual composition of this image.\",\n};\n\nconst detailLengths: Record<string, number> = {\n brief: 150,\n normal: 400,\n comprehensive: 800,\n};\n\nexport const describeImage = defineSkill({\n name: \"describe-image\",\n description: \"Describe an image using vision AI\",\n version: \"1.0.0\",\n model: \"ministral-3b\",\n input: DescribeImageInput,\n temperature: 0.5,\n\n async run({ input, gerbil }) {\n const { image, focus = \"general\", format = \"paragraph\", detail = \"normal\" } = input;\n\n // Check if model supports vision\n if (!gerbil.supportsVision()) {\n throw new Error(\n `Current model doesn't support vision. Load a vision model like \"ministral-3b\" first.`,\n );\n }\n\n const formatInstructions: Record<string, string> = {\n paragraph: \"Write your description as natural flowing paragraphs.\",\n bullets: \"Format your description as clear bullet points.\",\n structured:\n \"Structure your description with sections: Overview, Main Elements, Details, Observations.\",\n };\n\n const prompt = `${focusPrompts[focus]}\\n\\n${formatInstructions[format]}`;\n\n const result = await gerbil.generate(prompt, {\n images: [{ source: image }],\n maxTokens: detailLengths[detail],\n temperature: this.temperature,\n });\n\n return result.text;\n },\n});\n","/**\n * Explain Skill\n *\n * Explain code or concepts at various levels.\n * Optionally speaks the explanation aloud.\n */\n\nimport { execSync } from \"node:child_process\";\nimport { unlinkSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst ExplainInput = z.object({\n /** Content to explain */\n content: z.string(),\n\n /** Explanation level */\n level: z.enum([\"beginner\", \"intermediate\", \"expert\"]).default(\"intermediate\"),\n\n /** Programming language (for code) */\n language: z.string().optional(),\n\n /** Speak the explanation aloud using TTS */\n speak: z.boolean().default(false),\n\n /** Voice for TTS (if speak is true) */\n voice: z.enum([\"af_heart\", \"af_bella\", \"bf_emma\", \"am_fenrir\"]).default(\"af_heart\"),\n});\n\nexport type ExplainInput = z.infer<typeof ExplainInput>;\n\nconst levelGuide = {\n beginner: \"Explain like I'm new to programming. Use simple terms and analogies.\",\n intermediate: \"Explain for someone with programming experience.\",\n expert: \"Explain with technical depth, including implementation details.\",\n};\n\nfunction saveWav(filename: string, audio: Float32Array, sampleRate: number): void {\n const buffer = Buffer.alloc(44 + audio.length * 2);\n buffer.write(\"RIFF\", 0);\n buffer.writeUInt32LE(36 + audio.length * 2, 4);\n buffer.write(\"WAVE\", 8);\n buffer.write(\"fmt \", 12);\n buffer.writeUInt32LE(16, 16);\n buffer.writeUInt16LE(1, 20);\n buffer.writeUInt16LE(1, 22);\n buffer.writeUInt32LE(sampleRate, 24);\n buffer.writeUInt32LE(sampleRate * 2, 28);\n buffer.writeUInt16LE(2, 32);\n buffer.writeUInt16LE(16, 34);\n buffer.write(\"data\", 36);\n buffer.writeUInt32LE(audio.length * 2, 40);\n\n for (let i = 0; i < audio.length; i++) {\n const s = Math.max(-1, Math.min(1, audio[i]));\n buffer.writeInt16LE(Math.round(s * 32767), 44 + i * 2);\n }\n\n writeFileSync(filename, buffer);\n}\n\nfunction playAudio(audioFile: string): void {\n const platform = process.platform;\n if (platform === \"darwin\") {\n execSync(`afplay \"${audioFile}\"`, { stdio: \"inherit\" });\n } else if (platform === \"linux\") {\n try {\n execSync(`aplay \"${audioFile}\"`, { stdio: \"inherit\" });\n } catch {\n execSync(`paplay \"${audioFile}\"`, { stdio: \"inherit\" });\n }\n } else if (platform === \"win32\") {\n execSync(`powershell -c \"(New-Object Media.SoundPlayer '${audioFile}').PlaySync()\"`, {\n stdio: \"inherit\",\n });\n }\n}\n\nexport const explain = defineSkill({\n name: \"explain\",\n description: \"Explain code or concepts at various levels (optionally speak aloud)\",\n version: \"1.0.0\",\n input: ExplainInput,\n temperature: 0.5,\n maxTokens: 500,\n\n async run({ input, gerbil }) {\n const { content, level = \"intermediate\", language, speak = false, voice = \"af_heart\" } = input;\n\n let systemPrompt = `You are a patient teacher.\n${levelGuide[level]}`;\n\n if (language) {\n systemPrompt += `\\nThis is ${language} code.`;\n }\n\n systemPrompt += \"\\nBe clear and concise.\";\n\n const result = await gerbil.generate(`Explain this:\\n\\n${content}`, {\n system: systemPrompt,\n maxTokens: this.maxTokens,\n temperature: this.temperature,\n });\n\n const explanation = result.text;\n\n // Optionally speak the explanation\n if (speak) {\n // Split into sentences for smoother playback\n const sentences = explanation.split(/(?<=[.!?])\\s+/).filter((s) => s.trim());\n\n for (const sentence of sentences) {\n if (!sentence.trim()) continue;\n\n const speechResult = await gerbil.speak(sentence, { voice });\n const tempFile = join(tmpdir(), `gerbil-explain-${Date.now()}.wav`);\n saveWav(tempFile, speechResult.audio, speechResult.sampleRate);\n\n try {\n playAudio(tempFile);\n } finally {\n try {\n unlinkSync(tempFile);\n } catch {\n // Ignore\n }\n }\n }\n }\n\n return explanation;\n },\n});\n","/**\n * Extract Skill\n *\n * Extract structured data from content.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst ExtractInput = z.object({\n /** Content to extract from */\n content: z.string(),\n\n /** Zod schema for extraction (passed at runtime) */\n schema: z.any(),\n\n /** Additional context */\n context: z.string().optional(),\n});\n\nexport type ExtractInput = z.infer<typeof ExtractInput>;\n\nexport const extract = defineSkill({\n name: \"extract\",\n description: \"Extract structured data from content\",\n version: \"1.0.0\",\n input: ExtractInput,\n temperature: 0.3,\n\n async run({ input, gerbil }) {\n const { content, schema, context } = input;\n\n let prompt = \"Extract structured data from the following content.\";\n\n if (context) {\n prompt += `\\nContext: ${context}`;\n }\n\n prompt += `\\n\\nContent:\\n${content}`;\n\n return gerbil.json(prompt, { schema });\n },\n});\n","/**\n * Extract from Image Skill\n *\n * Extract text, code, data, or structured information from images.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst ExtractFromImageInput = z.object({\n /** Image URL or data URI */\n image: z.string(),\n\n /** What to extract */\n extract: z.enum([\"text\", \"code\", \"data\", \"table\", \"diagram\", \"form\", \"receipt\"]).default(\"text\"),\n\n /** Output format */\n outputFormat: z.enum([\"raw\", \"json\", \"markdown\", \"csv\"]).default(\"raw\"),\n\n /** Language hint for code extraction */\n language: z.string().optional(),\n});\n\nexport type ExtractFromImageInput = z.infer<typeof ExtractFromImageInput>;\n\nconst extractionPrompts: Record<string, string> = {\n text: `Extract all visible text from this image.\n- Preserve the original formatting and structure as much as possible\n- Include headings, paragraphs, and any labels\n- Note any text that's unclear or partially visible\n- Maintain the reading order`,\n\n code: `Extract the code visible in this image.\n- Preserve exact syntax, indentation, and formatting\n- Include comments if visible\n- Note any parts that are unclear\n- Format as a proper code block`,\n\n data: `Extract all data, numbers, and structured information from this image.\n- Include labels and their associated values\n- Preserve numerical precision\n- Note units and currencies\n- Identify any patterns or relationships`,\n\n table: `Extract the table data from this image.\n- Identify all columns and rows\n- Preserve cell alignment where meaningful\n- Handle merged cells appropriately\n- Include headers and any totals`,\n\n diagram: `Describe and extract information from this diagram or flowchart.\n- List all nodes/boxes and their labels\n- Describe connections and their directions\n- Note any annotations or legends\n- Explain the flow or relationships`,\n\n form: `Extract form fields and their values from this image.\n- List each field label and its value\n- Note required fields if indicated\n- Include dropdown selections\n- Preserve the form structure`,\n\n receipt: `Extract receipt/invoice information from this image.\n- Vendor/store name\n- Date and time\n- Line items with quantities and prices\n- Subtotals, taxes, and total\n- Payment method if shown`,\n};\n\nconst formatInstructions: Record<string, string> = {\n raw: \"Output the extracted content as plain text.\",\n json: \"Output the extracted content as valid JSON with appropriate structure.\",\n markdown: \"Format the output as Markdown with proper headings and formatting.\",\n csv: \"Output tabular data in CSV format with proper quoting.\",\n};\n\nexport const extractFromImage = defineSkill({\n name: \"extract-from-image\",\n description: \"Extract text, code, tables, or data from images\",\n version: \"1.0.0\",\n model: \"ministral-3b\",\n input: ExtractFromImageInput,\n maxTokens: 2000,\n temperature: 0.1, // Low temp for accuracy\n\n async run({ input, gerbil }) {\n const { image, extract = \"text\", outputFormat = \"raw\", language } = input;\n\n if (!gerbil.supportsVision()) {\n throw new Error(\n `Current model doesn't support vision. Load a vision model like \"ministral-3b\" first.`,\n );\n }\n\n let prompt = extractionPrompts[extract];\n\n if (language && extract === \"code\") {\n prompt += `\\n\\nThe code appears to be ${language}.`;\n }\n\n prompt += `\\n\\n${formatInstructions[outputFormat]}`;\n\n const result = await gerbil.generate(prompt, {\n images: [{ source: image }],\n maxTokens: this.maxTokens,\n temperature: this.temperature,\n });\n\n return result.text;\n },\n});\n","/**\n * Read Aloud Skill\n *\n * Read text or file content aloud using TTS.\n * Supports streaming for long content.\n */\n\nimport { execSync } from \"child_process\";\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from \"fs\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst ReadAloudInput = z.object({\n /** File path to read, or text content if no file */\n content: z.string(),\n\n /** Treat content as file path (default: auto-detect) */\n isFile: z.boolean().optional(),\n\n /** Voice ID */\n voice: z\n .enum([\"af_heart\", \"af_bella\", \"af_nicole\", \"am_fenrir\", \"am_michael\", \"bf_emma\", \"bm_george\"])\n .default(\"af_heart\"),\n\n /** Speech speed */\n speed: z.number().min(0.5).max(2.0).default(1.0),\n\n /** Maximum characters to read (default: 5000) */\n maxLength: z.number().default(5000),\n\n /** Summarize if content exceeds maxLength instead of truncating */\n summarizeIfLong: z.boolean().default(false),\n});\n\nexport type ReadAloudInput = z.infer<typeof ReadAloudInput>;\n\nfunction saveWav(filename: string, audio: Float32Array, sampleRate: number): void {\n const buffer = Buffer.alloc(44 + audio.length * 2);\n buffer.write(\"RIFF\", 0);\n buffer.writeUInt32LE(36 + audio.length * 2, 4);\n buffer.write(\"WAVE\", 8);\n buffer.write(\"fmt \", 12);\n buffer.writeUInt32LE(16, 16);\n buffer.writeUInt16LE(1, 20);\n buffer.writeUInt16LE(1, 22);\n buffer.writeUInt32LE(sampleRate, 24);\n buffer.writeUInt32LE(sampleRate * 2, 28);\n buffer.writeUInt16LE(2, 32);\n buffer.writeUInt16LE(16, 34);\n buffer.write(\"data\", 36);\n buffer.writeUInt32LE(audio.length * 2, 40);\n\n for (let i = 0; i < audio.length; i++) {\n const s = Math.max(-1, Math.min(1, audio[i]));\n buffer.writeInt16LE(Math.round(s * 32767), 44 + i * 2);\n }\n\n writeFileSync(filename, buffer);\n}\n\nfunction playAudio(audioFile: string): void {\n const platform = process.platform;\n if (platform === \"darwin\") {\n execSync(`afplay \"${audioFile}\"`, { stdio: \"inherit\" });\n } else if (platform === \"linux\") {\n try {\n execSync(`aplay \"${audioFile}\"`, { stdio: \"inherit\" });\n } catch {\n execSync(`paplay \"${audioFile}\"`, { stdio: \"inherit\" });\n }\n } else if (platform === \"win32\") {\n execSync(`powershell -c \"(New-Object Media.SoundPlayer '${audioFile}').PlaySync()\"`, {\n stdio: \"inherit\",\n });\n }\n}\n\nexport const readAloud = defineSkill({\n name: \"read-aloud\",\n description: \"Read text or file content aloud using TTS\",\n version: \"1.0.0\",\n input: ReadAloudInput,\n\n async run({ input, gerbil }) {\n const {\n content,\n isFile,\n voice = \"af_heart\",\n speed = 1.0,\n maxLength = 5000,\n summarizeIfLong = false,\n } = input;\n\n // Determine if content is a file path\n const shouldReadFile = isFile ?? existsSync(content);\n\n let textToRead: string;\n\n if (shouldReadFile) {\n if (!existsSync(content)) {\n throw new Error(`File not found: ${content}`);\n }\n textToRead = readFileSync(content, \"utf-8\");\n } else {\n textToRead = content;\n }\n\n // Handle long content\n if (textToRead.length > maxLength) {\n if (summarizeIfLong) {\n // Use AI to summarize\n const summary = await gerbil.generate(\n `Summarize this content in a way that's good for reading aloud (2-3 paragraphs max):\\n\\n${textToRead.slice(0, 10000)}`,\n {\n system: \"You are a skilled narrator. Create a clear, spoken-word friendly summary.\",\n maxTokens: 500,\n temperature: 0.3,\n },\n );\n textToRead = summary.text;\n } else {\n // Truncate\n textToRead = textToRead.slice(0, maxLength) + \"...\";\n }\n }\n\n // Generate and play speech using streaming for long content\n const sentences = textToRead.split(/(?<=[.!?])\\s+/).filter((s) => s.trim());\n let totalDuration = 0;\n\n for (const sentence of sentences) {\n if (!sentence.trim()) continue;\n\n const result = await gerbil.speak(sentence, { voice, speed });\n totalDuration += result.duration;\n\n // Play this chunk\n const tempFile = join(tmpdir(), `gerbil-read-${Date.now()}.wav`);\n saveWav(tempFile, result.audio, result.sampleRate);\n\n try {\n playAudio(tempFile);\n } finally {\n try {\n unlinkSync(tempFile);\n } catch {\n // Ignore\n }\n }\n }\n\n const charCount = textToRead.length;\n const source = shouldReadFile ? `file \"${content}\"` : \"text\";\n\n return `Read ${charCount} characters from ${source} (${totalDuration.toFixed(1)}s audio)`;\n },\n});\n","/**\n * Review Skill\n *\n * Code review with configurable focus areas.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst ReviewInput = z.object({\n /** Code to review */\n code: z.string(),\n\n /** Focus areas */\n focus: z.array(z.enum([\"security\", \"performance\", \"style\", \"bugs\", \"all\"])).default([\"all\"]),\n\n /** Output format */\n format: z.enum([\"inline\", \"summary\", \"detailed\"]).default(\"summary\"),\n});\n\nexport type ReviewInput = z.infer<typeof ReviewInput>;\n\nexport const review = defineSkill({\n name: \"review\",\n description: \"Code review with configurable focus areas\",\n version: \"1.0.0\",\n input: ReviewInput,\n temperature: 0.3,\n maxTokens: 600,\n\n async run({ input, gerbil }) {\n const { code, focus = [\"all\"], format = \"summary\" } = input;\n\n let systemPrompt = `You are a senior code reviewer.\nReview the code for:`;\n\n if (focus.includes(\"all\")) {\n systemPrompt +=\n \"\\n- Security vulnerabilities\\n- Performance issues\\n- Code style and readability\\n- Potential bugs\";\n } else {\n if (focus.includes(\"security\")) {\n systemPrompt += \"\\n- Security vulnerabilities\";\n }\n if (focus.includes(\"performance\")) {\n systemPrompt += \"\\n- Performance issues\";\n }\n if (focus.includes(\"style\")) {\n systemPrompt += \"\\n- Code style and readability\";\n }\n if (focus.includes(\"bugs\")) {\n systemPrompt += \"\\n- Potential bugs\";\n }\n }\n\n if (format === \"summary\") {\n systemPrompt += \"\\n\\nProvide a brief summary of issues found.\";\n } else if (format === \"detailed\") {\n systemPrompt += \"\\n\\nProvide detailed feedback with suggestions.\";\n } else {\n systemPrompt += \"\\n\\nProvide inline-style comments.\";\n }\n\n const result = await gerbil.generate(`Review this code:\\n\\n${code}`, {\n system: systemPrompt,\n maxTokens: this.maxTokens,\n temperature: this.temperature,\n });\n\n return result.text;\n },\n});\n","/**\n * Speak Skill\n *\n * Convert text to speech using on-device TTS.\n */\n\nimport { execSync } from \"child_process\";\nimport { unlinkSync, writeFileSync } from \"fs\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst SpeakInput = z.object({\n /** Text to speak */\n text: z.string(),\n\n /** Voice ID (default: af_heart) */\n voice: z\n .enum([\n \"af_heart\",\n \"af_bella\",\n \"af_nicole\",\n \"af_sarah\",\n \"am_fenrir\",\n \"am_michael\",\n \"bf_emma\",\n \"bf_isabella\",\n \"bm_george\",\n \"bm_lewis\",\n ])\n .default(\"af_heart\"),\n\n /** Speech speed 0.5-2.0 (default: 1.0) */\n speed: z.number().min(0.5).max(2.0).default(1.0),\n\n /** Save to file instead of playing */\n output: z.string().optional(),\n});\n\nexport type SpeakInput = z.infer<typeof SpeakInput>;\n\n/**\n * Save Float32Array as WAV file\n */\nfunction saveWav(filename: string, audio: Float32Array, sampleRate: number): void {\n const buffer = Buffer.alloc(44 + audio.length * 2);\n\n // WAV header\n buffer.write(\"RIFF\", 0);\n buffer.writeUInt32LE(36 + audio.length * 2, 4);\n buffer.write(\"WAVE\", 8);\n buffer.write(\"fmt \", 12);\n buffer.writeUInt32LE(16, 16);\n buffer.writeUInt16LE(1, 20);\n buffer.writeUInt16LE(1, 22);\n buffer.writeUInt32LE(sampleRate, 24);\n buffer.writeUInt32LE(sampleRate * 2, 28);\n buffer.writeUInt16LE(2, 32);\n buffer.writeUInt16LE(16, 34);\n buffer.write(\"data\", 36);\n buffer.writeUInt32LE(audio.length * 2, 40);\n\n for (let i = 0; i < audio.length; i++) {\n const s = Math.max(-1, Math.min(1, audio[i]));\n buffer.writeInt16LE(Math.round(s * 32767), 44 + i * 2);\n }\n\n writeFileSync(filename, buffer);\n}\n\nexport const speak = defineSkill({\n name: \"speak\",\n description: \"Convert text to speech using on-device TTS (Kokoro-82M)\",\n version: \"1.0.0\",\n input: SpeakInput,\n\n async run({ input, gerbil }) {\n const { text, voice = \"af_heart\", speed = 1.0, output } = input;\n\n // Generate speech\n const result = await gerbil.speak(text, { voice, speed });\n\n if (output) {\n // Save to specified file\n saveWav(output, result.audio, result.sampleRate);\n return `Saved ${result.duration.toFixed(1)}s of audio to ${output}`;\n }\n\n // Play audio using system player\n const tempFile = join(tmpdir(), `gerbil-speak-${Date.now()}.wav`);\n saveWav(tempFile, result.audio, result.sampleRate);\n\n try {\n // Detect platform and use appropriate player\n const platform = process.platform;\n if (platform === \"darwin\") {\n execSync(`afplay \"${tempFile}\"`, { stdio: \"inherit\" });\n } else if (platform === \"linux\") {\n // Try common Linux audio players\n try {\n execSync(`aplay \"${tempFile}\"`, { stdio: \"inherit\" });\n } catch {\n try {\n execSync(`paplay \"${tempFile}\"`, { stdio: \"inherit\" });\n } catch {\n execSync(`play \"${tempFile}\"`, { stdio: \"inherit\" });\n }\n }\n } else if (platform === \"win32\") {\n execSync(`powershell -c \"(New-Object Media.SoundPlayer '${tempFile}').PlaySync()\"`, {\n stdio: \"inherit\",\n });\n }\n } finally {\n // Clean up temp file\n try {\n unlinkSync(tempFile);\n } catch {\n // Ignore cleanup errors\n }\n }\n\n return `Spoke ${result.duration.toFixed(1)}s of audio (voice: ${voice}, speed: ${speed}x)`;\n },\n});\n","/**\n * Summarize Skill\n *\n * Summarize content in various lengths and formats.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst SummarizeInput = z.object({\n /** Content to summarize */\n content: z.string(),\n\n /** Summary length */\n length: z.enum([\"short\", \"medium\", \"long\"]).default(\"medium\"),\n\n /** Output format */\n format: z.enum([\"paragraph\", \"bullets\"]).default(\"paragraph\"),\n\n /** Focus areas */\n focus: z.array(z.string()).optional(),\n});\n\nexport type SummarizeInput = z.infer<typeof SummarizeInput>;\n\nconst lengthGuide = {\n short: \"2-3 sentences\",\n medium: \"1 paragraph (4-6 sentences)\",\n long: \"2-3 paragraphs\",\n};\n\nconst maxTokensGuide = {\n short: 100,\n medium: 200,\n long: 400,\n};\n\nexport const summarize = defineSkill({\n name: \"summarize\",\n description: \"Summarize content in various lengths and formats\",\n version: \"1.0.0\",\n input: SummarizeInput,\n temperature: 0.3,\n\n async run({ input, gerbil }) {\n const { content, length = \"medium\", format = \"paragraph\", focus } = input;\n\n let systemPrompt = `You are a summarization expert.\nSummarize the content in ${lengthGuide[length]}.`;\n\n if (format === \"bullets\") {\n systemPrompt += \"\\nUse bullet points.\";\n }\n\n if (focus && focus.length > 0) {\n systemPrompt += `\\nFocus on: ${focus.join(\", \")}.`;\n }\n\n systemPrompt += \"\\nOnly output the summary, nothing else.\";\n\n const result = await gerbil.generate(`Summarize this:\\n\\n${content}`, {\n system: systemPrompt,\n maxTokens: maxTokensGuide[length],\n temperature: this.temperature,\n });\n\n return result.text;\n },\n});\n","/**\n * Test Skill\n *\n * Generate tests for code.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst TestInput = z.object({\n /** Code to test */\n code: z.string(),\n\n /** Test framework */\n framework: z.enum([\"jest\", \"vitest\", \"mocha\", \"playwright\"]).default(\"vitest\"),\n\n /** Test style */\n style: z.enum([\"unit\", \"integration\", \"e2e\"]).default(\"unit\"),\n});\n\nexport type TestInput = z.infer<typeof TestInput>;\n\nexport const test = defineSkill({\n name: \"test\",\n description: \"Generate tests for code\",\n version: \"1.0.0\",\n input: TestInput,\n temperature: 0.3,\n maxTokens: 800,\n\n async run({ input, gerbil }) {\n const { code, framework, style } = input;\n\n const systemPrompt = `You are a test engineer.\nGenerate ${style} tests using ${framework} for the provided code.\nInclude edge cases and error scenarios.\nOnly output the test code, no explanations.`;\n\n const result = await gerbil.generate(`Generate tests for:\\n\\n${code}`, {\n system: systemPrompt,\n maxTokens: this.maxTokens,\n temperature: this.temperature,\n });\n\n return result.text;\n },\n});\n","/**\n * Title Skill\n *\n * Generate titles for content.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst TitleInput = z.object({\n /** Content to generate title for */\n content: z.string(),\n\n /** Title style */\n style: z.enum([\"professional\", \"clickbait\", \"seo\", \"simple\"]).default(\"professional\"),\n\n /** Max length */\n maxLength: z.number().default(60),\n});\n\nexport type TitleInput = z.infer<typeof TitleInput>;\n\nconst styleGuide = {\n professional: \"Clear, informative, and professional\",\n clickbait: \"Engaging and curiosity-inducing\",\n seo: \"SEO-optimized with relevant keywords\",\n simple: \"Simple and straightforward\",\n};\n\nexport const title = defineSkill({\n name: \"title\",\n description: \"Generate titles for content\",\n version: \"1.0.0\",\n input: TitleInput,\n temperature: 0.7,\n maxTokens: 30,\n\n async run({ input, gerbil }) {\n const { content, style = \"professional\", maxLength = 60 } = input;\n\n const systemPrompt = `Generate a title for the content.\nStyle: ${styleGuide[style]}\nMax length: ${maxLength} characters\nOnly output the title, nothing else.`;\n\n const result = await gerbil.generate(`Generate a title for:\\n\\n${content.substring(0, 1000)}`, {\n system: systemPrompt,\n maxTokens: this.maxTokens,\n temperature: this.temperature,\n });\n\n let generatedTitle = result.text.trim();\n generatedTitle = generatedTitle.replace(/^[\"']|[\"']$/g, \"\");\n\n if (generatedTitle.length > maxLength) {\n generatedTitle = `${generatedTitle.substring(0, maxLength - 3)}...`;\n }\n\n return generatedTitle;\n },\n});\n","/**\n * Transcribe Skill\n *\n * Convert audio to text using on-device STT (Whisper).\n */\n\nimport { existsSync, readFileSync, writeFileSync } from \"fs\";\nimport { resolve } from \"path\";\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst TranscribeInput = z.object({\n /** Audio file path (WAV, MP3, etc.) or URL */\n audio: z.string(),\n\n /** STT model ID (default: whisper-tiny.en) */\n model: z\n .enum([\"whisper-tiny.en\", \"whisper-base.en\", \"whisper-small.en\", \"whisper-large-v3-turbo\"])\n .default(\"whisper-tiny.en\"),\n\n /** Language hint for multilingual models */\n language: z.string().optional(),\n\n /** Include timestamps in output */\n timestamps: z.boolean().default(false),\n\n /** Save transcription to file */\n output: z.string().optional(),\n});\n\nexport type TranscribeInput = z.infer<typeof TranscribeInput>;\n\nexport const transcribe = defineSkill({\n name: \"transcribe\",\n description: \"Transcribe audio to text using on-device STT (Whisper)\",\n version: \"1.0.0\",\n input: TranscribeInput,\n\n async run({ input, gerbil }) {\n const { audio, model = \"whisper-tiny.en\", language, timestamps = false, output } = input;\n\n // Load audio file\n const filePath = resolve(audio);\n if (!existsSync(filePath)) {\n throw new Error(`Audio file not found: ${filePath}`);\n }\n\n const audioData = new Uint8Array(readFileSync(filePath));\n\n // Load the specified STT model\n await gerbil.loadSTT(model);\n\n // Transcribe\n const result = await gerbil.transcribe(audioData, {\n language,\n timestamps,\n });\n\n // Format output\n let text: string;\n if (timestamps && result.segments) {\n text = result.segments\n .map((seg) => `[${seg.start.toFixed(1)}s - ${seg.end.toFixed(1)}s] ${seg.text}`)\n .join(\"\\n\");\n } else {\n text = result.text;\n }\n\n // Save to file if requested\n if (output) {\n writeFileSync(output, text);\n return `Transcribed ${result.duration.toFixed(1)}s of audio to ${output}`;\n }\n\n return text;\n },\n});\n","/**\n * Translate Skill\n *\n * Translate text between languages.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst TranslateInput = z.object({\n /** Text to translate */\n text: z.string(),\n\n /** Source language (auto-detected if not provided) */\n from: z.string().optional(),\n\n /** Target language */\n to: z.string(),\n\n /** Preserve formatting */\n preserveFormatting: z.boolean().default(true),\n});\n\nexport type TranslateInput = z.infer<typeof TranslateInput>;\n\nexport const translate = defineSkill({\n name: \"translate\",\n description: \"Translate text between languages\",\n version: \"1.0.0\",\n input: TranslateInput,\n temperature: 0.3,\n\n async run({ input, gerbil }) {\n const { text, from, to, preserveFormatting } = input;\n\n let systemPrompt = `You are a professional translator.\nTranslate the text to ${to}.`;\n\n if (from) {\n systemPrompt += `\\nThe source language is ${from}.`;\n }\n\n if (preserveFormatting) {\n systemPrompt += \"\\nPreserve the original formatting (paragraphs, lists, etc.).\";\n }\n\n systemPrompt += \"\\nOnly output the translation, nothing else.\";\n\n const result = await gerbil.generate(`Translate:\\n\\n${text}`, {\n system: systemPrompt,\n maxTokens: Math.ceil(text.length / 2),\n temperature: this.temperature,\n });\n\n return result.text;\n },\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"skills-Hf3iEkq4.mjs","names":["ctx: SkillContext<TInput>","path","fs","loaded: string[]","analysisPrompts: Record<string, string>","stylePrompts: Record<string, string>","saveWav","join","tmpdir","captionPrompts: Record<string, string>","lengthGuides: Record<string, number>","platformGuides: Record<string, string>","execSync","comparisonPrompts: Record<string, string>","focusPrompts: Record<string, string>","detailLengths: Record<string, number>","saveWav","playAudio","speak","extractionPrompts: Record<string, string>","formatInstructions: Record<string, string>","extract","saveWav","existsSync","textToRead: string","readFileSync","join","tmpdir","join","tmpdir","existsSync","readFileSync","text: string"],"sources":["../src/skills/registry.ts","../src/skills/loader.ts","../src/skills/builtin/analyze-screenshot.ts","../src/skills/builtin/announce.ts","../src/skills/builtin/caption-image.ts","../src/skills/builtin/commit.ts","../src/skills/builtin/compare-images.ts","../src/skills/builtin/describe-image.ts","../src/skills/builtin/explain.ts","../src/skills/builtin/extract.ts","../src/skills/builtin/extract-from-image.ts","../src/skills/builtin/read-aloud.ts","../src/skills/builtin/review.ts","../src/skills/builtin/speak.ts","../src/skills/builtin/summarize.ts","../src/skills/builtin/test.ts","../src/skills/builtin/title.ts","../src/skills/builtin/transcribe.ts","../src/skills/builtin/translate.ts"],"sourcesContent":["/**\n * Skill Registry\n *\n * Central registry for all skills (built-in and custom).\n */\n\nimport type { Gerbil } from \"../core/gerbil.js\";\nimport { getInstance } from \"../core/one-liner.js\";\nimport type { Skill, SkillContext, SkillDefinition, SkillInfo } from \"./types.js\";\n\n// ============================================\n// Registry Storage\n// ============================================\n\nconst registry = new Map<string, Skill>();\nconst skillSources = new Map<string, string>(); // name -> source file\n\n// Reserved names that cannot be used for custom skills (CLI commands/views)\n// Note: Built-in skills like \"commit\", \"summarize\" ARE allowed because they\n// have dedicated CLI commands, but new skills cannot use these view names\nconst RESERVED_NAMES = new Set([\n // REPL views (would conflict with CLI shortcuts)\n \"repl\",\n \"chat\",\n \"skills\",\n \"tools\",\n \"model\",\n \"integrate\",\n \"benchmark\",\n \"info\",\n \"serve\",\n \"cache\",\n // CLI commands\n \"generate\",\n \"models\",\n \"bench\",\n // Aliases\n \"r\",\n \"c\",\n \"g\",\n // Other reserved\n \"help\",\n \"version\",\n \"gerbil\",\n]);\n\n// ============================================\n// Define Skill\n// ============================================\n\n/**\n * Define and register a skill\n *\n * @example\n * ```ts\n * const sentiment = defineSkill({\n * name: \"sentiment\",\n * description: \"Analyze sentiment of text\",\n * input: z.object({ text: z.string() }),\n * output: z.object({ sentiment: z.enum([\"positive\", \"negative\", \"neutral\"]) }),\n * async run({ input, gerbil }) {\n * return gerbil.json(`Analyze sentiment: ${input.text}`, { schema: this.output });\n * }\n * });\n * ```\n */\nexport function defineSkill<TInput, TOutput>(\n definition: SkillDefinition<TInput, TOutput>,\n): Skill<TInput, TOutput> {\n // Validate name format\n if (!/^[a-z][a-z0-9-]*$/.test(definition.name)) {\n throw new Error(`Skill name must be kebab-case starting with a letter: ${definition.name}`);\n }\n\n // Check for reserved names (CLI commands)\n if (RESERVED_NAMES.has(definition.name)) {\n throw new Error(\n `Skill name \"${definition.name}\" is reserved for CLI commands. Choose a different name.`,\n );\n }\n\n // Create the skill function\n const execute = async (input: TInput): Promise<TOutput> => skill.run(input);\n\n // Create the skill object\n const skill = Object.assign(execute, {\n definition,\n\n async run(input: TInput, gerbil?: Gerbil): Promise<TOutput> {\n // Get or create Gerbil instance\n const g = gerbil ?? (await getInstance(definition.model));\n\n // Validate input if schema provided\n let validatedInput = input;\n if (definition.input) {\n const parsed = definition.input.safeParse(input);\n if (!parsed.success) {\n throw new Error(`Invalid input for skill \"${definition.name}\": ${parsed.error.message}`);\n }\n validatedInput = parsed.data;\n }\n\n // Create context\n const ctx: SkillContext<TInput> = {\n input: validatedInput,\n gerbil: g,\n rawInput: input,\n definition: definition as SkillDefinition<TInput, unknown>,\n };\n\n // Run the skill\n const result = await definition.run.call(definition, ctx);\n\n // Validate output if schema provided\n if (definition.output && typeof result !== \"string\") {\n const parsed = definition.output.safeParse(result);\n if (!parsed.success) {\n throw new Error(\n `Invalid output from skill \"${definition.name}\": ${parsed.error.message}`,\n );\n }\n return parsed.data;\n }\n\n return result as TOutput;\n },\n }) as Skill<TInput, TOutput>;\n\n // Register the skill\n registry.set(definition.name, skill as Skill);\n\n return skill;\n}\n\n// ============================================\n// Use Skill\n// ============================================\n\n/**\n * Get a skill by name\n *\n * @example\n * ```ts\n * const sentiment = useSkill(\"sentiment\");\n * const result = await sentiment({ text: \"I love this!\" });\n * ```\n */\nexport function useSkill<TInput = unknown, TOutput = unknown>(\n name: string,\n): Skill<TInput, TOutput> {\n const skill = registry.get(name);\n if (!skill) {\n throw new Error(`Skill not found: \"${name}\". Available: ${listSkills().join(\", \") || \"none\"}`);\n }\n return skill as Skill<TInput, TOutput>;\n}\n\n// ============================================\n// Registry Operations\n// ============================================\n\n/**\n * List all registered skill names\n */\nexport function listSkills(): string[] {\n return Array.from(registry.keys()).sort();\n}\n\n/**\n * Get skill metadata\n */\nexport function getSkillInfo(name: string): SkillInfo | undefined {\n const skill = registry.get(name);\n if (!skill) {\n return;\n }\n\n return {\n name: skill.definition.name,\n description: skill.definition.description,\n version: skill.definition.version,\n author: skill.definition.author,\n builtin: !skillSources.has(name),\n source: skillSources.get(name),\n };\n}\n\n/**\n * Check if a skill exists\n */\nexport function hasSkill(name: string): boolean {\n return registry.has(name);\n}\n\n/**\n * Remove a skill from registry\n */\nexport function removeSkill(name: string): boolean {\n skillSources.delete(name);\n return registry.delete(name);\n}\n\n/**\n * Clear all skills from registry\n */\nexport function clearSkills(): void {\n registry.clear();\n skillSources.clear();\n}\n\n/**\n * Get all skill info\n */\nexport function getAllSkillInfo(): SkillInfo[] {\n return listSkills().map((name) => getSkillInfo(name)!);\n}\n\n/**\n * Check if a name is reserved (cannot be used for custom skills)\n */\nexport function isReservedName(name: string): boolean {\n return RESERVED_NAMES.has(name);\n}\n\n/**\n * Get list of reserved names\n */\nexport function getReservedNames(): string[] {\n return Array.from(RESERVED_NAMES).sort();\n}\n\n// ============================================\n// Internal: Register with source\n// ============================================\n\n/**\n * Register a skill with its source file (used by loader)\n * @internal\n */\nexport function registerSkillWithSource(skill: Skill, source: string): void {\n registry.set(skill.definition.name, skill);\n skillSources.set(skill.definition.name, source);\n}\n","/**\n * Skill Loader\n *\n * Load skills from files, directories, and packages.\n */\n\nimport { pathToFileURL } from \"node:url\";\nimport { registerSkillWithSource } from \"./registry.js\";\nimport type { LoadSkillsOptions, Skill } from \"./types.js\";\n\n// ============================================\n// Auto-Load Project Skills\n// ============================================\n\n/**\n * Load skills from the project's .gerbil/skills/ directory\n *\n * This is the standard location for project-specific skills.\n * Creates the directory structure if it doesn't exist.\n *\n * @example\n * ```ts\n * // Load from .gerbil/skills/ in current working directory\n * const loaded = await loadProjectSkills();\n * console.log(`Loaded ${loaded.length} project skills`);\n * ```\n */\nexport async function loadProjectSkills(cwd: string = process.cwd()): Promise<string[]> {\n const path = await import(\"node:path\");\n const fs = await import(\"node:fs\");\n\n const gerbilDir = path.join(cwd, \".gerbil\");\n const skillsDir = path.join(gerbilDir, \"skills\");\n\n // If .gerbil/skills doesn't exist, that's fine - just return empty\n if (!fs.existsSync(skillsDir)) {\n return [];\n }\n\n return loadSkills(skillsDir);\n}\n\n// ============================================\n// Load from Directory\n// ============================================\n\n/**\n * Load all skills from a directory\n *\n * @example\n * ```ts\n * // Load all *.skill.ts and *.skill.js files\n * const loaded = await loadSkills(\"./skills\");\n * console.log(`Loaded ${loaded.length} skills`);\n * ```\n */\nexport async function loadSkills(dir: string, options: LoadSkillsOptions = {}): Promise<string[]> {\n const { patterns = [\"*.skill.ts\", \"*.skill.js\"] } = options;\n const loaded: string[] = [];\n\n try {\n const fs = await import(\"node:fs\");\n const path = await import(\"node:path\");\n\n const resolvedDir = path.resolve(dir);\n\n if (!fs.existsSync(resolvedDir)) {\n throw new Error(`Skills directory not found: ${dir}`);\n }\n\n const files = fs.readdirSync(resolvedDir);\n\n for (const file of files) {\n const matches = patterns.some((pattern) => {\n const regex = new RegExp(`^${pattern.replace(/\\*/g, \".*\").replace(/\\./g, \"\\\\.\")}$`);\n return regex.test(file);\n });\n\n if (matches) {\n const filePath = path.join(resolvedDir, file);\n const skill = await loadSkill(filePath);\n if (skill) {\n loaded.push(skill.definition.name);\n }\n }\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"MODULE_NOT_FOUND\") {\n throw new Error(`Skills directory not found: ${dir}`);\n }\n throw error;\n }\n\n return loaded;\n}\n\n// ============================================\n// Load Single Skill\n// ============================================\n\n/**\n * Load a single skill from a file\n *\n * @example\n * ```ts\n * const skill = await loadSkill(\"./skills/sentiment.skill.ts\");\n * if (skill) {\n * const result = await skill({ text: \"Hello\" });\n * }\n * ```\n */\nexport async function loadSkill(filePath: string): Promise<Skill | null> {\n try {\n const path = await import(\"node:path\");\n const resolvedPath = path.resolve(filePath);\n\n // Use dynamic import with file URL for ESM compatibility\n const fileUrl = pathToFileURL(resolvedPath).href;\n const module = await import(fileUrl);\n\n // Get the default export\n const skill = module.default as Skill;\n\n if (!skill?.definition) {\n return null;\n }\n\n // Register with source tracking\n registerSkillWithSource(skill, resolvedPath);\n\n return skill;\n } catch (_error) {\n return null;\n }\n}\n\n// ============================================\n// Load from Package\n// ============================================\n\n/**\n * Load skills from an npm package\n *\n * @example\n * ```ts\n * // Load skills from a package\n * const loaded = await loadSkillPackage(\"gerbil-skills-extra\");\n * ```\n */\nexport async function loadSkillPackage(packageName: string): Promise<string[]> {\n try {\n const module = await import(packageName);\n const loaded: string[] = [];\n\n // Look for exported skills\n for (const [_key, value] of Object.entries(module)) {\n if (isSkill(value)) {\n const skill = value as Skill;\n registerSkillWithSource(skill, `npm:${packageName}`);\n loaded.push(skill.definition.name);\n }\n }\n\n // Also check default export if it's an object of skills\n if (module.default && typeof module.default === \"object\") {\n for (const [_key, value] of Object.entries(module.default)) {\n if (isSkill(value)) {\n const skill = value as Skill;\n registerSkillWithSource(skill, `npm:${packageName}`);\n loaded.push(skill.definition.name);\n }\n }\n }\n\n return loaded;\n } catch (error) {\n throw new Error(`Failed to load skill package \"${packageName}\": ${error}`);\n }\n}\n\n// ============================================\n// Helpers\n// ============================================\n\n/**\n * Check if a value is a Skill\n */\nfunction isSkill(value: unknown): value is Skill {\n return (\n typeof value === \"function\" &&\n typeof (value as Skill).definition === \"object\" &&\n typeof (value as Skill).definition.name === \"string\" &&\n typeof (value as Skill).definition.run === \"function\"\n );\n}\n","/**\n * Analyze Screenshot Skill\n *\n * Analyze a UI screenshot for design, accessibility, or QA purposes.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst AnalyzeScreenshotInput = z.object({\n /** Screenshot URL or data URI */\n image: z.string(),\n\n /** Analysis type */\n type: z\n .enum([\"ui-review\", \"accessibility\", \"suggestions\", \"qa\", \"ux-audit\", \"mobile-check\"])\n .default(\"ui-review\"),\n\n /** Specific areas to focus on (optional) */\n focus: z.array(z.string()).optional(),\n});\n\nexport type AnalyzeScreenshotInput = z.infer<typeof AnalyzeScreenshotInput>;\n\nconst analysisPrompts: Record<string, string> = {\n \"ui-review\": `Review this UI screenshot as a design expert. Analyze:\n- Visual hierarchy and layout\n- Typography and readability\n- Color scheme and contrast\n- Spacing and alignment\n- Component consistency\n- Overall design quality\n\nProvide specific observations and suggestions.`,\n\n accessibility: `Analyze this screenshot for accessibility issues. Check for:\n- Color contrast ratios\n- Text size and readability\n- Touch target sizes\n- Visual affordances for interactive elements\n- Potential issues for users with visual impairments\n- Missing alt text indicators\n\nList specific issues found and how to fix them.`,\n\n suggestions: `As a senior UX designer, review this UI and suggest improvements:\n- Modern design patterns that could be applied\n- Usability improvements\n- Visual polish opportunities\n- User experience enhancements\n- Ways to make it more engaging\n\nBe specific and actionable.`,\n\n qa: `Perform QA analysis on this screenshot. Look for:\n- Visual bugs or glitches\n- Alignment and spacing issues\n- Broken or missing elements\n- Inconsistencies with design standards\n- Text overflow or truncation issues\n- Responsive design problems\n\nReport each issue with its location and severity.`,\n\n \"ux-audit\": `Conduct a UX audit of this interface:\n- User flow clarity\n- Call-to-action visibility\n- Information architecture\n- Cognitive load assessment\n- Error prevention\n- User feedback mechanisms\n\nProvide a structured audit report.`,\n\n \"mobile-check\": `Evaluate this UI for mobile usability:\n- Touch target sizes (minimum 44x44px)\n- Thumb-zone accessibility\n- Content priority on small screens\n- Gesture affordances\n- Loading indicators\n- Mobile-specific patterns\n\nIdentify mobile-specific issues and recommendations.`,\n};\n\nexport const analyzeScreenshot = defineSkill({\n name: \"analyze-screenshot\",\n description: \"Analyze a UI screenshot for design, accessibility, or QA\",\n version: \"1.0.0\",\n model: \"ministral-3b\",\n input: AnalyzeScreenshotInput,\n maxTokens: 1200,\n temperature: 0.3,\n\n async run({ input, gerbil }) {\n const { image, type = \"ui-review\", focus } = input;\n\n if (!gerbil.supportsVision()) {\n throw new Error(\n `Current model doesn't support vision. Load a vision model like \"ministral-3b\" first.`,\n );\n }\n\n let prompt = analysisPrompts[type];\n\n if (focus && focus.length > 0) {\n prompt += `\\n\\nPay special attention to: ${focus.join(\", \")}.`;\n }\n\n const result = await gerbil.generate(prompt, {\n images: [{ source: image }],\n maxTokens: this.maxTokens,\n temperature: this.temperature,\n });\n\n return result.text;\n },\n});\n","/**\n * Announce Skill\n *\n * Generate and speak an announcement using AI.\n * Useful for scripts that need to give voice updates.\n */\n\nimport { execSync } from \"child_process\";\nimport { unlinkSync, writeFileSync } from \"fs\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst AnnounceInput = z.object({\n /** What to announce (will be rephrased by AI) */\n message: z.string(),\n\n /** Announcement style */\n style: z.enum([\"casual\", \"formal\", \"excited\", \"calm\", \"urgent\"]).default(\"casual\"),\n\n /** Voice ID */\n voice: z\n .enum([\"af_heart\", \"af_bella\", \"af_nicole\", \"am_fenrir\", \"am_michael\", \"bf_emma\", \"bm_george\"])\n .default(\"af_heart\"),\n\n /** Speech speed */\n speed: z.number().min(0.5).max(2.0).default(1.0),\n\n /** Just generate text, don't speak */\n textOnly: z.boolean().default(false),\n});\n\nexport type AnnounceInput = z.infer<typeof AnnounceInput>;\n\nconst stylePrompts: Record<string, string> = {\n casual: \"Rephrase this as a casual, friendly announcement. Keep it natural and conversational.\",\n formal: \"Rephrase this as a professional, formal announcement. Be clear and dignified.\",\n excited: \"Rephrase this as an excited, enthusiastic announcement! Add energy and positivity.\",\n calm: \"Rephrase this as a calm, soothing announcement. Be gentle and reassuring.\",\n urgent: \"Rephrase this as an urgent announcement. Be direct and emphasize importance.\",\n};\n\nfunction saveWav(filename: string, audio: Float32Array, sampleRate: number): void {\n const buffer = Buffer.alloc(44 + audio.length * 2);\n buffer.write(\"RIFF\", 0);\n buffer.writeUInt32LE(36 + audio.length * 2, 4);\n buffer.write(\"WAVE\", 8);\n buffer.write(\"fmt \", 12);\n buffer.writeUInt32LE(16, 16);\n buffer.writeUInt16LE(1, 20);\n buffer.writeUInt16LE(1, 22);\n buffer.writeUInt32LE(sampleRate, 24);\n buffer.writeUInt32LE(sampleRate * 2, 28);\n buffer.writeUInt16LE(2, 32);\n buffer.writeUInt16LE(16, 34);\n buffer.write(\"data\", 36);\n buffer.writeUInt32LE(audio.length * 2, 40);\n\n for (let i = 0; i < audio.length; i++) {\n const s = Math.max(-1, Math.min(1, audio[i]));\n buffer.writeInt16LE(Math.round(s * 32767), 44 + i * 2);\n }\n\n writeFileSync(filename, buffer);\n}\n\nexport const announce = defineSkill({\n name: \"announce\",\n description: \"Generate and speak an AI-crafted announcement\",\n version: \"1.0.0\",\n input: AnnounceInput,\n temperature: 0.7,\n maxTokens: 150,\n\n async run({ input, gerbil }) {\n const { message, style = \"casual\", voice = \"af_heart\", speed = 1.0, textOnly = false } = input;\n\n // Generate announcement text using AI\n const result = await gerbil.generate(message, {\n system: `${stylePrompts[style]}\nKeep it brief (1-2 sentences max). \nOutput only the announcement text, nothing else.`,\n temperature: this.temperature,\n maxTokens: this.maxTokens,\n });\n\n const announcementText = result.text.trim();\n\n if (textOnly) {\n return announcementText;\n }\n\n // Speak the announcement\n const speechResult = await gerbil.speak(announcementText, { voice, speed });\n\n // Play audio\n const tempFile = join(tmpdir(), `gerbil-announce-${Date.now()}.wav`);\n saveWav(tempFile, speechResult.audio, speechResult.sampleRate);\n\n try {\n const platform = process.platform;\n if (platform === \"darwin\") {\n execSync(`afplay \"${tempFile}\"`, { stdio: \"inherit\" });\n } else if (platform === \"linux\") {\n try {\n execSync(`aplay \"${tempFile}\"`, { stdio: \"inherit\" });\n } catch {\n execSync(`paplay \"${tempFile}\"`, { stdio: \"inherit\" });\n }\n } else if (platform === \"win32\") {\n execSync(`powershell -c \"(New-Object Media.SoundPlayer '${tempFile}').PlaySync()\"`, {\n stdio: \"inherit\",\n });\n }\n } finally {\n try {\n unlinkSync(tempFile);\n } catch {\n // Ignore\n }\n }\n\n return announcementText;\n },\n});\n","/**\n * Caption Image Skill\n *\n * Generate captions, alt text, or social media descriptions for images.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst CaptionImageInput = z.object({\n /** Image URL or data URI */\n image: z.string(),\n\n /** Caption type */\n type: z\n .enum([\"alt-text\", \"caption\", \"social-media\", \"seo\", \"artistic\", \"technical\"])\n .default(\"caption\"),\n\n /** Tone/style for the caption */\n tone: z\n .enum([\"professional\", \"casual\", \"playful\", \"formal\", \"descriptive\"])\n .default(\"professional\"),\n\n /** Maximum length hint */\n maxLength: z.enum([\"short\", \"medium\", \"long\"]).default(\"medium\"),\n\n /** Platform hint for social media captions */\n platform: z.enum([\"twitter\", \"instagram\", \"linkedin\", \"facebook\"]).optional(),\n});\n\nexport type CaptionImageInput = z.infer<typeof CaptionImageInput>;\n\nconst captionPrompts: Record<string, string> = {\n \"alt-text\": `Generate accessible alt text for this image.\n- Describe the essential content concisely\n- Include relevant details for screen reader users\n- Avoid starting with \"Image of\" or \"Picture of\"\n- Be factual and objective`,\n\n caption: `Write a caption for this image.\n- Capture the essence of the image\n- Make it engaging and informative\n- Consider the context and mood`,\n\n \"social-media\": `Create a social media caption for this image.\n- Make it engaging and shareable\n- Include relevant emoji where appropriate\n- Suggest hashtags if applicable\n- Optimize for engagement`,\n\n seo: `Write SEO-optimized alt text and description for this image.\n- Include relevant keywords naturally\n- Be descriptive but concise\n- Focus on searchable terms\n- Maintain readability`,\n\n artistic: `Write an artistic, evocative caption for this image.\n- Capture the mood and emotion\n- Use creative language\n- Tell a micro-story if appropriate\n- Be memorable and unique`,\n\n technical: `Write a technical description of this image.\n- Describe components and elements precisely\n- Use appropriate technical terminology\n- Note measurements or specifications if visible\n- Be objective and detailed`,\n};\n\nconst lengthGuides: Record<string, number> = {\n short: 100,\n medium: 200,\n long: 400,\n};\n\nconst platformGuides: Record<string, string> = {\n twitter: \"Keep under 280 characters. Make it punchy and tweetable.\",\n instagram: \"Include relevant hashtags. Can be longer and more storytelling.\",\n linkedin: \"Keep it professional. Focus on value and insights.\",\n facebook: \"Can be conversational. Ask questions to encourage engagement.\",\n};\n\nexport const captionImage = defineSkill({\n name: \"caption-image\",\n description: \"Generate captions, alt text, or social media descriptions for images\",\n version: \"1.0.0\",\n model: \"ministral-3b\",\n input: CaptionImageInput,\n temperature: 0.7,\n\n async run({ input, gerbil }) {\n const {\n image,\n type = \"caption\",\n tone = \"professional\",\n maxLength = \"medium\",\n platform,\n } = input;\n\n if (!gerbil.supportsVision()) {\n throw new Error(\n `Current model doesn't support vision. Load a vision model like \"ministral-3b\" first.`,\n );\n }\n\n let prompt = captionPrompts[type];\n prompt += `\\n\\nTone: ${tone}`;\n\n if (platform && type === \"social-media\") {\n prompt += `\\n\\nPlatform: ${platform}. ${platformGuides[platform]}`;\n }\n\n const result = await gerbil.generate(prompt, {\n images: [{ source: image }],\n maxTokens: lengthGuides[maxLength],\n temperature: this.temperature,\n });\n\n return result.text;\n },\n});\n","/**\n * Commit Skill\n *\n * Generate git commit messages from staged changes.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst CommitInput = z.object({\n /** Git diff (auto-detected if not provided) */\n diff: z.string().optional(),\n\n /** Commit style */\n type: z.enum([\"conventional\", \"simple\", \"detailed\"]).default(\"conventional\"),\n\n /** Max length for commit message */\n maxLength: z.number().default(72),\n});\n\nexport type CommitInput = z.infer<typeof CommitInput>;\n\nasync function getGitDiff(): Promise<string> {\n try {\n const { execSync } = await import(\"node:child_process\");\n return execSync(\"git diff --staged\", { encoding: \"utf-8\" });\n } catch {\n return \"\";\n }\n}\n\nfunction getSystemPrompt(type: CommitInput[\"type\"]): string {\n switch (type) {\n case \"conventional\":\n return `Generate a git commit message following the Conventional Commits format.\nUse one of these types: feat, fix, docs, style, refactor, perf, test, chore.\nFormat: type(scope): description\nKeep it under 72 characters.\nNo period at the end.\nOnly output the commit message, nothing else.`;\n\n case \"detailed\":\n return `Generate a detailed git commit message with a subject line and body.\nSubject: under 72 chars, imperative mood\nBody: explain what and why\nOnly output the commit message, nothing else.`;\n\n default:\n return `Generate a concise git commit message.\nUse imperative mood (e.g., \"Add\", \"Fix\", \"Update\").\nKeep it under 72 characters.\nNo period at the end.\nOnly output the commit message, nothing else.`;\n }\n}\n\nexport const commit = defineSkill({\n name: \"commit\",\n description: \"Generate a git commit message from staged changes\",\n version: \"1.0.0\",\n input: CommitInput,\n temperature: 0.3,\n maxTokens: 100,\n\n async run({ input, gerbil }) {\n const { diff, type = \"conventional\", maxLength = 72 } = input;\n\n // Get diff from git if not provided\n const actualDiff = diff || (await getGitDiff());\n\n if (!actualDiff || actualDiff.trim().length === 0) {\n throw new Error(\"No changes staged. Run `git add` first.\");\n }\n\n const result = await gerbil.generate(\n `Generate a commit message for the following diff:\\n\\n${actualDiff}`,\n {\n system: getSystemPrompt(type),\n maxTokens: this.maxTokens,\n temperature: this.temperature,\n },\n );\n\n // Clean and truncate\n let message = result.text.trim();\n message = message.replace(/<think>[\\s\\S]*?<\\/think>/g, \"\").trim(); // Remove think tags\n message = message.replace(/<\\/?think>/g, \"\").trim(); // Remove unclosed tags\n message = message.replace(/^[\"']|[\"']$/g, \"\"); // Remove quotes\n message = message.split(\"\\n\")[0]; // First line only\n if (message.length > maxLength) {\n message = `${message.substring(0, maxLength - 3)}...`;\n }\n\n return message;\n },\n});\n","/**\n * Compare Images Skill\n *\n * Compare two images and describe their differences.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst CompareImagesInput = z.object({\n /** First image URL or data URI */\n image1: z.string(),\n\n /** Second image URL or data URI */\n image2: z.string(),\n\n /** Comparison type */\n type: z\n .enum([\"visual-diff\", \"design-comparison\", \"before-after\", \"a-b-test\", \"version-diff\"])\n .default(\"visual-diff\"),\n\n /** Specific aspects to compare */\n focus: z.array(z.string()).optional(),\n});\n\nexport type CompareImagesInput = z.infer<typeof CompareImagesInput>;\n\nconst comparisonPrompts: Record<string, string> = {\n \"visual-diff\": `Compare these two images and identify all visual differences.\n- List specific elements that differ\n- Note position, color, size, and content changes\n- Identify additions and removals\n- Rate the significance of each change`,\n\n \"design-comparison\": `Compare these two designs as a design expert.\n- Analyze layout differences\n- Compare typography and colors\n- Evaluate which design is more effective\n- Suggest which elements work better in each`,\n\n \"before-after\": `Analyze these before and after images.\n- Describe what changed\n- Evaluate if the changes are improvements\n- Note any unintended consequences\n- Summarize the overall transformation`,\n\n \"a-b-test\": `Analyze these two variants for A/B testing purposes.\n- Identify the key differences being tested\n- Predict which might perform better and why\n- Note potential user experience impacts\n- Suggest what metrics to measure`,\n\n \"version-diff\": `Compare these two versions of the UI.\n- Document all changes between versions\n- Categorize changes (bug fix, feature, style)\n- Identify breaking changes\n- Note any regressions`,\n};\n\nexport const compareImages = defineSkill({\n name: \"compare-images\",\n description: \"Compare two images and describe their differences\",\n version: \"1.0.0\",\n model: \"ministral-3b\",\n input: CompareImagesInput,\n maxTokens: 1500,\n temperature: 0.3,\n\n async run({ input, gerbil }) {\n const { image1, image2, type = \"visual-diff\", focus } = input;\n\n if (!gerbil.supportsVision()) {\n throw new Error(\n `Current model doesn't support vision. Load a vision model like \"ministral-3b\" first.`,\n );\n }\n\n let prompt = `I'm showing you two images for comparison.\\n\\n${comparisonPrompts[type]}`;\n\n if (focus && focus.length > 0) {\n prompt += `\\n\\nFocus specifically on: ${focus.join(\", \")}.`;\n }\n\n prompt += \"\\n\\nThe first image is shown first, followed by the second image.\";\n\n const result = await gerbil.generate(prompt, {\n images: [{ source: image1 }, { source: image2 }],\n maxTokens: this.maxTokens,\n temperature: this.temperature,\n });\n\n return result.text;\n },\n});\n","/**\n * Describe Image Skill\n *\n * Describe an image using vision AI.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst DescribeImageInput = z.object({\n /** Image URL or data URI */\n image: z.string(),\n\n /** What to focus on */\n focus: z.enum([\"general\", \"details\", \"text\", \"objects\", \"scene\", \"colors\"]).default(\"general\"),\n\n /** Output format */\n format: z.enum([\"paragraph\", \"bullets\", \"structured\"]).default(\"paragraph\"),\n\n /** Maximum detail level */\n detail: z.enum([\"brief\", \"normal\", \"comprehensive\"]).default(\"normal\"),\n});\n\nexport type DescribeImageInput = z.infer<typeof DescribeImageInput>;\n\nconst focusPrompts: Record<string, string> = {\n general:\n \"Describe this image comprehensively, covering the main subject, context, and notable elements.\",\n details:\n \"Describe this image in detail, including colors, textures, lighting, and small elements.\",\n text: \"Extract and describe any text visible in this image. Include the text content and its context.\",\n objects:\n \"List and describe all objects you can identify in this image, including their positions.\",\n scene: \"Describe the scene, setting, and atmosphere of this image. What story does it tell?\",\n colors: \"Analyze the color palette and visual composition of this image.\",\n};\n\nconst detailLengths: Record<string, number> = {\n brief: 150,\n normal: 400,\n comprehensive: 800,\n};\n\nexport const describeImage = defineSkill({\n name: \"describe-image\",\n description: \"Describe an image using vision AI\",\n version: \"1.0.0\",\n model: \"ministral-3b\",\n input: DescribeImageInput,\n temperature: 0.5,\n\n async run({ input, gerbil }) {\n const { image, focus = \"general\", format = \"paragraph\", detail = \"normal\" } = input;\n\n // Check if model supports vision\n if (!gerbil.supportsVision()) {\n throw new Error(\n `Current model doesn't support vision. Load a vision model like \"ministral-3b\" first.`,\n );\n }\n\n const formatInstructions: Record<string, string> = {\n paragraph: \"Write your description as natural flowing paragraphs.\",\n bullets: \"Format your description as clear bullet points.\",\n structured:\n \"Structure your description with sections: Overview, Main Elements, Details, Observations.\",\n };\n\n const prompt = `${focusPrompts[focus]}\\n\\n${formatInstructions[format]}`;\n\n const result = await gerbil.generate(prompt, {\n images: [{ source: image }],\n maxTokens: detailLengths[detail],\n temperature: this.temperature,\n });\n\n return result.text;\n },\n});\n","/**\n * Explain Skill\n *\n * Explain code or concepts at various levels.\n * Optionally speaks the explanation aloud.\n */\n\nimport { execSync } from \"node:child_process\";\nimport { unlinkSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst ExplainInput = z.object({\n /** Content to explain */\n content: z.string(),\n\n /** Explanation level */\n level: z.enum([\"beginner\", \"intermediate\", \"expert\"]).default(\"intermediate\"),\n\n /** Programming language (for code) */\n language: z.string().optional(),\n\n /** Speak the explanation aloud using TTS */\n speak: z.boolean().default(false),\n\n /** Voice for TTS (if speak is true) */\n voice: z.enum([\"af_heart\", \"af_bella\", \"bf_emma\", \"am_fenrir\"]).default(\"af_heart\"),\n});\n\nexport type ExplainInput = z.infer<typeof ExplainInput>;\n\nconst levelGuide = {\n beginner: \"Explain like I'm new to programming. Use simple terms and analogies.\",\n intermediate: \"Explain for someone with programming experience.\",\n expert: \"Explain with technical depth, including implementation details.\",\n};\n\nfunction saveWav(filename: string, audio: Float32Array, sampleRate: number): void {\n const buffer = Buffer.alloc(44 + audio.length * 2);\n buffer.write(\"RIFF\", 0);\n buffer.writeUInt32LE(36 + audio.length * 2, 4);\n buffer.write(\"WAVE\", 8);\n buffer.write(\"fmt \", 12);\n buffer.writeUInt32LE(16, 16);\n buffer.writeUInt16LE(1, 20);\n buffer.writeUInt16LE(1, 22);\n buffer.writeUInt32LE(sampleRate, 24);\n buffer.writeUInt32LE(sampleRate * 2, 28);\n buffer.writeUInt16LE(2, 32);\n buffer.writeUInt16LE(16, 34);\n buffer.write(\"data\", 36);\n buffer.writeUInt32LE(audio.length * 2, 40);\n\n for (let i = 0; i < audio.length; i++) {\n const s = Math.max(-1, Math.min(1, audio[i]));\n buffer.writeInt16LE(Math.round(s * 32767), 44 + i * 2);\n }\n\n writeFileSync(filename, buffer);\n}\n\nfunction playAudio(audioFile: string): void {\n const platform = process.platform;\n if (platform === \"darwin\") {\n execSync(`afplay \"${audioFile}\"`, { stdio: \"inherit\" });\n } else if (platform === \"linux\") {\n try {\n execSync(`aplay \"${audioFile}\"`, { stdio: \"inherit\" });\n } catch {\n execSync(`paplay \"${audioFile}\"`, { stdio: \"inherit\" });\n }\n } else if (platform === \"win32\") {\n execSync(`powershell -c \"(New-Object Media.SoundPlayer '${audioFile}').PlaySync()\"`, {\n stdio: \"inherit\",\n });\n }\n}\n\nexport const explain = defineSkill({\n name: \"explain\",\n description: \"Explain code or concepts at various levels (optionally speak aloud)\",\n version: \"1.0.0\",\n input: ExplainInput,\n temperature: 0.5,\n maxTokens: 500,\n\n async run({ input, gerbil }) {\n const { content, level = \"intermediate\", language, speak = false, voice = \"af_heart\" } = input;\n\n let systemPrompt = `You are a patient teacher.\n${levelGuide[level]}`;\n\n if (language) {\n systemPrompt += `\\nThis is ${language} code.`;\n }\n\n systemPrompt += \"\\nBe clear and concise.\";\n\n const result = await gerbil.generate(`Explain this:\\n\\n${content}`, {\n system: systemPrompt,\n maxTokens: this.maxTokens,\n temperature: this.temperature,\n });\n\n const explanation = result.text;\n\n // Optionally speak the explanation\n if (speak) {\n // Split into sentences for smoother playback\n const sentences = explanation.split(/(?<=[.!?])\\s+/).filter((s) => s.trim());\n\n for (const sentence of sentences) {\n if (!sentence.trim()) continue;\n\n const speechResult = await gerbil.speak(sentence, { voice });\n const tempFile = join(tmpdir(), `gerbil-explain-${Date.now()}.wav`);\n saveWav(tempFile, speechResult.audio, speechResult.sampleRate);\n\n try {\n playAudio(tempFile);\n } finally {\n try {\n unlinkSync(tempFile);\n } catch {\n // Ignore\n }\n }\n }\n }\n\n return explanation;\n },\n});\n","/**\n * Extract Skill\n *\n * Extract structured data from content.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst ExtractInput = z.object({\n /** Content to extract from */\n content: z.string(),\n\n /** Zod schema for extraction (passed at runtime) */\n schema: z.any(),\n\n /** Additional context */\n context: z.string().optional(),\n});\n\nexport type ExtractInput = z.infer<typeof ExtractInput>;\n\nexport const extract = defineSkill({\n name: \"extract\",\n description: \"Extract structured data from content\",\n version: \"1.0.0\",\n input: ExtractInput,\n temperature: 0.3,\n\n async run({ input, gerbil }) {\n const { content, schema, context } = input;\n\n let prompt = \"Extract structured data from the following content.\";\n\n if (context) {\n prompt += `\\nContext: ${context}`;\n }\n\n prompt += `\\n\\nContent:\\n${content}`;\n\n return gerbil.json(prompt, { schema });\n },\n});\n","/**\n * Extract from Image Skill\n *\n * Extract text, code, data, or structured information from images.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst ExtractFromImageInput = z.object({\n /** Image URL or data URI */\n image: z.string(),\n\n /** What to extract */\n extract: z.enum([\"text\", \"code\", \"data\", \"table\", \"diagram\", \"form\", \"receipt\"]).default(\"text\"),\n\n /** Output format */\n outputFormat: z.enum([\"raw\", \"json\", \"markdown\", \"csv\"]).default(\"raw\"),\n\n /** Language hint for code extraction */\n language: z.string().optional(),\n});\n\nexport type ExtractFromImageInput = z.infer<typeof ExtractFromImageInput>;\n\nconst extractionPrompts: Record<string, string> = {\n text: `Extract all visible text from this image.\n- Preserve the original formatting and structure as much as possible\n- Include headings, paragraphs, and any labels\n- Note any text that's unclear or partially visible\n- Maintain the reading order`,\n\n code: `Extract the code visible in this image.\n- Preserve exact syntax, indentation, and formatting\n- Include comments if visible\n- Note any parts that are unclear\n- Format as a proper code block`,\n\n data: `Extract all data, numbers, and structured information from this image.\n- Include labels and their associated values\n- Preserve numerical precision\n- Note units and currencies\n- Identify any patterns or relationships`,\n\n table: `Extract the table data from this image.\n- Identify all columns and rows\n- Preserve cell alignment where meaningful\n- Handle merged cells appropriately\n- Include headers and any totals`,\n\n diagram: `Describe and extract information from this diagram or flowchart.\n- List all nodes/boxes and their labels\n- Describe connections and their directions\n- Note any annotations or legends\n- Explain the flow or relationships`,\n\n form: `Extract form fields and their values from this image.\n- List each field label and its value\n- Note required fields if indicated\n- Include dropdown selections\n- Preserve the form structure`,\n\n receipt: `Extract receipt/invoice information from this image.\n- Vendor/store name\n- Date and time\n- Line items with quantities and prices\n- Subtotals, taxes, and total\n- Payment method if shown`,\n};\n\nconst formatInstructions: Record<string, string> = {\n raw: \"Output the extracted content as plain text.\",\n json: \"Output the extracted content as valid JSON with appropriate structure.\",\n markdown: \"Format the output as Markdown with proper headings and formatting.\",\n csv: \"Output tabular data in CSV format with proper quoting.\",\n};\n\nexport const extractFromImage = defineSkill({\n name: \"extract-from-image\",\n description: \"Extract text, code, tables, or data from images\",\n version: \"1.0.0\",\n model: \"ministral-3b\",\n input: ExtractFromImageInput,\n maxTokens: 2000,\n temperature: 0.1, // Low temp for accuracy\n\n async run({ input, gerbil }) {\n const { image, extract = \"text\", outputFormat = \"raw\", language } = input;\n\n if (!gerbil.supportsVision()) {\n throw new Error(\n `Current model doesn't support vision. Load a vision model like \"ministral-3b\" first.`,\n );\n }\n\n let prompt = extractionPrompts[extract];\n\n if (language && extract === \"code\") {\n prompt += `\\n\\nThe code appears to be ${language}.`;\n }\n\n prompt += `\\n\\n${formatInstructions[outputFormat]}`;\n\n const result = await gerbil.generate(prompt, {\n images: [{ source: image }],\n maxTokens: this.maxTokens,\n temperature: this.temperature,\n });\n\n return result.text;\n },\n});\n","/**\n * Read Aloud Skill\n *\n * Read text or file content aloud using TTS.\n * Supports streaming for long content.\n */\n\nimport { execSync } from \"child_process\";\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from \"fs\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst ReadAloudInput = z.object({\n /** File path to read, or text content if no file */\n content: z.string(),\n\n /** Treat content as file path (default: auto-detect) */\n isFile: z.boolean().optional(),\n\n /** Voice ID */\n voice: z\n .enum([\"af_heart\", \"af_bella\", \"af_nicole\", \"am_fenrir\", \"am_michael\", \"bf_emma\", \"bm_george\"])\n .default(\"af_heart\"),\n\n /** Speech speed */\n speed: z.number().min(0.5).max(2.0).default(1.0),\n\n /** Maximum characters to read (default: 5000) */\n maxLength: z.number().default(5000),\n\n /** Summarize if content exceeds maxLength instead of truncating */\n summarizeIfLong: z.boolean().default(false),\n});\n\nexport type ReadAloudInput = z.infer<typeof ReadAloudInput>;\n\nfunction saveWav(filename: string, audio: Float32Array, sampleRate: number): void {\n const buffer = Buffer.alloc(44 + audio.length * 2);\n buffer.write(\"RIFF\", 0);\n buffer.writeUInt32LE(36 + audio.length * 2, 4);\n buffer.write(\"WAVE\", 8);\n buffer.write(\"fmt \", 12);\n buffer.writeUInt32LE(16, 16);\n buffer.writeUInt16LE(1, 20);\n buffer.writeUInt16LE(1, 22);\n buffer.writeUInt32LE(sampleRate, 24);\n buffer.writeUInt32LE(sampleRate * 2, 28);\n buffer.writeUInt16LE(2, 32);\n buffer.writeUInt16LE(16, 34);\n buffer.write(\"data\", 36);\n buffer.writeUInt32LE(audio.length * 2, 40);\n\n for (let i = 0; i < audio.length; i++) {\n const s = Math.max(-1, Math.min(1, audio[i]));\n buffer.writeInt16LE(Math.round(s * 32767), 44 + i * 2);\n }\n\n writeFileSync(filename, buffer);\n}\n\nfunction playAudio(audioFile: string): void {\n const platform = process.platform;\n if (platform === \"darwin\") {\n execSync(`afplay \"${audioFile}\"`, { stdio: \"inherit\" });\n } else if (platform === \"linux\") {\n try {\n execSync(`aplay \"${audioFile}\"`, { stdio: \"inherit\" });\n } catch {\n execSync(`paplay \"${audioFile}\"`, { stdio: \"inherit\" });\n }\n } else if (platform === \"win32\") {\n execSync(`powershell -c \"(New-Object Media.SoundPlayer '${audioFile}').PlaySync()\"`, {\n stdio: \"inherit\",\n });\n }\n}\n\nexport const readAloud = defineSkill({\n name: \"read-aloud\",\n description: \"Read text or file content aloud using TTS\",\n version: \"1.0.0\",\n input: ReadAloudInput,\n\n async run({ input, gerbil }) {\n const {\n content,\n isFile,\n voice = \"af_heart\",\n speed = 1.0,\n maxLength = 5000,\n summarizeIfLong = false,\n } = input;\n\n // Determine if content is a file path\n const shouldReadFile = isFile ?? existsSync(content);\n\n let textToRead: string;\n\n if (shouldReadFile) {\n if (!existsSync(content)) {\n throw new Error(`File not found: ${content}`);\n }\n textToRead = readFileSync(content, \"utf-8\");\n } else {\n textToRead = content;\n }\n\n // Handle long content\n if (textToRead.length > maxLength) {\n if (summarizeIfLong) {\n // Use AI to summarize\n const summary = await gerbil.generate(\n `Summarize this content in a way that's good for reading aloud (2-3 paragraphs max):\\n\\n${textToRead.slice(0, 10000)}`,\n {\n system: \"You are a skilled narrator. Create a clear, spoken-word friendly summary.\",\n maxTokens: 500,\n temperature: 0.3,\n },\n );\n textToRead = summary.text;\n } else {\n // Truncate\n textToRead = textToRead.slice(0, maxLength) + \"...\";\n }\n }\n\n // Generate and play speech using streaming for long content\n const sentences = textToRead.split(/(?<=[.!?])\\s+/).filter((s) => s.trim());\n let totalDuration = 0;\n\n for (const sentence of sentences) {\n if (!sentence.trim()) continue;\n\n const result = await gerbil.speak(sentence, { voice, speed });\n totalDuration += result.duration;\n\n // Play this chunk\n const tempFile = join(tmpdir(), `gerbil-read-${Date.now()}.wav`);\n saveWav(tempFile, result.audio, result.sampleRate);\n\n try {\n playAudio(tempFile);\n } finally {\n try {\n unlinkSync(tempFile);\n } catch {\n // Ignore\n }\n }\n }\n\n const charCount = textToRead.length;\n const source = shouldReadFile ? `file \"${content}\"` : \"text\";\n\n return `Read ${charCount} characters from ${source} (${totalDuration.toFixed(1)}s audio)`;\n },\n});\n","/**\n * Review Skill\n *\n * Code review with configurable focus areas.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst ReviewInput = z.object({\n /** Code to review */\n code: z.string(),\n\n /** Focus areas */\n focus: z.array(z.enum([\"security\", \"performance\", \"style\", \"bugs\", \"all\"])).default([\"all\"]),\n\n /** Output format */\n format: z.enum([\"inline\", \"summary\", \"detailed\"]).default(\"summary\"),\n});\n\nexport type ReviewInput = z.infer<typeof ReviewInput>;\n\nexport const review = defineSkill({\n name: \"review\",\n description: \"Code review with configurable focus areas\",\n version: \"1.0.0\",\n input: ReviewInput,\n temperature: 0.3,\n maxTokens: 600,\n\n async run({ input, gerbil }) {\n const { code, focus = [\"all\"], format = \"summary\" } = input;\n\n let systemPrompt = `You are a senior code reviewer.\nReview the code for:`;\n\n if (focus.includes(\"all\")) {\n systemPrompt +=\n \"\\n- Security vulnerabilities\\n- Performance issues\\n- Code style and readability\\n- Potential bugs\";\n } else {\n if (focus.includes(\"security\")) {\n systemPrompt += \"\\n- Security vulnerabilities\";\n }\n if (focus.includes(\"performance\")) {\n systemPrompt += \"\\n- Performance issues\";\n }\n if (focus.includes(\"style\")) {\n systemPrompt += \"\\n- Code style and readability\";\n }\n if (focus.includes(\"bugs\")) {\n systemPrompt += \"\\n- Potential bugs\";\n }\n }\n\n if (format === \"summary\") {\n systemPrompt += \"\\n\\nProvide a brief summary of issues found.\";\n } else if (format === \"detailed\") {\n systemPrompt += \"\\n\\nProvide detailed feedback with suggestions.\";\n } else {\n systemPrompt += \"\\n\\nProvide inline-style comments.\";\n }\n\n const result = await gerbil.generate(`Review this code:\\n\\n${code}`, {\n system: systemPrompt,\n maxTokens: this.maxTokens,\n temperature: this.temperature,\n });\n\n return result.text;\n },\n});\n","/**\n * Speak Skill\n *\n * Convert text to speech using on-device TTS.\n */\n\nimport { execSync } from \"child_process\";\nimport { unlinkSync, writeFileSync } from \"fs\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst SpeakInput = z.object({\n /** Text to speak */\n text: z.string(),\n\n /** Voice ID (default: af_heart) */\n voice: z\n .enum([\n \"af_heart\",\n \"af_bella\",\n \"af_nicole\",\n \"af_sarah\",\n \"am_fenrir\",\n \"am_michael\",\n \"bf_emma\",\n \"bf_isabella\",\n \"bm_george\",\n \"bm_lewis\",\n ])\n .default(\"af_heart\"),\n\n /** Speech speed 0.5-2.0 (default: 1.0) */\n speed: z.number().min(0.5).max(2.0).default(1.0),\n\n /** Save to file instead of playing */\n output: z.string().optional(),\n});\n\nexport type SpeakInput = z.infer<typeof SpeakInput>;\n\n/**\n * Save Float32Array as WAV file\n */\nfunction saveWav(filename: string, audio: Float32Array, sampleRate: number): void {\n const buffer = Buffer.alloc(44 + audio.length * 2);\n\n // WAV header\n buffer.write(\"RIFF\", 0);\n buffer.writeUInt32LE(36 + audio.length * 2, 4);\n buffer.write(\"WAVE\", 8);\n buffer.write(\"fmt \", 12);\n buffer.writeUInt32LE(16, 16);\n buffer.writeUInt16LE(1, 20);\n buffer.writeUInt16LE(1, 22);\n buffer.writeUInt32LE(sampleRate, 24);\n buffer.writeUInt32LE(sampleRate * 2, 28);\n buffer.writeUInt16LE(2, 32);\n buffer.writeUInt16LE(16, 34);\n buffer.write(\"data\", 36);\n buffer.writeUInt32LE(audio.length * 2, 40);\n\n for (let i = 0; i < audio.length; i++) {\n const s = Math.max(-1, Math.min(1, audio[i]));\n buffer.writeInt16LE(Math.round(s * 32767), 44 + i * 2);\n }\n\n writeFileSync(filename, buffer);\n}\n\nexport const speak = defineSkill({\n name: \"speak\",\n description: \"Convert text to speech using on-device TTS (Kokoro-82M)\",\n version: \"1.0.0\",\n input: SpeakInput,\n\n async run({ input, gerbil }) {\n const { text, voice = \"af_heart\", speed = 1.0, output } = input;\n\n // Generate speech\n const result = await gerbil.speak(text, { voice, speed });\n\n if (output) {\n // Save to specified file\n saveWav(output, result.audio, result.sampleRate);\n return `Saved ${result.duration.toFixed(1)}s of audio to ${output}`;\n }\n\n // Play audio using system player\n const tempFile = join(tmpdir(), `gerbil-speak-${Date.now()}.wav`);\n saveWav(tempFile, result.audio, result.sampleRate);\n\n try {\n // Detect platform and use appropriate player\n const platform = process.platform;\n if (platform === \"darwin\") {\n execSync(`afplay \"${tempFile}\"`, { stdio: \"inherit\" });\n } else if (platform === \"linux\") {\n // Try common Linux audio players\n try {\n execSync(`aplay \"${tempFile}\"`, { stdio: \"inherit\" });\n } catch {\n try {\n execSync(`paplay \"${tempFile}\"`, { stdio: \"inherit\" });\n } catch {\n execSync(`play \"${tempFile}\"`, { stdio: \"inherit\" });\n }\n }\n } else if (platform === \"win32\") {\n execSync(`powershell -c \"(New-Object Media.SoundPlayer '${tempFile}').PlaySync()\"`, {\n stdio: \"inherit\",\n });\n }\n } finally {\n // Clean up temp file\n try {\n unlinkSync(tempFile);\n } catch {\n // Ignore cleanup errors\n }\n }\n\n return `Spoke ${result.duration.toFixed(1)}s of audio (voice: ${voice}, speed: ${speed}x)`;\n },\n});\n","/**\n * Summarize Skill\n *\n * Summarize content in various lengths and formats.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst SummarizeInput = z.object({\n /** Content to summarize */\n content: z.string(),\n\n /** Summary length */\n length: z.enum([\"short\", \"medium\", \"long\"]).default(\"medium\"),\n\n /** Output format */\n format: z.enum([\"paragraph\", \"bullets\"]).default(\"paragraph\"),\n\n /** Focus areas */\n focus: z.array(z.string()).optional(),\n});\n\nexport type SummarizeInput = z.infer<typeof SummarizeInput>;\n\nconst lengthGuide = {\n short: \"2-3 sentences\",\n medium: \"1 paragraph (4-6 sentences)\",\n long: \"2-3 paragraphs\",\n};\n\nconst maxTokensGuide = {\n short: 100,\n medium: 200,\n long: 400,\n};\n\nexport const summarize = defineSkill({\n name: \"summarize\",\n description: \"Summarize content in various lengths and formats\",\n version: \"1.0.0\",\n input: SummarizeInput,\n temperature: 0.3,\n\n async run({ input, gerbil }) {\n const { content, length = \"medium\", format = \"paragraph\", focus } = input;\n\n let systemPrompt = `You are a summarization expert.\nSummarize the content in ${lengthGuide[length]}.`;\n\n if (format === \"bullets\") {\n systemPrompt += \"\\nUse bullet points.\";\n }\n\n if (focus && focus.length > 0) {\n systemPrompt += `\\nFocus on: ${focus.join(\", \")}.`;\n }\n\n systemPrompt += \"\\nOnly output the summary, nothing else.\";\n\n const result = await gerbil.generate(`Summarize this:\\n\\n${content}`, {\n system: systemPrompt,\n maxTokens: maxTokensGuide[length],\n temperature: this.temperature,\n });\n\n return result.text;\n },\n});\n","/**\n * Test Skill\n *\n * Generate tests for code.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst TestInput = z.object({\n /** Code to test */\n code: z.string(),\n\n /** Test framework */\n framework: z.enum([\"jest\", \"vitest\", \"mocha\", \"playwright\"]).default(\"vitest\"),\n\n /** Test style */\n style: z.enum([\"unit\", \"integration\", \"e2e\"]).default(\"unit\"),\n});\n\nexport type TestInput = z.infer<typeof TestInput>;\n\nexport const test = defineSkill({\n name: \"test\",\n description: \"Generate tests for code\",\n version: \"1.0.0\",\n input: TestInput,\n temperature: 0.3,\n maxTokens: 800,\n\n async run({ input, gerbil }) {\n const { code, framework, style } = input;\n\n const systemPrompt = `You are a test engineer.\nGenerate ${style} tests using ${framework} for the provided code.\nInclude edge cases and error scenarios.\nOnly output the test code, no explanations.`;\n\n const result = await gerbil.generate(`Generate tests for:\\n\\n${code}`, {\n system: systemPrompt,\n maxTokens: this.maxTokens,\n temperature: this.temperature,\n });\n\n return result.text;\n },\n});\n","/**\n * Title Skill\n *\n * Generate titles for content.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst TitleInput = z.object({\n /** Content to generate title for */\n content: z.string(),\n\n /** Title style */\n style: z.enum([\"professional\", \"clickbait\", \"seo\", \"simple\"]).default(\"professional\"),\n\n /** Max length */\n maxLength: z.number().default(60),\n});\n\nexport type TitleInput = z.infer<typeof TitleInput>;\n\nconst styleGuide = {\n professional: \"Clear, informative, and professional\",\n clickbait: \"Engaging and curiosity-inducing\",\n seo: \"SEO-optimized with relevant keywords\",\n simple: \"Simple and straightforward\",\n};\n\nexport const title = defineSkill({\n name: \"title\",\n description: \"Generate titles for content\",\n version: \"1.0.0\",\n input: TitleInput,\n temperature: 0.7,\n maxTokens: 30,\n\n async run({ input, gerbil }) {\n const { content, style = \"professional\", maxLength = 60 } = input;\n\n const systemPrompt = `Generate a title for the content.\nStyle: ${styleGuide[style]}\nMax length: ${maxLength} characters\nOnly output the title, nothing else.`;\n\n const result = await gerbil.generate(`Generate a title for:\\n\\n${content.substring(0, 1000)}`, {\n system: systemPrompt,\n maxTokens: this.maxTokens,\n temperature: this.temperature,\n });\n\n let generatedTitle = result.text.trim();\n generatedTitle = generatedTitle.replace(/^[\"']|[\"']$/g, \"\");\n\n if (generatedTitle.length > maxLength) {\n generatedTitle = `${generatedTitle.substring(0, maxLength - 3)}...`;\n }\n\n return generatedTitle;\n },\n});\n","/**\n * Transcribe Skill\n *\n * Convert audio to text using on-device STT (Whisper).\n */\n\nimport { existsSync, readFileSync, writeFileSync } from \"fs\";\nimport { resolve } from \"path\";\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst TranscribeInput = z.object({\n /** Audio file path (WAV, MP3, etc.) or URL */\n audio: z.string(),\n\n /** STT model ID (default: whisper-tiny.en) */\n model: z\n .enum([\"whisper-tiny.en\", \"whisper-base.en\", \"whisper-small.en\", \"whisper-large-v3-turbo\"])\n .default(\"whisper-tiny.en\"),\n\n /** Language hint for multilingual models */\n language: z.string().optional(),\n\n /** Include timestamps in output */\n timestamps: z.boolean().default(false),\n\n /** Save transcription to file */\n output: z.string().optional(),\n});\n\nexport type TranscribeInput = z.infer<typeof TranscribeInput>;\n\nexport const transcribe = defineSkill({\n name: \"transcribe\",\n description: \"Transcribe audio to text using on-device STT (Whisper)\",\n version: \"1.0.0\",\n input: TranscribeInput,\n\n async run({ input, gerbil }) {\n const { audio, model = \"whisper-tiny.en\", language, timestamps = false, output } = input;\n\n // Load audio file\n const filePath = resolve(audio);\n if (!existsSync(filePath)) {\n throw new Error(`Audio file not found: ${filePath}`);\n }\n\n const audioData = new Uint8Array(readFileSync(filePath));\n\n // Load the specified STT model\n await gerbil.loadSTT(model);\n\n // Transcribe\n const result = await gerbil.transcribe(audioData, {\n language,\n timestamps,\n });\n\n // Format output\n let text: string;\n if (timestamps && result.segments) {\n text = result.segments\n .map((seg) => `[${seg.start.toFixed(1)}s - ${seg.end.toFixed(1)}s] ${seg.text}`)\n .join(\"\\n\");\n } else {\n text = result.text;\n }\n\n // Save to file if requested\n if (output) {\n writeFileSync(output, text);\n return `Transcribed ${result.duration.toFixed(1)}s of audio to ${output}`;\n }\n\n return text;\n },\n});\n","/**\n * Translate Skill\n *\n * Translate text between languages.\n */\n\nimport { z } from \"zod\";\nimport { defineSkill } from \"../registry.js\";\n\nconst TranslateInput = z.object({\n /** Text to translate */\n text: z.string(),\n\n /** Source language (auto-detected if not provided) */\n from: z.string().optional(),\n\n /** Target language */\n to: z.string(),\n\n /** Preserve formatting */\n preserveFormatting: z.boolean().default(true),\n});\n\nexport type TranslateInput = z.infer<typeof TranslateInput>;\n\nexport const translate = defineSkill({\n name: \"translate\",\n description: \"Translate text between languages\",\n version: \"1.0.0\",\n input: TranslateInput,\n temperature: 0.3,\n\n async run({ input, gerbil }) {\n const { text, from, to, preserveFormatting } = input;\n\n let systemPrompt = `You are a professional translator.\nTranslate the text to ${to}.`;\n\n if (from) {\n systemPrompt += `\\nThe source language is ${from}.`;\n }\n\n if (preserveFormatting) {\n systemPrompt += \"\\nPreserve the original formatting (paragraphs, lists, etc.).\";\n }\n\n systemPrompt += \"\\nOnly output the translation, nothing else.\";\n\n const result = await gerbil.generate(`Translate:\\n\\n${text}`, {\n system: systemPrompt,\n maxTokens: Math.ceil(text.length / 2),\n temperature: this.temperature,\n });\n\n return result.text;\n },\n});\n"],"mappings":"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"}
@@ -234,13 +234,7 @@ var WhisperSTT = class {
234
234
  const { onProgress, device = "auto" } = options;
235
235
  onProgress?.({ status: "Loading transformers.js..." });
236
236
  const isNode = typeof process !== "undefined" && process.versions?.node;
237
- let transformers;
238
- if (isNode) transformers = await import(
239
- /* webpackIgnore: true */
240
- "@huggingface/transformers"
241
- );
242
- else transformers = await import("https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.8.1");
243
- const { pipeline, env } = transformers;
237
+ const { pipeline, env } = await import("@huggingface/transformers");
244
238
  if (isNode) {
245
239
  env.allowLocalModels = true;
246
240
  env.allowRemoteModels = true;
@@ -436,4 +430,4 @@ var WhisperSTT = class {
436
430
 
437
431
  //#endregion
438
432
  export { resampleAudio as i, WhisperSTT as n, decodeWav as r, WHISPER_MODELS as t };
439
- //# sourceMappingURL=stt-CkfJswka.mjs.map
433
+ //# sourceMappingURL=stt-CpLYbGFd.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stt-CpLYbGFd.mjs","names":["WHISPER_MODELS: STTModelConfig[]","tfDevice: \"webgpu\" | \"cpu\" | \"wasm\"","audioData: Float32Array","pipelineOptions: any","transcribeResult: TranscribeResult","audioBuffer: Float32Array[]","intervalId: ReturnType<typeof setInterval> | null","e: any"],"sources":["../src/core/stt.ts"],"sourcesContent":["/**\n * Speech-to-Text with Whisper\n *\n * Provides local speech recognition using Whisper ONNX models via transformers.js.\n * Supports multiple model sizes and languages.\n *\n * @example\n * ```ts\n * const stt = new WhisperSTT();\n * await stt.load({ onProgress: (p) => console.log(p.status) });\n *\n * // Transcribe audio (Float32Array at 16kHz)\n * const result = await stt.transcribe(audioData);\n * console.log(result.text);\n *\n * // With timestamps\n * const result = await stt.transcribe(audioData, { timestamps: true });\n * for (const seg of result.segments) {\n * console.log(`[${seg.start.toFixed(1)}s] ${seg.text}`);\n * }\n * ```\n */\n\nimport type {\n LoadSTTOptions,\n ProgressInfo,\n STTModelConfig,\n StreamingTranscriptionOptions,\n StreamingTranscriptionSession,\n TranscribeOptions,\n TranscribeResult,\n TranscribeSegment,\n} from \"./types.js\";\n\n// ============================================\n// Model Registry\n// ============================================\n\n/**\n * Available Whisper models\n * Ordered by size (smallest first)\n */\nexport const WHISPER_MODELS: STTModelConfig[] = [\n {\n id: \"whisper-tiny.en\",\n repo: \"onnx-community/whisper-tiny.en\",\n description: \"Tiny English-only model, fastest\",\n size: \"39M\",\n multilingual: false,\n languages: [\"en\"],\n sampleRate: 16000,\n },\n {\n id: \"whisper-tiny\",\n repo: \"onnx-community/whisper-tiny\",\n description: \"Tiny multilingual model\",\n size: \"39M\",\n multilingual: true,\n languages: [\"en\", \"es\", \"fr\", \"de\", \"it\", \"pt\", \"nl\", \"ru\", \"zh\", \"ja\", \"ko\"],\n sampleRate: 16000,\n },\n {\n id: \"whisper-base.en\",\n repo: \"onnx-community/whisper-base.en\",\n description: \"Base English-only model, good balance\",\n size: \"74M\",\n multilingual: false,\n languages: [\"en\"],\n sampleRate: 16000,\n },\n {\n id: \"whisper-base\",\n repo: \"onnx-community/whisper-base\",\n description: \"Base multilingual model\",\n size: \"74M\",\n multilingual: true,\n languages: [\"en\", \"es\", \"fr\", \"de\", \"it\", \"pt\", \"nl\", \"ru\", \"zh\", \"ja\", \"ko\"],\n sampleRate: 16000,\n },\n {\n id: \"whisper-small.en\",\n repo: \"onnx-community/whisper-small.en\",\n description: \"Small English-only model, high quality\",\n size: \"244M\",\n multilingual: false,\n languages: [\"en\"],\n sampleRate: 16000,\n },\n {\n id: \"whisper-small\",\n repo: \"onnx-community/whisper-small\",\n description: \"Small multilingual model\",\n size: \"244M\",\n multilingual: true,\n languages: [\"en\", \"es\", \"fr\", \"de\", \"it\", \"pt\", \"nl\", \"ru\", \"zh\", \"ja\", \"ko\"],\n sampleRate: 16000,\n },\n {\n id: \"whisper-large-v3-turbo\",\n repo: \"onnx-community/whisper-large-v3-turbo\",\n description: \"Large Turbo model, 5.4x faster, 80+ languages\",\n size: \"809M\",\n multilingual: true,\n languages: [\n \"en\",\n \"es\",\n \"fr\",\n \"de\",\n \"it\",\n \"pt\",\n \"nl\",\n \"ru\",\n \"zh\",\n \"ja\",\n \"ko\",\n \"ar\",\n \"hi\",\n \"vi\",\n \"th\",\n ],\n sampleRate: 16000,\n },\n];\n\n// Default model\nconst DEFAULT_MODEL = \"whisper-tiny.en\";\n\n// ============================================\n// Audio Utilities\n// ============================================\n\n/**\n * Decode WAV file to Float32Array\n * Handles stereo to mono conversion\n */\nexport function decodeWav(buffer: Uint8Array): { audio: Float32Array; sampleRate: number } {\n const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);\n\n // Validate RIFF header\n const riff = String.fromCharCode(buffer[0], buffer[1], buffer[2], buffer[3]);\n if (riff !== \"RIFF\") {\n throw new Error(\"Invalid WAV file: missing RIFF header\");\n }\n\n // Get format details\n const numChannels = view.getUint16(22, true);\n const sampleRate = view.getUint32(24, true);\n const bitsPerSample = view.getUint16(34, true);\n\n if (bitsPerSample !== 16) {\n throw new Error(`Unsupported bit depth: ${bitsPerSample}. Only 16-bit WAV is supported.`);\n }\n\n // Find data chunk\n let dataOffset = 12;\n while (dataOffset < buffer.length - 8) {\n const chunkId = String.fromCharCode(\n buffer[dataOffset],\n buffer[dataOffset + 1],\n buffer[dataOffset + 2],\n buffer[dataOffset + 3],\n );\n const chunkSize = view.getUint32(dataOffset + 4, true);\n if (chunkId === \"data\") {\n dataOffset += 8;\n break;\n }\n dataOffset += 8 + chunkSize;\n }\n\n const dataSize = buffer.length - dataOffset;\n const bytesPerSample = bitsPerSample / 8;\n const totalSamples = Math.floor(dataSize / bytesPerSample);\n const samplesPerChannel = Math.floor(totalSamples / numChannels);\n\n // Convert to mono Float32Array\n const audio = new Float32Array(samplesPerChannel);\n\n for (let i = 0; i < samplesPerChannel; i++) {\n if (numChannels === 2) {\n const left = view.getInt16(dataOffset + i * 4, true);\n const right = view.getInt16(dataOffset + i * 4 + 2, true);\n audio[i] = (left + right) / 2 / 32768;\n } else {\n const sample = view.getInt16(dataOffset + i * 2, true);\n audio[i] = sample / 32768;\n }\n }\n\n return { audio, sampleRate };\n}\n\n/**\n * Resample audio to target sample rate using linear interpolation\n */\nexport function resampleAudio(audio: Float32Array, fromRate: number, toRate: number): Float32Array {\n if (fromRate === toRate) return audio;\n\n const ratio = toRate / fromRate;\n const newLength = Math.round(audio.length * ratio);\n const result = new Float32Array(newLength);\n\n for (let i = 0; i < newLength; i++) {\n const srcIndex = i / ratio;\n const floor = Math.floor(srcIndex);\n const ceil = Math.min(floor + 1, audio.length - 1);\n const t = srcIndex - floor;\n result[i] = audio[floor] * (1 - t) + audio[ceil] * t;\n }\n\n return result;\n}\n\n// ============================================\n// WhisperSTT Class\n// ============================================\n\n/**\n * Speech-to-Text using Whisper ONNX models\n */\nexport class WhisperSTT {\n private modelConfig: STTModelConfig;\n private pipeline: any = null;\n private loadPromise: Promise<void> | null = null;\n private _isLoaded = false;\n private _deviceMode: \"webgpu\" | \"cpu\" = \"cpu\";\n\n constructor(modelId: string = DEFAULT_MODEL) {\n const config = WHISPER_MODELS.find((m) => m.id === modelId);\n if (!config) {\n const available = WHISPER_MODELS.map((m) => m.id).join(\", \");\n throw new Error(`Unknown STT model: ${modelId}. Available: ${available}`);\n }\n this.modelConfig = config;\n }\n\n /**\n * Check if model is loaded\n */\n isLoaded(): boolean {\n return this._isLoaded;\n }\n\n /**\n * Get model configuration\n */\n getModelConfig(): STTModelConfig {\n return this.modelConfig;\n }\n\n /**\n * Get model info (alias for getModelConfig)\n */\n getModelInfo(): STTModelConfig {\n return this.modelConfig;\n }\n\n /**\n * Get current device mode\n */\n getDeviceMode(): \"webgpu\" | \"cpu\" {\n return this._deviceMode;\n }\n\n /**\n * List available models\n */\n static listModels(): STTModelConfig[] {\n return [...WHISPER_MODELS];\n }\n\n /**\n * Load the STT model\n */\n async load(options: LoadSTTOptions = {}): Promise<void> {\n if (this._isLoaded) return;\n if (this.loadPromise) {\n await this.loadPromise;\n return;\n }\n\n this.loadPromise = this._load(options);\n await this.loadPromise;\n }\n\n private async _load(options: LoadSTTOptions = {}): Promise<void> {\n const { onProgress, device = \"auto\" } = options;\n\n onProgress?.({ status: \"Loading transformers.js...\" });\n\n // Check if we're in Node.js or browser\n const isNode = typeof process !== \"undefined\" && process.versions?.node;\n\n // Import transformers.js dynamically\n // tsdown handles resolution: Node.js = external, Browser = bundled\n const { pipeline, env } = await import(\"@huggingface/transformers\");\n\n // Configure environment based on runtime\n if (isNode) {\n // Node.js: allow local models (for CLI/server use)\n env.allowLocalModels = true;\n env.allowRemoteModels = true;\n } else {\n // Browser: use IndexedDB cache, fetch from HuggingFace CDN\n env.useBrowserCache = true;\n env.allowLocalModels = false;\n }\n\n // Determine device\n // Note: Whisper ONNX models work best with fp32 on CPU/WASM\n // WebGPU support for ASR is limited, so we use CPU for reliability\n let tfDevice: \"webgpu\" | \"cpu\" | \"wasm\" = \"cpu\";\n\n // In browser, use WASM for better compatibility\n if (!isNode) {\n tfDevice = \"wasm\";\n }\n\n // Store device mode\n this._deviceMode = \"cpu\"; // STT always reports as CPU since WASM is CPU-based\n\n onProgress?.({ status: `Loading ${this.modelConfig.id}...` });\n\n // Load the ASR pipeline\n // Always use fp32 for Whisper models (fp16 not available for ONNX ASR)\n this.pipeline = await pipeline(\"automatic-speech-recognition\", this.modelConfig.repo, {\n dtype: \"fp32\",\n device: tfDevice,\n progress_callback: (progress: any) => {\n if (progress.status === \"progress\" && progress.file) {\n onProgress?.({\n status: `Downloading ${progress.file}`,\n progress: Math.round(progress.progress || 0),\n file: progress.file,\n });\n }\n },\n });\n\n this._isLoaded = true;\n onProgress?.({ status: `Ready (${tfDevice.toUpperCase()})!` });\n }\n\n /**\n * Transcribe audio to text\n *\n * @param audio - Audio data as Float32Array (mono, 16kHz preferred) or Uint8Array (WAV file)\n * @param options - Transcription options\n * @returns Transcription result with text and optional timestamps\n */\n async transcribe(\n audio: Float32Array | Uint8Array,\n options: TranscribeOptions = {},\n ): Promise<TranscribeResult> {\n if (!this._isLoaded) {\n throw new Error(\"STT model not loaded. Call load() first.\");\n }\n\n const { language, timestamps = false, onProgress } = options;\n const startTime = performance.now();\n\n // Convert Uint8Array (WAV) to Float32Array\n let audioData: Float32Array;\n let inputSampleRate = 16000;\n\n if (audio instanceof Uint8Array) {\n onProgress?.({ status: \"Decoding audio...\" });\n const decoded = decodeWav(audio);\n audioData = decoded.audio;\n inputSampleRate = decoded.sampleRate;\n } else {\n audioData = audio;\n }\n\n // Resample to 16kHz if needed\n if (inputSampleRate !== 16000) {\n onProgress?.({ status: \"Resampling to 16kHz...\" });\n audioData = resampleAudio(audioData, inputSampleRate, 16000);\n }\n\n const audioDuration = audioData.length / 16000;\n onProgress?.({ status: `Transcribing ${audioDuration.toFixed(1)}s of audio...` });\n\n // Build pipeline options\n const pipelineOptions: any = {};\n\n // Only set language for multilingual models\n if (language && this.modelConfig.multilingual) {\n pipelineOptions.language = language;\n pipelineOptions.task = \"transcribe\";\n }\n\n // Enable timestamps if requested\n if (timestamps) {\n pipelineOptions.return_timestamps = true;\n }\n\n // Run transcription\n const result = await this.pipeline(audioData, pipelineOptions);\n\n const totalTime = performance.now() - startTime;\n\n // Build result\n let text = result.text?.trim() || \"\";\n\n // Filter out Whisper artifacts\n if (text === \"[BLANK_AUDIO]\" || text === \"(blank audio)\" || text === \"[BLANK AUDIO]\") {\n text = \"\";\n }\n\n const transcribeResult: TranscribeResult = {\n text,\n language: language || (this.modelConfig.multilingual ? \"auto\" : \"en\"),\n duration: audioDuration,\n totalTime,\n };\n\n // Add segments if timestamps were requested\n if (timestamps && result.chunks) {\n transcribeResult.segments = result.chunks.map(\n (chunk: any): TranscribeSegment => ({\n text: chunk.text?.trim() || \"\",\n start: chunk.timestamp?.[0] || 0,\n end: chunk.timestamp?.[1] || 0,\n }),\n );\n }\n\n onProgress?.({ status: \"Done!\" });\n\n return transcribeResult;\n }\n\n /**\n * Create a streaming transcription session\n *\n * Transcribes audio in real-time by processing chunks at regular intervals.\n * Perfect for live captioning, call transcription, or real-time subtitles.\n *\n * @param options - Streaming options\n * @returns Streaming session controller\n *\n * @example\n * ```ts\n * const session = stt.createStreamingSession({\n * chunkDuration: 3000, // Transcribe every 3 seconds\n * onChunk: (text, idx) => console.log(`Chunk ${idx}: ${text}`),\n * onTranscript: (fullText) => console.log(\"Full:\", fullText),\n * });\n *\n * // Feed audio data as it comes in (Float32Array at 16kHz)\n * session.feedAudio(audioChunk);\n *\n * // Or manually trigger transcription\n * await session.flush();\n *\n * // Stop and get final transcript\n * const finalText = await session.stop();\n * ```\n */\n createStreamingSession(\n options: StreamingTranscriptionOptions = {},\n ): StreamingTranscriptionSession {\n const {\n chunkDuration = 3000,\n minChunkSize = 8000, // ~0.5 seconds at 16kHz\n onChunk,\n onTranscript,\n onError,\n language,\n } = options;\n\n let audioBuffer: Float32Array[] = [];\n let fullTranscript = \"\";\n let chunkIndex = 0;\n let intervalId: ReturnType<typeof setInterval> | null = null;\n let isRunning = false;\n\n const getBufferSize = (): number => {\n return audioBuffer.reduce((sum, chunk) => sum + chunk.length, 0);\n };\n\n const mergeBuffer = (): Float32Array => {\n const totalLength = getBufferSize();\n const merged = new Float32Array(totalLength);\n let offset = 0;\n for (const chunk of audioBuffer) {\n merged.set(chunk, offset);\n offset += chunk.length;\n }\n return merged;\n };\n\n const transcribeBuffer = async (): Promise<string> => {\n if (!this._isLoaded || getBufferSize() < minChunkSize) {\n return \"\";\n }\n\n const audio = mergeBuffer();\n audioBuffer = []; // Clear buffer\n\n try {\n const result = await this.transcribe(audio, { language });\n const text = result.text.trim();\n\n if (text) {\n chunkIndex++;\n onChunk?.(text, chunkIndex);\n\n // Append to full transcript\n fullTranscript = fullTranscript + (fullTranscript ? \" \" : \"\") + text;\n onTranscript?.(fullTranscript);\n }\n\n return text;\n } catch (e: any) {\n onError?.(e.message || \"Transcription failed\");\n return \"\";\n }\n };\n\n let aborted = false;\n\n const session: StreamingTranscriptionSession = {\n feedAudio: (audio: Float32Array) => {\n if (!aborted) {\n audioBuffer.push(audio);\n }\n },\n\n flush: async () => {\n if (aborted) return \"\";\n return transcribeBuffer();\n },\n\n start: () => {\n if (isRunning || aborted) return;\n isRunning = true;\n\n intervalId = setInterval(async () => {\n if (isRunning && !aborted) {\n await transcribeBuffer();\n }\n }, chunkDuration);\n },\n\n stop: async () => {\n isRunning = false;\n\n if (intervalId) {\n clearInterval(intervalId);\n intervalId = null;\n }\n\n // Transcribe any remaining audio (unless aborted)\n if (!aborted && getBufferSize() >= minChunkSize) {\n await transcribeBuffer();\n }\n\n return fullTranscript;\n },\n\n abort: () => {\n // Immediately stop without final transcription\n aborted = true;\n isRunning = false;\n\n if (intervalId) {\n clearInterval(intervalId);\n intervalId = null;\n }\n\n audioBuffer = [];\n },\n\n isRunning: () => isRunning,\n\n getTranscript: () => fullTranscript,\n\n getChunkCount: () => chunkIndex,\n\n reset: () => {\n audioBuffer = [];\n fullTranscript = \"\";\n chunkIndex = 0;\n },\n };\n\n return session;\n }\n\n /**\n * Dispose of resources\n */\n dispose(): void {\n this.pipeline = null;\n this._isLoaded = false;\n this.loadPromise = null;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { i as resampleAudio, n as WhisperSTT, r as decodeWav, t as WHISPER_MODELS } from "./stt-CpLYbGFd.mjs";
2
+
3
+ export { WhisperSTT };
@@ -234,13 +234,7 @@ var WhisperSTT = class {
234
234
  const { onProgress, device = "auto" } = options;
235
235
  onProgress?.({ status: "Loading transformers.js..." });
236
236
  const isNode = typeof process !== "undefined" && process.versions?.node;
237
- let transformers;
238
- if (isNode) transformers = await import(
239
- /* webpackIgnore: true */
240
- "@huggingface/transformers"
241
- );
242
- else transformers = await import("https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.8.1");
243
- const { pipeline, env } = transformers;
237
+ const { pipeline, env } = await import("./transformers.web-DokyH3rP.js");
244
238
  if (isNode) {
245
239
  env.allowLocalModels = true;
246
240
  env.allowRemoteModels = true;
@@ -436,4 +430,4 @@ var WhisperSTT = class {
436
430
 
437
431
  //#endregion
438
432
  export { WhisperSTT };
439
- //# sourceMappingURL=stt-BtklAjR2.js.map
433
+ //# sourceMappingURL=stt-Te8Qz-Ay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stt-Te8Qz-Ay.js","names":["WHISPER_MODELS: STTModelConfig[]","tfDevice: \"webgpu\" | \"cpu\" | \"wasm\"","audioData: Float32Array","pipelineOptions: any","transcribeResult: TranscribeResult","audioBuffer: Float32Array[]","intervalId: ReturnType<typeof setInterval> | null","e: any"],"sources":["../src/core/stt.ts"],"sourcesContent":["/**\n * Speech-to-Text with Whisper\n *\n * Provides local speech recognition using Whisper ONNX models via transformers.js.\n * Supports multiple model sizes and languages.\n *\n * @example\n * ```ts\n * const stt = new WhisperSTT();\n * await stt.load({ onProgress: (p) => console.log(p.status) });\n *\n * // Transcribe audio (Float32Array at 16kHz)\n * const result = await stt.transcribe(audioData);\n * console.log(result.text);\n *\n * // With timestamps\n * const result = await stt.transcribe(audioData, { timestamps: true });\n * for (const seg of result.segments) {\n * console.log(`[${seg.start.toFixed(1)}s] ${seg.text}`);\n * }\n * ```\n */\n\nimport type {\n LoadSTTOptions,\n ProgressInfo,\n STTModelConfig,\n StreamingTranscriptionOptions,\n StreamingTranscriptionSession,\n TranscribeOptions,\n TranscribeResult,\n TranscribeSegment,\n} from \"./types.js\";\n\n// ============================================\n// Model Registry\n// ============================================\n\n/**\n * Available Whisper models\n * Ordered by size (smallest first)\n */\nexport const WHISPER_MODELS: STTModelConfig[] = [\n {\n id: \"whisper-tiny.en\",\n repo: \"onnx-community/whisper-tiny.en\",\n description: \"Tiny English-only model, fastest\",\n size: \"39M\",\n multilingual: false,\n languages: [\"en\"],\n sampleRate: 16000,\n },\n {\n id: \"whisper-tiny\",\n repo: \"onnx-community/whisper-tiny\",\n description: \"Tiny multilingual model\",\n size: \"39M\",\n multilingual: true,\n languages: [\"en\", \"es\", \"fr\", \"de\", \"it\", \"pt\", \"nl\", \"ru\", \"zh\", \"ja\", \"ko\"],\n sampleRate: 16000,\n },\n {\n id: \"whisper-base.en\",\n repo: \"onnx-community/whisper-base.en\",\n description: \"Base English-only model, good balance\",\n size: \"74M\",\n multilingual: false,\n languages: [\"en\"],\n sampleRate: 16000,\n },\n {\n id: \"whisper-base\",\n repo: \"onnx-community/whisper-base\",\n description: \"Base multilingual model\",\n size: \"74M\",\n multilingual: true,\n languages: [\"en\", \"es\", \"fr\", \"de\", \"it\", \"pt\", \"nl\", \"ru\", \"zh\", \"ja\", \"ko\"],\n sampleRate: 16000,\n },\n {\n id: \"whisper-small.en\",\n repo: \"onnx-community/whisper-small.en\",\n description: \"Small English-only model, high quality\",\n size: \"244M\",\n multilingual: false,\n languages: [\"en\"],\n sampleRate: 16000,\n },\n {\n id: \"whisper-small\",\n repo: \"onnx-community/whisper-small\",\n description: \"Small multilingual model\",\n size: \"244M\",\n multilingual: true,\n languages: [\"en\", \"es\", \"fr\", \"de\", \"it\", \"pt\", \"nl\", \"ru\", \"zh\", \"ja\", \"ko\"],\n sampleRate: 16000,\n },\n {\n id: \"whisper-large-v3-turbo\",\n repo: \"onnx-community/whisper-large-v3-turbo\",\n description: \"Large Turbo model, 5.4x faster, 80+ languages\",\n size: \"809M\",\n multilingual: true,\n languages: [\n \"en\",\n \"es\",\n \"fr\",\n \"de\",\n \"it\",\n \"pt\",\n \"nl\",\n \"ru\",\n \"zh\",\n \"ja\",\n \"ko\",\n \"ar\",\n \"hi\",\n \"vi\",\n \"th\",\n ],\n sampleRate: 16000,\n },\n];\n\n// Default model\nconst DEFAULT_MODEL = \"whisper-tiny.en\";\n\n// ============================================\n// Audio Utilities\n// ============================================\n\n/**\n * Decode WAV file to Float32Array\n * Handles stereo to mono conversion\n */\nexport function decodeWav(buffer: Uint8Array): { audio: Float32Array; sampleRate: number } {\n const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);\n\n // Validate RIFF header\n const riff = String.fromCharCode(buffer[0], buffer[1], buffer[2], buffer[3]);\n if (riff !== \"RIFF\") {\n throw new Error(\"Invalid WAV file: missing RIFF header\");\n }\n\n // Get format details\n const numChannels = view.getUint16(22, true);\n const sampleRate = view.getUint32(24, true);\n const bitsPerSample = view.getUint16(34, true);\n\n if (bitsPerSample !== 16) {\n throw new Error(`Unsupported bit depth: ${bitsPerSample}. Only 16-bit WAV is supported.`);\n }\n\n // Find data chunk\n let dataOffset = 12;\n while (dataOffset < buffer.length - 8) {\n const chunkId = String.fromCharCode(\n buffer[dataOffset],\n buffer[dataOffset + 1],\n buffer[dataOffset + 2],\n buffer[dataOffset + 3],\n );\n const chunkSize = view.getUint32(dataOffset + 4, true);\n if (chunkId === \"data\") {\n dataOffset += 8;\n break;\n }\n dataOffset += 8 + chunkSize;\n }\n\n const dataSize = buffer.length - dataOffset;\n const bytesPerSample = bitsPerSample / 8;\n const totalSamples = Math.floor(dataSize / bytesPerSample);\n const samplesPerChannel = Math.floor(totalSamples / numChannels);\n\n // Convert to mono Float32Array\n const audio = new Float32Array(samplesPerChannel);\n\n for (let i = 0; i < samplesPerChannel; i++) {\n if (numChannels === 2) {\n const left = view.getInt16(dataOffset + i * 4, true);\n const right = view.getInt16(dataOffset + i * 4 + 2, true);\n audio[i] = (left + right) / 2 / 32768;\n } else {\n const sample = view.getInt16(dataOffset + i * 2, true);\n audio[i] = sample / 32768;\n }\n }\n\n return { audio, sampleRate };\n}\n\n/**\n * Resample audio to target sample rate using linear interpolation\n */\nexport function resampleAudio(audio: Float32Array, fromRate: number, toRate: number): Float32Array {\n if (fromRate === toRate) return audio;\n\n const ratio = toRate / fromRate;\n const newLength = Math.round(audio.length * ratio);\n const result = new Float32Array(newLength);\n\n for (let i = 0; i < newLength; i++) {\n const srcIndex = i / ratio;\n const floor = Math.floor(srcIndex);\n const ceil = Math.min(floor + 1, audio.length - 1);\n const t = srcIndex - floor;\n result[i] = audio[floor] * (1 - t) + audio[ceil] * t;\n }\n\n return result;\n}\n\n// ============================================\n// WhisperSTT Class\n// ============================================\n\n/**\n * Speech-to-Text using Whisper ONNX models\n */\nexport class WhisperSTT {\n private modelConfig: STTModelConfig;\n private pipeline: any = null;\n private loadPromise: Promise<void> | null = null;\n private _isLoaded = false;\n private _deviceMode: \"webgpu\" | \"cpu\" = \"cpu\";\n\n constructor(modelId: string = DEFAULT_MODEL) {\n const config = WHISPER_MODELS.find((m) => m.id === modelId);\n if (!config) {\n const available = WHISPER_MODELS.map((m) => m.id).join(\", \");\n throw new Error(`Unknown STT model: ${modelId}. Available: ${available}`);\n }\n this.modelConfig = config;\n }\n\n /**\n * Check if model is loaded\n */\n isLoaded(): boolean {\n return this._isLoaded;\n }\n\n /**\n * Get model configuration\n */\n getModelConfig(): STTModelConfig {\n return this.modelConfig;\n }\n\n /**\n * Get model info (alias for getModelConfig)\n */\n getModelInfo(): STTModelConfig {\n return this.modelConfig;\n }\n\n /**\n * Get current device mode\n */\n getDeviceMode(): \"webgpu\" | \"cpu\" {\n return this._deviceMode;\n }\n\n /**\n * List available models\n */\n static listModels(): STTModelConfig[] {\n return [...WHISPER_MODELS];\n }\n\n /**\n * Load the STT model\n */\n async load(options: LoadSTTOptions = {}): Promise<void> {\n if (this._isLoaded) return;\n if (this.loadPromise) {\n await this.loadPromise;\n return;\n }\n\n this.loadPromise = this._load(options);\n await this.loadPromise;\n }\n\n private async _load(options: LoadSTTOptions = {}): Promise<void> {\n const { onProgress, device = \"auto\" } = options;\n\n onProgress?.({ status: \"Loading transformers.js...\" });\n\n // Check if we're in Node.js or browser\n const isNode = typeof process !== \"undefined\" && process.versions?.node;\n\n // Import transformers.js dynamically\n // tsdown handles resolution: Node.js = external, Browser = bundled\n const { pipeline, env } = await import(\"@huggingface/transformers\");\n\n // Configure environment based on runtime\n if (isNode) {\n // Node.js: allow local models (for CLI/server use)\n env.allowLocalModels = true;\n env.allowRemoteModels = true;\n } else {\n // Browser: use IndexedDB cache, fetch from HuggingFace CDN\n env.useBrowserCache = true;\n env.allowLocalModels = false;\n }\n\n // Determine device\n // Note: Whisper ONNX models work best with fp32 on CPU/WASM\n // WebGPU support for ASR is limited, so we use CPU for reliability\n let tfDevice: \"webgpu\" | \"cpu\" | \"wasm\" = \"cpu\";\n\n // In browser, use WASM for better compatibility\n if (!isNode) {\n tfDevice = \"wasm\";\n }\n\n // Store device mode\n this._deviceMode = \"cpu\"; // STT always reports as CPU since WASM is CPU-based\n\n onProgress?.({ status: `Loading ${this.modelConfig.id}...` });\n\n // Load the ASR pipeline\n // Always use fp32 for Whisper models (fp16 not available for ONNX ASR)\n this.pipeline = await pipeline(\"automatic-speech-recognition\", this.modelConfig.repo, {\n dtype: \"fp32\",\n device: tfDevice,\n progress_callback: (progress: any) => {\n if (progress.status === \"progress\" && progress.file) {\n onProgress?.({\n status: `Downloading ${progress.file}`,\n progress: Math.round(progress.progress || 0),\n file: progress.file,\n });\n }\n },\n });\n\n this._isLoaded = true;\n onProgress?.({ status: `Ready (${tfDevice.toUpperCase()})!` });\n }\n\n /**\n * Transcribe audio to text\n *\n * @param audio - Audio data as Float32Array (mono, 16kHz preferred) or Uint8Array (WAV file)\n * @param options - Transcription options\n * @returns Transcription result with text and optional timestamps\n */\n async transcribe(\n audio: Float32Array | Uint8Array,\n options: TranscribeOptions = {},\n ): Promise<TranscribeResult> {\n if (!this._isLoaded) {\n throw new Error(\"STT model not loaded. Call load() first.\");\n }\n\n const { language, timestamps = false, onProgress } = options;\n const startTime = performance.now();\n\n // Convert Uint8Array (WAV) to Float32Array\n let audioData: Float32Array;\n let inputSampleRate = 16000;\n\n if (audio instanceof Uint8Array) {\n onProgress?.({ status: \"Decoding audio...\" });\n const decoded = decodeWav(audio);\n audioData = decoded.audio;\n inputSampleRate = decoded.sampleRate;\n } else {\n audioData = audio;\n }\n\n // Resample to 16kHz if needed\n if (inputSampleRate !== 16000) {\n onProgress?.({ status: \"Resampling to 16kHz...\" });\n audioData = resampleAudio(audioData, inputSampleRate, 16000);\n }\n\n const audioDuration = audioData.length / 16000;\n onProgress?.({ status: `Transcribing ${audioDuration.toFixed(1)}s of audio...` });\n\n // Build pipeline options\n const pipelineOptions: any = {};\n\n // Only set language for multilingual models\n if (language && this.modelConfig.multilingual) {\n pipelineOptions.language = language;\n pipelineOptions.task = \"transcribe\";\n }\n\n // Enable timestamps if requested\n if (timestamps) {\n pipelineOptions.return_timestamps = true;\n }\n\n // Run transcription\n const result = await this.pipeline(audioData, pipelineOptions);\n\n const totalTime = performance.now() - startTime;\n\n // Build result\n let text = result.text?.trim() || \"\";\n\n // Filter out Whisper artifacts\n if (text === \"[BLANK_AUDIO]\" || text === \"(blank audio)\" || text === \"[BLANK AUDIO]\") {\n text = \"\";\n }\n\n const transcribeResult: TranscribeResult = {\n text,\n language: language || (this.modelConfig.multilingual ? \"auto\" : \"en\"),\n duration: audioDuration,\n totalTime,\n };\n\n // Add segments if timestamps were requested\n if (timestamps && result.chunks) {\n transcribeResult.segments = result.chunks.map(\n (chunk: any): TranscribeSegment => ({\n text: chunk.text?.trim() || \"\",\n start: chunk.timestamp?.[0] || 0,\n end: chunk.timestamp?.[1] || 0,\n }),\n );\n }\n\n onProgress?.({ status: \"Done!\" });\n\n return transcribeResult;\n }\n\n /**\n * Create a streaming transcription session\n *\n * Transcribes audio in real-time by processing chunks at regular intervals.\n * Perfect for live captioning, call transcription, or real-time subtitles.\n *\n * @param options - Streaming options\n * @returns Streaming session controller\n *\n * @example\n * ```ts\n * const session = stt.createStreamingSession({\n * chunkDuration: 3000, // Transcribe every 3 seconds\n * onChunk: (text, idx) => console.log(`Chunk ${idx}: ${text}`),\n * onTranscript: (fullText) => console.log(\"Full:\", fullText),\n * });\n *\n * // Feed audio data as it comes in (Float32Array at 16kHz)\n * session.feedAudio(audioChunk);\n *\n * // Or manually trigger transcription\n * await session.flush();\n *\n * // Stop and get final transcript\n * const finalText = await session.stop();\n * ```\n */\n createStreamingSession(\n options: StreamingTranscriptionOptions = {},\n ): StreamingTranscriptionSession {\n const {\n chunkDuration = 3000,\n minChunkSize = 8000, // ~0.5 seconds at 16kHz\n onChunk,\n onTranscript,\n onError,\n language,\n } = options;\n\n let audioBuffer: Float32Array[] = [];\n let fullTranscript = \"\";\n let chunkIndex = 0;\n let intervalId: ReturnType<typeof setInterval> | null = null;\n let isRunning = false;\n\n const getBufferSize = (): number => {\n return audioBuffer.reduce((sum, chunk) => sum + chunk.length, 0);\n };\n\n const mergeBuffer = (): Float32Array => {\n const totalLength = getBufferSize();\n const merged = new Float32Array(totalLength);\n let offset = 0;\n for (const chunk of audioBuffer) {\n merged.set(chunk, offset);\n offset += chunk.length;\n }\n return merged;\n };\n\n const transcribeBuffer = async (): Promise<string> => {\n if (!this._isLoaded || getBufferSize() < minChunkSize) {\n return \"\";\n }\n\n const audio = mergeBuffer();\n audioBuffer = []; // Clear buffer\n\n try {\n const result = await this.transcribe(audio, { language });\n const text = result.text.trim();\n\n if (text) {\n chunkIndex++;\n onChunk?.(text, chunkIndex);\n\n // Append to full transcript\n fullTranscript = fullTranscript + (fullTranscript ? \" \" : \"\") + text;\n onTranscript?.(fullTranscript);\n }\n\n return text;\n } catch (e: any) {\n onError?.(e.message || \"Transcription failed\");\n return \"\";\n }\n };\n\n let aborted = false;\n\n const session: StreamingTranscriptionSession = {\n feedAudio: (audio: Float32Array) => {\n if (!aborted) {\n audioBuffer.push(audio);\n }\n },\n\n flush: async () => {\n if (aborted) return \"\";\n return transcribeBuffer();\n },\n\n start: () => {\n if (isRunning || aborted) return;\n isRunning = true;\n\n intervalId = setInterval(async () => {\n if (isRunning && !aborted) {\n await transcribeBuffer();\n }\n }, chunkDuration);\n },\n\n stop: async () => {\n isRunning = false;\n\n if (intervalId) {\n clearInterval(intervalId);\n intervalId = null;\n }\n\n // Transcribe any remaining audio (unless aborted)\n if (!aborted && getBufferSize() >= minChunkSize) {\n await transcribeBuffer();\n }\n\n return fullTranscript;\n },\n\n abort: () => {\n // Immediately stop without final transcription\n aborted = true;\n isRunning = false;\n\n if (intervalId) {\n clearInterval(intervalId);\n intervalId = null;\n }\n\n audioBuffer = [];\n },\n\n isRunning: () => isRunning,\n\n getTranscript: () => fullTranscript,\n\n getChunkCount: () => chunkIndex,\n\n reset: () => {\n audioBuffer = [];\n fullTranscript = \"\";\n chunkIndex = 0;\n },\n };\n\n return session;\n }\n\n /**\n * Dispose of resources\n */\n dispose(): void {\n this.pipeline = null;\n this._isLoaded = false;\n this.loadPromise = null;\n }\n}\n"],"mappings":";;;;;AA0CA,MAAaA,iBAAmC;CAC9C;EACE,IAAI;EACJ,MAAM;EACN,aAAa;EACb,MAAM;EACN,cAAc;EACd,WAAW,CAAC,KAAK;EACjB,YAAY;EACb;CACD;EACE,IAAI;EACJ,MAAM;EACN,aAAa;EACb,MAAM;EACN,cAAc;EACd,WAAW;GAAC;GAAM;GAAM;GAAM;GAAM;GAAM;GAAM;GAAM;GAAM;GAAM;GAAM;GAAK;EAC7E,YAAY;EACb;CACD;EACE,IAAI;EACJ,MAAM;EACN,aAAa;EACb,MAAM;EACN,cAAc;EACd,WAAW,CAAC,KAAK;EACjB,YAAY;EACb;CACD;EACE,IAAI;EACJ,MAAM;EACN,aAAa;EACb,MAAM;EACN,cAAc;EACd,WAAW;GAAC;GAAM;GAAM;GAAM;GAAM;GAAM;GAAM;GAAM;GAAM;GAAM;GAAM;GAAK;EAC7E,YAAY;EACb;CACD;EACE,IAAI;EACJ,MAAM;EACN,aAAa;EACb,MAAM;EACN,cAAc;EACd,WAAW,CAAC,KAAK;EACjB,YAAY;EACb;CACD;EACE,IAAI;EACJ,MAAM;EACN,aAAa;EACb,MAAM;EACN,cAAc;EACd,WAAW;GAAC;GAAM;GAAM;GAAM;GAAM;GAAM;GAAM;GAAM;GAAM;GAAM;GAAM;GAAK;EAC7E,YAAY;EACb;CACD;EACE,IAAI;EACJ,MAAM;EACN,aAAa;EACb,MAAM;EACN,cAAc;EACd,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD;EACD,YAAY;EACb;CACF;AAGD,MAAM,gBAAgB;;;;;AAUtB,SAAgB,UAAU,QAAiE;CACzF,MAAM,OAAO,IAAI,SAAS,OAAO,QAAQ,OAAO,YAAY,OAAO,WAAW;AAI9E,KADa,OAAO,aAAa,OAAO,IAAI,OAAO,IAAI,OAAO,IAAI,OAAO,GAAG,KAC/D,OACX,OAAM,IAAI,MAAM,wCAAwC;CAI1D,MAAM,cAAc,KAAK,UAAU,IAAI,KAAK;CAC5C,MAAM,aAAa,KAAK,UAAU,IAAI,KAAK;CAC3C,MAAM,gBAAgB,KAAK,UAAU,IAAI,KAAK;AAE9C,KAAI,kBAAkB,GACpB,OAAM,IAAI,MAAM,0BAA0B,cAAc,iCAAiC;CAI3F,IAAI,aAAa;AACjB,QAAO,aAAa,OAAO,SAAS,GAAG;EACrC,MAAM,UAAU,OAAO,aACrB,OAAO,aACP,OAAO,aAAa,IACpB,OAAO,aAAa,IACpB,OAAO,aAAa,GACrB;EACD,MAAM,YAAY,KAAK,UAAU,aAAa,GAAG,KAAK;AACtD,MAAI,YAAY,QAAQ;AACtB,iBAAc;AACd;;AAEF,gBAAc,IAAI;;CAGpB,MAAM,WAAW,OAAO,SAAS;CACjC,MAAM,iBAAiB,gBAAgB;CACvC,MAAM,eAAe,KAAK,MAAM,WAAW,eAAe;CAC1D,MAAM,oBAAoB,KAAK,MAAM,eAAe,YAAY;CAGhE,MAAM,QAAQ,IAAI,aAAa,kBAAkB;AAEjD,MAAK,IAAI,IAAI,GAAG,IAAI,mBAAmB,IACrC,KAAI,gBAAgB,EAGlB,OAAM,MAFO,KAAK,SAAS,aAAa,IAAI,GAAG,KAAK,GACtC,KAAK,SAAS,aAAa,IAAI,IAAI,GAAG,KAAK,IAC7B,IAAI;KAGhC,OAAM,KADS,KAAK,SAAS,aAAa,IAAI,GAAG,KAAK,GAClC;AAIxB,QAAO;EAAE;EAAO;EAAY;;;;;AAM9B,SAAgB,cAAc,OAAqB,UAAkB,QAA8B;AACjG,KAAI,aAAa,OAAQ,QAAO;CAEhC,MAAM,QAAQ,SAAS;CACvB,MAAM,YAAY,KAAK,MAAM,MAAM,SAAS,MAAM;CAClD,MAAM,SAAS,IAAI,aAAa,UAAU;AAE1C,MAAK,IAAI,IAAI,GAAG,IAAI,WAAW,KAAK;EAClC,MAAM,WAAW,IAAI;EACrB,MAAM,QAAQ,KAAK,MAAM,SAAS;EAClC,MAAM,OAAO,KAAK,IAAI,QAAQ,GAAG,MAAM,SAAS,EAAE;EAClD,MAAM,IAAI,WAAW;AACrB,SAAO,KAAK,MAAM,UAAU,IAAI,KAAK,MAAM,QAAQ;;AAGrD,QAAO;;;;;AAUT,IAAa,aAAb,MAAwB;CACtB,AAAQ;CACR,AAAQ,WAAgB;CACxB,AAAQ,cAAoC;CAC5C,AAAQ,YAAY;CACpB,AAAQ,cAAgC;CAExC,YAAY,UAAkB,eAAe;EAC3C,MAAM,SAAS,eAAe,MAAM,MAAM,EAAE,OAAO,QAAQ;AAC3D,MAAI,CAAC,QAAQ;GACX,MAAM,YAAY,eAAe,KAAK,MAAM,EAAE,GAAG,CAAC,KAAK,KAAK;AAC5D,SAAM,IAAI,MAAM,sBAAsB,QAAQ,eAAe,YAAY;;AAE3E,OAAK,cAAc;;;;;CAMrB,WAAoB;AAClB,SAAO,KAAK;;;;;CAMd,iBAAiC;AAC/B,SAAO,KAAK;;;;;CAMd,eAA+B;AAC7B,SAAO,KAAK;;;;;CAMd,gBAAkC;AAChC,SAAO,KAAK;;;;;CAMd,OAAO,aAA+B;AACpC,SAAO,CAAC,GAAG,eAAe;;;;;CAM5B,MAAM,KAAK,UAA0B,EAAE,EAAiB;AACtD,MAAI,KAAK,UAAW;AACpB,MAAI,KAAK,aAAa;AACpB,SAAM,KAAK;AACX;;AAGF,OAAK,cAAc,KAAK,MAAM,QAAQ;AACtC,QAAM,KAAK;;CAGb,MAAc,MAAM,UAA0B,EAAE,EAAiB;EAC/D,MAAM,EAAE,YAAY,SAAS,WAAW;AAExC,eAAa,EAAE,QAAQ,8BAA8B,CAAC;EAGtD,MAAM,SAAS,OAAO,YAAY,eAAe,QAAQ,UAAU;EAInE,MAAM,EAAE,UAAU,QAAQ,MAAM,OAAO;AAGvC,MAAI,QAAQ;AAEV,OAAI,mBAAmB;AACvB,OAAI,oBAAoB;SACnB;AAEL,OAAI,kBAAkB;AACtB,OAAI,mBAAmB;;EAMzB,IAAIC,WAAsC;AAG1C,MAAI,CAAC,OACH,YAAW;AAIb,OAAK,cAAc;AAEnB,eAAa,EAAE,QAAQ,WAAW,KAAK,YAAY,GAAG,MAAM,CAAC;AAI7D,OAAK,WAAW,MAAM,SAAS,gCAAgC,KAAK,YAAY,MAAM;GACpF,OAAO;GACP,QAAQ;GACR,oBAAoB,aAAkB;AACpC,QAAI,SAAS,WAAW,cAAc,SAAS,KAC7C,cAAa;KACX,QAAQ,eAAe,SAAS;KAChC,UAAU,KAAK,MAAM,SAAS,YAAY,EAAE;KAC5C,MAAM,SAAS;KAChB,CAAC;;GAGP,CAAC;AAEF,OAAK,YAAY;AACjB,eAAa,EAAE,QAAQ,UAAU,SAAS,aAAa,CAAC,KAAK,CAAC;;;;;;;;;CAUhE,MAAM,WACJ,OACA,UAA6B,EAAE,EACJ;AAC3B,MAAI,CAAC,KAAK,UACR,OAAM,IAAI,MAAM,2CAA2C;EAG7D,MAAM,EAAE,UAAU,aAAa,OAAO,eAAe;EACrD,MAAM,YAAY,YAAY,KAAK;EAGnC,IAAIC;EACJ,IAAI,kBAAkB;AAEtB,MAAI,iBAAiB,YAAY;AAC/B,gBAAa,EAAE,QAAQ,qBAAqB,CAAC;GAC7C,MAAM,UAAU,UAAU,MAAM;AAChC,eAAY,QAAQ;AACpB,qBAAkB,QAAQ;QAE1B,aAAY;AAId,MAAI,oBAAoB,MAAO;AAC7B,gBAAa,EAAE,QAAQ,0BAA0B,CAAC;AAClD,eAAY,cAAc,WAAW,iBAAiB,KAAM;;EAG9D,MAAM,gBAAgB,UAAU,SAAS;AACzC,eAAa,EAAE,QAAQ,gBAAgB,cAAc,QAAQ,EAAE,CAAC,gBAAgB,CAAC;EAGjF,MAAMC,kBAAuB,EAAE;AAG/B,MAAI,YAAY,KAAK,YAAY,cAAc;AAC7C,mBAAgB,WAAW;AAC3B,mBAAgB,OAAO;;AAIzB,MAAI,WACF,iBAAgB,oBAAoB;EAItC,MAAM,SAAS,MAAM,KAAK,SAAS,WAAW,gBAAgB;EAE9D,MAAM,YAAY,YAAY,KAAK,GAAG;EAGtC,IAAI,OAAO,OAAO,MAAM,MAAM,IAAI;AAGlC,MAAI,SAAS,mBAAmB,SAAS,mBAAmB,SAAS,gBACnE,QAAO;EAGT,MAAMC,mBAAqC;GACzC;GACA,UAAU,aAAa,KAAK,YAAY,eAAe,SAAS;GAChE,UAAU;GACV;GACD;AAGD,MAAI,cAAc,OAAO,OACvB,kBAAiB,WAAW,OAAO,OAAO,KACvC,WAAmC;GAClC,MAAM,MAAM,MAAM,MAAM,IAAI;GAC5B,OAAO,MAAM,YAAY,MAAM;GAC/B,KAAK,MAAM,YAAY,MAAM;GAC9B,EACF;AAGH,eAAa,EAAE,QAAQ,SAAS,CAAC;AAEjC,SAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BT,uBACE,UAAyC,EAAE,EACZ;EAC/B,MAAM,EACJ,gBAAgB,KAChB,eAAe,KACf,SACA,cACA,SACA,aACE;EAEJ,IAAIC,cAA8B,EAAE;EACpC,IAAI,iBAAiB;EACrB,IAAI,aAAa;EACjB,IAAIC,aAAoD;EACxD,IAAI,YAAY;EAEhB,MAAM,sBAA8B;AAClC,UAAO,YAAY,QAAQ,KAAK,UAAU,MAAM,MAAM,QAAQ,EAAE;;EAGlE,MAAM,oBAAkC;GACtC,MAAM,cAAc,eAAe;GACnC,MAAM,SAAS,IAAI,aAAa,YAAY;GAC5C,IAAI,SAAS;AACb,QAAK,MAAM,SAAS,aAAa;AAC/B,WAAO,IAAI,OAAO,OAAO;AACzB,cAAU,MAAM;;AAElB,UAAO;;EAGT,MAAM,mBAAmB,YAA6B;AACpD,OAAI,CAAC,KAAK,aAAa,eAAe,GAAG,aACvC,QAAO;GAGT,MAAM,QAAQ,aAAa;AAC3B,iBAAc,EAAE;AAEhB,OAAI;IAEF,MAAM,QADS,MAAM,KAAK,WAAW,OAAO,EAAE,UAAU,CAAC,EACrC,KAAK,MAAM;AAE/B,QAAI,MAAM;AACR;AACA,eAAU,MAAM,WAAW;AAG3B,sBAAiB,kBAAkB,iBAAiB,MAAM,MAAM;AAChE,oBAAe,eAAe;;AAGhC,WAAO;YACAC,GAAQ;AACf,cAAU,EAAE,WAAW,uBAAuB;AAC9C,WAAO;;;EAIX,IAAI,UAAU;AAmEd,SAjE+C;GAC7C,YAAY,UAAwB;AAClC,QAAI,CAAC,QACH,aAAY,KAAK,MAAM;;GAI3B,OAAO,YAAY;AACjB,QAAI,QAAS,QAAO;AACpB,WAAO,kBAAkB;;GAG3B,aAAa;AACX,QAAI,aAAa,QAAS;AAC1B,gBAAY;AAEZ,iBAAa,YAAY,YAAY;AACnC,SAAI,aAAa,CAAC,QAChB,OAAM,kBAAkB;OAEzB,cAAc;;GAGnB,MAAM,YAAY;AAChB,gBAAY;AAEZ,QAAI,YAAY;AACd,mBAAc,WAAW;AACzB,kBAAa;;AAIf,QAAI,CAAC,WAAW,eAAe,IAAI,aACjC,OAAM,kBAAkB;AAG1B,WAAO;;GAGT,aAAa;AAEX,cAAU;AACV,gBAAY;AAEZ,QAAI,YAAY;AACd,mBAAc,WAAW;AACzB,kBAAa;;AAGf,kBAAc,EAAE;;GAGlB,iBAAiB;GAEjB,qBAAqB;GAErB,qBAAqB;GAErB,aAAa;AACX,kBAAc,EAAE;AAChB,qBAAiB;AACjB,iBAAa;;GAEhB;;;;;CAQH,UAAgB;AACd,OAAK,WAAW;AAChB,OAAK,YAAY;AACjB,OAAK,cAAc"}
@@ -0,0 +1,3 @@
1
+ import { a as __webpack_exports__env, i as __webpack_exports__Tensor, n as __webpack_exports__RawAudio, o as __webpack_exports__pipeline, r as __webpack_exports__StyleTextToSpeech2Model, t as __webpack_exports__AutoTokenizer } from "./transformers.web-M6mCnEYJ.js";
2
+
3
+ export { __webpack_exports__env as env, __webpack_exports__pipeline as pipeline };