@hasna/recordings 0.1.10 → 0.1.11

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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(ssh apple03:*)"
5
+ ]
6
+ }
7
+ }
package/dist/cli/index.js CHANGED
@@ -7,7 +7,7 @@ var __require = import.meta.require;
7
7
  var require_package = __commonJS((exports, module) => {
8
8
  module.exports = {
9
9
  name: "@hasna/recordings",
10
- version: "0.1.10",
10
+ version: "0.1.11",
11
11
  type: "module",
12
12
  description: "Speech-to-text recording tool with MCP and CLI \u2014 records, transcribes, and optionally enhances text using AI",
13
13
  main: "dist/index.js",
@@ -60,7 +60,7 @@ import { Command } from "commander";
60
60
  import chalk from "chalk";
61
61
 
62
62
  // src/lib/config.ts
63
- import { existsSync, readFileSync, mkdirSync, cpSync } from "fs";
63
+ import { existsSync, readFileSync, mkdirSync, cpSync, readdirSync, statSync } from "fs";
64
64
  import { join } from "path";
65
65
  import { homedir } from "os";
66
66
  var DEFAULT_CONFIG = {
@@ -97,7 +97,7 @@ function loadConfig(configPath) {
97
97
  try {
98
98
  const raw = readFileSync(filePath, "utf-8");
99
99
  const fileConfig = JSON.parse(raw);
100
- Object.assign(config, fileConfig);
100
+ Object.assign(config, expandEnvBackedConfig(fileConfig));
101
101
  } catch {}
102
102
  }
103
103
  if (process.env.OPENAI_API_KEY) {
@@ -143,6 +143,16 @@ function loadConfig(configPath) {
143
143
  }
144
144
  return config;
145
145
  }
146
+ function expandEnvBackedConfig(config) {
147
+ const expanded = { ...config };
148
+ for (const key of ["openai_api_key", "enhancement_api_key"]) {
149
+ const value = expanded[key];
150
+ if (typeof value === "string" && value.startsWith("$") && value.length > 1) {
151
+ expanded[key] = process.env[value.slice(1)] || value;
152
+ }
153
+ }
154
+ return expanded;
155
+ }
146
156
  function findConfigFile() {
147
157
  let dir = process.cwd();
148
158
  const root = "/";
@@ -184,20 +194,44 @@ function loadSecretKey(keyName) {
184
194
  const secretsPath = join(homedir(), ".secrets");
185
195
  if (!existsSync(secretsPath))
186
196
  return "";
187
- try {
188
- const content = readFileSync(secretsPath, "utf-8");
189
- const match = content.match(new RegExp(`export\\s+${keyName}\\s*=\\s*"([^"]+)"`));
190
- if (match)
191
- return match[1];
192
- const match2 = content.match(new RegExp(`export\\s+${keyName}\\s*=\\s*'([^']+)'`));
193
- if (match2)
194
- return match2[1];
195
- const match3 = content.match(new RegExp(`${keyName}\\s*=\\s*(.+)`));
196
- if (match3)
197
- return match3[1].trim().replace(/^["']|["']$/g, "");
198
- } catch {}
197
+ for (const candidate of listSecretFiles(secretsPath)) {
198
+ try {
199
+ const content = readFileSync(candidate, "utf-8");
200
+ const match = content.match(new RegExp(`export\\s+${keyName}\\s*=\\s*"([^"]+)"`));
201
+ if (match)
202
+ return match[1];
203
+ const match2 = content.match(new RegExp(`export\\s+${keyName}\\s*=\\s*'([^']+)'`));
204
+ if (match2)
205
+ return match2[1];
206
+ const match3 = content.match(new RegExp(`${keyName}\\s*=\\s*(.+)`));
207
+ if (match3)
208
+ return match3[1].trim().replace(/^["']|["']$/g, "");
209
+ } catch {}
210
+ }
199
211
  return "";
200
212
  }
213
+ function listSecretFiles(path) {
214
+ try {
215
+ const stats = statSync(path);
216
+ if (stats.isFile())
217
+ return [path];
218
+ if (!stats.isDirectory())
219
+ return [];
220
+ return readdirSync(path).sort().flatMap((entry) => {
221
+ const child = join(path, entry);
222
+ try {
223
+ const childStats = statSync(child);
224
+ if (childStats.isDirectory())
225
+ return listSecretFiles(child);
226
+ if (childStats.isFile() && child.endsWith(".env"))
227
+ return [child];
228
+ } catch {}
229
+ return [];
230
+ });
231
+ } catch {
232
+ return [];
233
+ }
234
+ }
201
235
  function ensureDataDir(config) {
202
236
  const { mkdirSync: mkdirSync2 } = __require("fs");
203
237
  mkdirSync2(config.audio_dir, { recursive: true });
@@ -212,7 +246,7 @@ import { Database } from "bun:sqlite";
212
246
  import {
213
247
  existsSync as existsSync2,
214
248
  mkdirSync as mkdirSync2,
215
- readdirSync,
249
+ readdirSync as readdirSync2,
216
250
  copyFileSync
217
251
  } from "fs";
218
252
  import { homedir as homedir2 } from "os";
@@ -220,7 +254,7 @@ import { join as join2, relative } from "path";
220
254
  import { existsSync as existsSync22, mkdirSync as mkdirSync22, readFileSync as readFileSync2, writeFileSync } from "fs";
221
255
  import { homedir as homedir22 } from "os";
222
256
  import { join as join22 } from "path";
223
- import { readdirSync as readdirSync2, existsSync as existsSync3 } from "fs";
257
+ import { readdirSync as readdirSync22, existsSync as existsSync3 } from "fs";
224
258
  import { join as join3 } from "path";
225
259
  import { homedir as homedir3 } from "os";
226
260
  import { homedir as homedir4 } from "os";
@@ -9499,7 +9533,7 @@ function discoverServices() {
9499
9533
  if (!existsSync3(dataDir))
9500
9534
  return [];
9501
9535
  try {
9502
- const entries = readdirSync2(dataDir, { withFileTypes: true });
9536
+ const entries = readdirSync22(dataDir, { withFileTypes: true });
9503
9537
  return entries.filter((e) => {
9504
9538
  if (!e.isDirectory())
9505
9539
  return false;
@@ -9526,7 +9560,7 @@ function getServiceDbPath(service) {
9526
9560
  join3(dataDir, "database.db")
9527
9561
  ];
9528
9562
  try {
9529
- const files = readdirSync2(dataDir);
9563
+ const files = readdirSync22(dataDir);
9530
9564
  for (const f of files) {
9531
9565
  if (f.endsWith(".db") && !f.endsWith("-wal") && !f.endsWith("-shm")) {
9532
9566
  candidates.push(join3(dataDir, f));
@@ -10244,15 +10278,9 @@ function extractInstruction(text, trigger) {
10244
10278
  }
10245
10279
  return text;
10246
10280
  }
10247
- async function enhanceText(rawText, instruction, config) {
10281
+ async function enhanceText(rawText, instruction, config, systemPrompt) {
10248
10282
  const client = getEnhancementClient(config);
10249
- try {
10250
- const response = await client.chat.completions.create({
10251
- model: config.enhancement_model,
10252
- messages: [
10253
- {
10254
- role: "system",
10255
- content: `You are a writing assistant. The user has dictated speech that needs to be transformed into polished output.
10283
+ const basePrompt = `You are a writing assistant. The user has dictated speech that needs to be transformed into polished output.
10256
10284
 
10257
10285
  Rules:
10258
10286
  - Output ONLY the enhanced/rewritten text \u2014 no explanations, no preamble
@@ -10260,7 +10288,18 @@ Rules:
10260
10288
  - Fix grammar, structure, and clarity
10261
10289
  - If the user is giving instructions (e.g., "write an email saying..."), produce the actual output (the email), not a description of it
10262
10290
  - If the user says "say it better" or similar, rewrite their preceding text to be clearer and more professional
10263
- - Match the appropriate tone (formal for business, casual for personal)`
10291
+ - Match the appropriate tone (formal for business, casual for personal)`;
10292
+ const fullPrompt = systemPrompt ? `${basePrompt}
10293
+
10294
+ Additional context:
10295
+ ${systemPrompt}` : basePrompt;
10296
+ try {
10297
+ const response = await client.chat.completions.create({
10298
+ model: config.enhancement_model,
10299
+ messages: [
10300
+ {
10301
+ role: "system",
10302
+ content: fullPrompt
10264
10303
  },
10265
10304
  {
10266
10305
  role: "user",
@@ -10282,7 +10321,7 @@ Rules:
10282
10321
  throw new EnhancementError(`Enhancement failed: ${msg}`);
10283
10322
  }
10284
10323
  }
10285
- async function processText(rawText, config) {
10324
+ async function processText(rawText, config, systemPrompt) {
10286
10325
  if (!config.auto_enhance) {
10287
10326
  return { text: rawText, mode: "raw", enhancement_model: null };
10288
10327
  }
@@ -10290,7 +10329,7 @@ async function processText(rawText, config) {
10290
10329
  if (!detection.needs) {
10291
10330
  return { text: rawText, mode: "raw", enhancement_model: null };
10292
10331
  }
10293
- const result = await enhanceText(rawText, detection.instruction, config);
10332
+ const result = await enhanceText(rawText, detection.instruction, config, systemPrompt);
10294
10333
  return {
10295
10334
  text: result.enhanced,
10296
10335
  mode: "enhanced",
@@ -10299,7 +10338,6 @@ async function processText(rawText, config) {
10299
10338
  }
10300
10339
 
10301
10340
  // src/cli/index.ts
10302
- var __dirname = "/home/hasna/workspace/hasna/opensource/opensourcedev/open-recordings/src/cli";
10303
10341
  var program = new Command;
10304
10342
  program.name("recordings").description("Speech-to-text recording tool \u2014 record, transcribe, and enhance with AI").version("0.0.3").option("--json", "Output as JSON").option("--agent <name>", "Agent name or ID").option("--project <name>", "Project name or ID").option("--session <id>", "Session ID");
10305
10343
  program.command("record").description("Record from microphone, transcribe, and optionally enhance").option("-d, --duration <seconds>", "Record for specific duration").option("--no-enhance", "Skip AI enhancement").option("-t, --tags <tags>", "Comma-separated tags").option("-l, --language <lang>", "Language code (e.g. en, es, fr)").action(async (opts) => {
@@ -10371,14 +10409,14 @@ Output:`));
10371
10409
  Saved as ${recording.id.slice(0, 8)}`));
10372
10410
  }
10373
10411
  });
10374
- program.command("transcribe <file>").description("Transcribe an existing audio file").option("--no-enhance", "Skip AI enhancement").option("-t, --tags <tags>", "Comma-separated tags").action(async (file, opts) => {
10412
+ program.command("transcribe <file>").description("Transcribe an existing audio file").option("--no-enhance", "Skip AI enhancement").option("-t, --tags <tags>", "Comma-separated tags").option("--system-prompt <prompt>", "System prompt for enhancement context").action(async (file, opts) => {
10375
10413
  const config = loadConfig();
10376
10414
  ensureDataDir(config);
10377
10415
  if (opts.noEnhance === false)
10378
10416
  config.auto_enhance = false;
10379
10417
  console.log(chalk.blue("Transcribing..."));
10380
10418
  const transcription = await transcribeAudio(file, config);
10381
- const processed = await processText(transcription.text, config);
10419
+ const processed = await processText(transcription.text, config, opts.systemPrompt);
10382
10420
  const parentOpts = program.opts();
10383
10421
  const tags = opts.tags ? opts.tags.split(",").map((t) => t.trim()) : [];
10384
10422
  const recording = createRecording({
@@ -10582,42 +10620,6 @@ program.command("check").description("Check system dependencies (sox, API keys)"
10582
10620
  console.log(chalk.yellow(`\u26A0 Enhancement API key not configured \u2014 enhancement disabled`));
10583
10621
  }
10584
10622
  });
10585
- program.command("start").description("Launch the menu bar helper app (F5 to toggle recording)").option("--login", "Also add to Login Items so it starts automatically").action(async (opts) => {
10586
- const { execSync } = __require("child_process");
10587
- const { join: pathJoin } = __require("path");
10588
- const { homedir: getHome } = __require("os");
10589
- const { existsSync: fileExists } = __require("fs");
10590
- const home = getHome();
10591
- const appPath = pathJoin(home, ".hasna", "recordings", "RecordingsHelper.app");
10592
- const oldAppPath = pathJoin(home, ".recordings", "RecordingsHelper.app");
10593
- if (!fileExists(appPath) && !fileExists(oldAppPath)) {
10594
- console.error(chalk.red("RecordingsHelper.app not found. Run: recordings shortcut --install"));
10595
- process.exit(1);
10596
- }
10597
- const resolvedAppPath = fileExists(appPath) ? appPath : oldAppPath;
10598
- try {
10599
- execSync("pkill -f RecordingsHelper", { stdio: "pipe" });
10600
- } catch {}
10601
- execSync(`open "${resolvedAppPath}"`, { stdio: "pipe" });
10602
- console.log(chalk.green("Recordings helper launched \u2014 press F5 to record"));
10603
- if (opts.login) {
10604
- try {
10605
- execSync(`osascript -e 'tell application "System Events" to make login item at end with properties {path:"${resolvedAppPath}", hidden:true}'`, { stdio: "pipe" });
10606
- console.log(chalk.green("Added to Login Items \u2014 will start on boot"));
10607
- } catch {
10608
- console.log(chalk.yellow("Could not add to Login Items \u2014 add manually in System Settings > General > Login Items"));
10609
- }
10610
- }
10611
- });
10612
- program.command("stop").description("Stop the menu bar helper app").action(() => {
10613
- const { execSync } = __require("child_process");
10614
- try {
10615
- execSync("pkill -f RecordingsHelper", { stdio: "pipe" });
10616
- console.log(chalk.green("Recordings helper stopped"));
10617
- } catch {
10618
- console.log(chalk.dim("Not running"));
10619
- }
10620
- });
10621
10623
  program.command("listen").description("Push-to-talk mode \u2014 press Space to start/stop recording, Esc to quit").option("-t, --tags <tags>", "Comma-separated tags for all recordings").option("--no-enhance", "Skip AI enhancement").option("-l, --language <lang>", "Language code").option("--copy", "Copy output to clipboard").option("--paste", "Copy output to clipboard AND paste into frontmost app").action(async (opts) => {
10622
10624
  const config = loadConfig();
10623
10625
  ensureDataDir(config);
@@ -10726,8 +10728,8 @@ Bye.`));
10726
10728
  }
10727
10729
  });
10728
10730
  });
10729
- program.command("shortcut").description("Set up a global keyboard shortcut for recording (macOS)").option("--raycast", "Generate Raycast script command").option("--install", "Set up F5 global shortcut via macOS Services (no extra installs)").option("--karabiner", "Set up Fn key via Karabiner-Elements").option("--skhd", "Generate skhd hotkey config").option("--hammerspoon", "Generate Hammerspoon config").option("--script", "Just output the shell script path").action((opts) => {
10730
- const { writeFileSync: writeFileSync2, mkdirSync: mkdirSync4, chmodSync, existsSync: fileExists } = __require("fs");
10731
+ program.command("shortcut").description("Set up a global keyboard shortcut for recording (macOS)").option("--raycast", "Generate Raycast script command").option("--karabiner", "Set up Fn key via Karabiner-Elements").option("--skhd", "Generate skhd hotkey config").option("--hammerspoon", "Generate Hammerspoon config").option("--script", "Just output the shell script path").action((opts) => {
10732
+ const { writeFileSync: writeFileSync2, mkdirSync: mkdirSync4, chmodSync } = __require("fs");
10731
10733
  const { join: pathJoin } = __require("path");
10732
10734
  const { homedir: getHome } = __require("os");
10733
10735
  const home = getHome();
@@ -10782,54 +10784,6 @@ fi
10782
10784
  `;
10783
10785
  writeFileSync2(scriptPath, script, "utf-8");
10784
10786
  chmodSync(scriptPath, 493);
10785
- if (opts.install) {
10786
- const { execSync: exec } = __require("child_process");
10787
- const appPath = pathJoin(home, ".hasna", "recordings", "RecordingsHelper.app");
10788
- const srcSwift = pathJoin(__dirname, "..", "native", "RecordingsHelper.swift");
10789
- const distApp = pathJoin(__dirname, "..", "RecordingsHelper.app");
10790
- if (fileExists(pathJoin(distApp, "Contents", "MacOS", "RecordingsHelper"))) {
10791
- exec(`rm -rf "${appPath}" && cp -R "${distApp}" "${appPath}"`, { stdio: "pipe", shell: "/bin/bash" });
10792
- } else if (fileExists(srcSwift)) {
10793
- console.log(chalk.blue("Compiling native helper app..."));
10794
- const appDir = pathJoin(home, ".hasna", "recordings", "RecordingsHelper.app", "Contents", "MacOS");
10795
- mkdirSync4(appDir, { recursive: true });
10796
- const plistDir = pathJoin(home, ".hasna", "recordings", "RecordingsHelper.app", "Contents");
10797
- const plist = `<?xml version="1.0" encoding="UTF-8"?>
10798
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
10799
- <plist version="1.0"><dict>
10800
- <key>CFBundleExecutable</key><string>RecordingsHelper</string>
10801
- <key>CFBundleIdentifier</key><string>com.hasna.recordings-helper</string>
10802
- <key>CFBundleName</key><string>Recordings</string>
10803
- <key>LSUIElement</key><true/>
10804
- <key>NSMicrophoneUsageDescription</key><string>Recordings needs microphone access for speech transcription.</string>
10805
- </dict></plist>`;
10806
- writeFileSync2(pathJoin(plistDir, "Info.plist"), plist, "utf-8");
10807
- try {
10808
- exec(`DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer xcrun swiftc -O -o "${appDir}/RecordingsHelper" "${srcSwift}" -framework Cocoa -framework Carbon`, { stdio: "pipe" });
10809
- } catch {
10810
- exec(`swiftc -O -o "${appDir}/RecordingsHelper" "${srcSwift}" -framework Cocoa -framework Carbon`, { stdio: "pipe" });
10811
- }
10812
- } else {
10813
- console.error(chalk.red("Cannot find RecordingsHelper. Run from the project directory or rebuild."));
10814
- process.exit(1);
10815
- }
10816
- try {
10817
- exec("pkill -f RecordingsHelper", { stdio: "pipe" });
10818
- } catch {}
10819
- exec(`open "${appPath}"`, { stdio: "pipe" });
10820
- try {
10821
- exec(`osascript -e 'tell application "System Events" to make login item at end with properties {path:"${appPath}", hidden:true}'`, { stdio: "pipe" });
10822
- } catch {}
10823
- console.log(chalk.green(`
10824
- Recordings helper installed and running!
10825
- `));
10826
- console.log(` ${chalk.yellow("F5")} Start/stop recording`);
10827
- console.log(` ${chalk.dim("\uD83C\uDF99")} Menu bar icon (click for options)`);
10828
- console.log(` ${chalk.dim("Auto")} Starts on login
10829
- `);
10830
- console.log(chalk.dim(" Press F5 \u2192 speak \u2192 F5 \u2192 text is pasted where your cursor is."));
10831
- return;
10832
- }
10833
10787
  if (opts.karabiner) {
10834
10788
  const karabinerDir = pathJoin(home, ".config", "karabiner", "assets", "complex_modifications");
10835
10789
  mkdirSync4(karabinerDir, { recursive: true });
@@ -10917,9 +10871,6 @@ ${scriptPath}
10917
10871
  console.log(chalk.cyan(` ${scriptPath}
10918
10872
  `));
10919
10873
  console.log(`Bind it to a hotkey using any of these:
10920
- `);
10921
- console.log(chalk.bold(" macOS built-in") + chalk.dim(" (no extra installs \u2014 recommended)"));
10922
- console.log(` recordings shortcut --install
10923
10874
  `);
10924
10875
  console.log(chalk.bold(" Karabiner-Elements") + chalk.dim(" (for Fn key specifically)"));
10925
10876
  console.log(` brew install --cask karabiner-elements`);
package/dist/index.js CHANGED
@@ -9559,7 +9559,7 @@ import { mkdirSync as mkdirSync5 } from "fs";
9559
9559
  import { dirname as dirname2 } from "path";
9560
9560
 
9561
9561
  // src/lib/config.ts
9562
- import { existsSync as existsSync4, readFileSync as readFileSync2, mkdirSync as mkdirSync3, cpSync } from "fs";
9562
+ import { existsSync as existsSync4, readFileSync as readFileSync2, mkdirSync as mkdirSync3, cpSync, readdirSync as readdirSync3, statSync } from "fs";
9563
9563
  import { join as join5 } from "path";
9564
9564
  import { homedir as homedir6 } from "os";
9565
9565
  var DEFAULT_CONFIG = {
@@ -9596,7 +9596,7 @@ function loadConfig(configPath) {
9596
9596
  try {
9597
9597
  const raw = readFileSync2(filePath, "utf-8");
9598
9598
  const fileConfig = JSON.parse(raw);
9599
- Object.assign(config, fileConfig);
9599
+ Object.assign(config, expandEnvBackedConfig(fileConfig));
9600
9600
  } catch {}
9601
9601
  }
9602
9602
  if (process.env.OPENAI_API_KEY) {
@@ -9642,6 +9642,16 @@ function loadConfig(configPath) {
9642
9642
  }
9643
9643
  return config;
9644
9644
  }
9645
+ function expandEnvBackedConfig(config) {
9646
+ const expanded = { ...config };
9647
+ for (const key of ["openai_api_key", "enhancement_api_key"]) {
9648
+ const value = expanded[key];
9649
+ if (typeof value === "string" && value.startsWith("$") && value.length > 1) {
9650
+ expanded[key] = process.env[value.slice(1)] || value;
9651
+ }
9652
+ }
9653
+ return expanded;
9654
+ }
9645
9655
  function findConfigFile() {
9646
9656
  let dir = process.cwd();
9647
9657
  const root = "/";
@@ -9683,20 +9693,44 @@ function loadSecretKey(keyName) {
9683
9693
  const secretsPath = join5(homedir6(), ".secrets");
9684
9694
  if (!existsSync4(secretsPath))
9685
9695
  return "";
9686
- try {
9687
- const content = readFileSync2(secretsPath, "utf-8");
9688
- const match = content.match(new RegExp(`export\\s+${keyName}\\s*=\\s*"([^"]+)"`));
9689
- if (match)
9690
- return match[1];
9691
- const match2 = content.match(new RegExp(`export\\s+${keyName}\\s*=\\s*'([^']+)'`));
9692
- if (match2)
9693
- return match2[1];
9694
- const match3 = content.match(new RegExp(`${keyName}\\s*=\\s*(.+)`));
9695
- if (match3)
9696
- return match3[1].trim().replace(/^["']|["']$/g, "");
9697
- } catch {}
9696
+ for (const candidate of listSecretFiles(secretsPath)) {
9697
+ try {
9698
+ const content = readFileSync2(candidate, "utf-8");
9699
+ const match = content.match(new RegExp(`export\\s+${keyName}\\s*=\\s*"([^"]+)"`));
9700
+ if (match)
9701
+ return match[1];
9702
+ const match2 = content.match(new RegExp(`export\\s+${keyName}\\s*=\\s*'([^']+)'`));
9703
+ if (match2)
9704
+ return match2[1];
9705
+ const match3 = content.match(new RegExp(`${keyName}\\s*=\\s*(.+)`));
9706
+ if (match3)
9707
+ return match3[1].trim().replace(/^["']|["']$/g, "");
9708
+ } catch {}
9709
+ }
9698
9710
  return "";
9699
9711
  }
9712
+ function listSecretFiles(path) {
9713
+ try {
9714
+ const stats = statSync(path);
9715
+ if (stats.isFile())
9716
+ return [path];
9717
+ if (!stats.isDirectory())
9718
+ return [];
9719
+ return readdirSync3(path).sort().flatMap((entry) => {
9720
+ const child = join5(path, entry);
9721
+ try {
9722
+ const childStats = statSync(child);
9723
+ if (childStats.isDirectory())
9724
+ return listSecretFiles(child);
9725
+ if (childStats.isFile() && child.endsWith(".env"))
9726
+ return [child];
9727
+ } catch {}
9728
+ return [];
9729
+ });
9730
+ } catch {
9731
+ return [];
9732
+ }
9733
+ }
9700
9734
  function ensureDataDir(config) {
9701
9735
  const { mkdirSync: mkdirSync4 } = __require("fs");
9702
9736
  mkdirSync4(config.audio_dir, { recursive: true });
@@ -10166,15 +10200,9 @@ function extractInstruction(text, trigger) {
10166
10200
  }
10167
10201
  return text;
10168
10202
  }
10169
- async function enhanceText(rawText, instruction, config) {
10203
+ async function enhanceText(rawText, instruction, config, systemPrompt) {
10170
10204
  const client = getEnhancementClient(config);
10171
- try {
10172
- const response = await client.chat.completions.create({
10173
- model: config.enhancement_model,
10174
- messages: [
10175
- {
10176
- role: "system",
10177
- content: `You are a writing assistant. The user has dictated speech that needs to be transformed into polished output.
10205
+ const basePrompt = `You are a writing assistant. The user has dictated speech that needs to be transformed into polished output.
10178
10206
 
10179
10207
  Rules:
10180
10208
  - Output ONLY the enhanced/rewritten text \u2014 no explanations, no preamble
@@ -10182,7 +10210,18 @@ Rules:
10182
10210
  - Fix grammar, structure, and clarity
10183
10211
  - If the user is giving instructions (e.g., "write an email saying..."), produce the actual output (the email), not a description of it
10184
10212
  - If the user says "say it better" or similar, rewrite their preceding text to be clearer and more professional
10185
- - Match the appropriate tone (formal for business, casual for personal)`
10213
+ - Match the appropriate tone (formal for business, casual for personal)`;
10214
+ const fullPrompt = systemPrompt ? `${basePrompt}
10215
+
10216
+ Additional context:
10217
+ ${systemPrompt}` : basePrompt;
10218
+ try {
10219
+ const response = await client.chat.completions.create({
10220
+ model: config.enhancement_model,
10221
+ messages: [
10222
+ {
10223
+ role: "system",
10224
+ content: fullPrompt
10186
10225
  },
10187
10226
  {
10188
10227
  role: "user",
@@ -10204,7 +10243,7 @@ Rules:
10204
10243
  throw new EnhancementError(`Enhancement failed: ${msg}`);
10205
10244
  }
10206
10245
  }
10207
- async function processText(rawText, config) {
10246
+ async function processText(rawText, config, systemPrompt) {
10208
10247
  if (!config.auto_enhance) {
10209
10248
  return { text: rawText, mode: "raw", enhancement_model: null };
10210
10249
  }
@@ -10212,7 +10251,7 @@ async function processText(rawText, config) {
10212
10251
  if (!detection.needs) {
10213
10252
  return { text: rawText, mode: "raw", enhancement_model: null };
10214
10253
  }
10215
- const result = await enhanceText(rawText, detection.instruction, config);
10254
+ const result = await enhanceText(rawText, detection.instruction, config, systemPrompt);
10216
10255
  return {
10217
10256
  text: result.enhanced,
10218
10257
  mode: "enhanced",
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,eAAO,MAAM,cAAc,EAAE,gBA0B5B,CAAC;AAEF,wBAAgB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAqEhE;AAgBD,wBAAgB,UAAU,IAAI,MAAM,CA4BnC;AA0BD,wBAAgB,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAU5D"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,eAAO,MAAM,cAAc,EAAE,gBA0B5B,CAAC;AAEF,wBAAgB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAqEhE;AA6BD,wBAAgB,UAAU,IAAI,MAAM,CA4BnC;AAqDD,wBAAgB,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAU5D"}
@@ -13,11 +13,11 @@ export declare function needsEnhancement(text: string, config: RecordingsConfig)
13
13
  reason: string;
14
14
  instruction: string;
15
15
  };
16
- export declare function enhanceText(rawText: string, instruction: string, config: RecordingsConfig): Promise<EnhancementResult>;
16
+ export declare function enhanceText(rawText: string, instruction: string, config: RecordingsConfig, systemPrompt?: string): Promise<EnhancementResult>;
17
17
  /**
18
18
  * Full pipeline: detect if enhancement is needed, enhance if so.
19
19
  */
20
- export declare function processText(rawText: string, config: RecordingsConfig): Promise<{
20
+ export declare function processText(rawText: string, config: RecordingsConfig, systemPrompt?: string): Promise<{
21
21
  text: string;
22
22
  mode: "raw" | "enhanced";
23
23
  enhancement_model: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"enhancer.d.ts","sourceRoot":"","sources":["../../src/lib/enhancer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,mBAAmB,CAAC;AAiB3B,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,gBAAgB,GACvB;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAmCzD;AAoBD,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,iBAAiB,CAAC,CAyC5B;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,KAAK,GAAG,UAAU,CAAC;IACzB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC,CAAC,CAkBD"}
1
+ {"version":3,"file":"enhancer.d.ts","sourceRoot":"","sources":["../../src/lib/enhancer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,mBAAmB,CAAC;AAiB3B,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,gBAAgB,GACvB;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAmCzD;AAoBD,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,gBAAgB,EACxB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,iBAAiB,CAAC,CA6C5B;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,gBAAgB,EACxB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,KAAK,GAAG,UAAU,CAAC;IACzB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC,CAAC,CAkBD"}