@hasna/recordings 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -7,7 +7,7 @@ import { Command } from "commander";
7
7
  import chalk from "chalk";
8
8
 
9
9
  // src/lib/config.ts
10
- import { existsSync, readFileSync } from "fs";
10
+ import { existsSync, readFileSync, mkdirSync, cpSync } from "fs";
11
11
  import { join } from "path";
12
12
  import { homedir } from "os";
13
13
  var DEFAULT_CONFIG = {
@@ -65,7 +65,9 @@ function loadConfig(configPath) {
65
65
  if (process.env.RECORDINGS_LANGUAGE) {
66
66
  config.language = process.env.RECORDINGS_LANGUAGE;
67
67
  }
68
- if (process.env.RECORDINGS_DB_PATH) {
68
+ if (process.env.HASNA_RECORDINGS_DB_PATH) {
69
+ config.db_path = process.env.HASNA_RECORDINGS_DB_PATH;
70
+ } else if (process.env.RECORDINGS_DB_PATH) {
69
71
  config.db_path = process.env.RECORDINGS_DB_PATH;
70
72
  }
71
73
  if (process.env.RECORDINGS_AUDIO_DIR) {
@@ -114,7 +116,16 @@ function getDataDir() {
114
116
  break;
115
117
  dir = parent;
116
118
  }
117
- return join(homedir(), ".recordings");
119
+ const home = homedir();
120
+ const newDir = join(home, ".hasna", "recordings");
121
+ const oldDir = join(home, ".recordings");
122
+ if (!existsSync(newDir) && existsSync(oldDir)) {
123
+ try {
124
+ mkdirSync(join(home, ".hasna"), { recursive: true });
125
+ cpSync(oldDir, newDir, { recursive: true });
126
+ } catch {}
127
+ }
128
+ return newDir;
118
129
  }
119
130
  function loadSecretKey(keyName) {
120
131
  const secretsPath = join(homedir(), ".secrets");
@@ -135,16 +146,16 @@ function loadSecretKey(keyName) {
135
146
  return "";
136
147
  }
137
148
  function ensureDataDir(config) {
138
- const { mkdirSync } = __require("fs");
139
- mkdirSync(config.audio_dir, { recursive: true });
149
+ const { mkdirSync: mkdirSync2 } = __require("fs");
150
+ mkdirSync2(config.audio_dir, { recursive: true });
140
151
  const dbDir = config.db_path.substring(0, config.db_path.lastIndexOf("/"));
141
152
  if (dbDir)
142
- mkdirSync(dbDir, { recursive: true });
153
+ mkdirSync2(dbDir, { recursive: true });
143
154
  }
144
155
 
145
156
  // src/db/database.ts
146
157
  import { Database } from "bun:sqlite";
147
- import { mkdirSync } from "fs";
158
+ import { mkdirSync as mkdirSync2 } from "fs";
148
159
  import { dirname } from "path";
149
160
  var _db = null;
150
161
  var MIGRATIONS = [
@@ -203,6 +214,12 @@ var MIGRATIONS = [
203
214
  CREATE INDEX IF NOT EXISTS idx_recordings_created ON recordings(created_at);
204
215
  CREATE INDEX IF NOT EXISTS idx_recordings_mode ON recordings(processing_mode);
205
216
  CREATE INDEX IF NOT EXISTS idx_recording_tags_tag ON recording_tags(tag);
217
+ `,
218
+ `
219
+ ALTER TABLE recordings ADD COLUMN goal TEXT;
220
+ ALTER TABLE recordings ADD COLUMN role TEXT;
221
+ ALTER TABLE recordings ADD COLUMN task_list_id TEXT;
222
+ INSERT OR IGNORE INTO _migrations (id) VALUES (2);
206
223
  `
207
224
  ];
208
225
  function getDatabase(dbPath) {
@@ -210,7 +227,7 @@ function getDatabase(dbPath) {
210
227
  return _db;
211
228
  const path = dbPath || loadConfig().db_path;
212
229
  const dir = dirname(path);
213
- mkdirSync(dir, { recursive: true });
230
+ mkdirSync2(dir, { recursive: true });
214
231
  _db = new Database(path, { create: true });
215
232
  _db.run("PRAGMA journal_mode = WAL");
216
233
  _db.run("PRAGMA busy_timeout = 5000");
@@ -249,6 +266,9 @@ function parseRow(row) {
249
266
  agent_id: row["agent_id"] || null,
250
267
  project_id: row["project_id"] || null,
251
268
  session_id: row["session_id"] || null,
269
+ goal: row["goal"] || null,
270
+ role: row["role"] || null,
271
+ task_list_id: row["task_list_id"] || null,
252
272
  metadata: JSON.parse(row["metadata"] || "{}"),
253
273
  created_at: row["created_at"]
254
274
  };
@@ -258,8 +278,8 @@ function createRecording(input, db) {
258
278
  const id = crypto.randomUUID();
259
279
  const tagsJson = JSON.stringify(input.tags || []);
260
280
  const metadataJson = JSON.stringify(input.metadata || {});
261
- d.query(`INSERT INTO recordings (id, audio_path, raw_text, processed_text, processing_mode, model_used, enhancement_model, duration_ms, language, tags, agent_id, project_id, session_id, metadata)
262
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, input.audio_path || null, input.raw_text, input.processed_text || null, input.processing_mode || "raw", input.model_used || "gpt-4o-mini-transcribe", input.enhancement_model || null, input.duration_ms || 0, input.language || null, tagsJson, input.agent_id || null, input.project_id || null, input.session_id || null, metadataJson);
281
+ d.query(`INSERT INTO recordings (id, audio_path, raw_text, processed_text, processing_mode, model_used, enhancement_model, duration_ms, language, tags, agent_id, project_id, session_id, goal, role, task_list_id, metadata)
282
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, input.audio_path || null, input.raw_text, input.processed_text || null, input.processing_mode || "raw", input.model_used || "gpt-4o-mini-transcribe", input.enhancement_model || null, input.duration_ms || 0, input.language || null, tagsJson, input.agent_id || null, input.project_id || null, input.session_id || null, input.goal || null, input.role || null, input.task_list_id || null, metadataJson);
263
283
  if (input.tags && input.tags.length > 0) {
264
284
  const insertTag = d.query("INSERT OR IGNORE INTO recording_tags (recording_id, tag) VALUES (?, ?)");
265
285
  for (const tag of input.tags) {
@@ -925,12 +945,12 @@ program.command("projects").description("List registered projects").action(() =>
925
945
  }
926
946
  });
927
947
  program.command("init").description("Initialize .recordings/ in current directory").action(() => {
928
- const { mkdirSync: mkdirSync2, writeFileSync, existsSync: existsSync3 } = __require("fs");
948
+ const { mkdirSync: mkdirSync3, writeFileSync, existsSync: existsSync3 } = __require("fs");
929
949
  const { join: join3 } = __require("path");
930
950
  const dir = join3(process.cwd(), ".recordings");
931
951
  const audioDir = join3(dir, "audio");
932
952
  const configFile = join3(dir, "config.json");
933
- mkdirSync2(audioDir, { recursive: true });
953
+ mkdirSync3(audioDir, { recursive: true });
934
954
  if (!existsSync3(configFile)) {
935
955
  const defaultConf = {
936
956
  transcription_model: "gpt-4o-mini-transcribe",
@@ -971,19 +991,21 @@ program.command("start").description("Launch the menu bar helper app (F5 to togg
971
991
  const { homedir: getHome } = __require("os");
972
992
  const { existsSync: fileExists } = __require("fs");
973
993
  const home = getHome();
974
- const appPath = pathJoin(home, ".recordings", "RecordingsHelper.app");
975
- if (!fileExists(appPath)) {
994
+ const appPath = pathJoin(home, ".hasna", "recordings", "RecordingsHelper.app");
995
+ const oldAppPath = pathJoin(home, ".recordings", "RecordingsHelper.app");
996
+ if (!fileExists(appPath) && !fileExists(oldAppPath)) {
976
997
  console.error(chalk.red("RecordingsHelper.app not found. Run: recordings shortcut --install"));
977
998
  process.exit(1);
978
999
  }
1000
+ const resolvedAppPath = fileExists(appPath) ? appPath : oldAppPath;
979
1001
  try {
980
1002
  execSync("pkill -f RecordingsHelper", { stdio: "pipe" });
981
1003
  } catch {}
982
- execSync(`open "${appPath}"`, { stdio: "pipe" });
1004
+ execSync(`open "${resolvedAppPath}"`, { stdio: "pipe" });
983
1005
  console.log(chalk.green("Recordings helper launched \u2014 press F5 to record"));
984
1006
  if (opts.login) {
985
1007
  try {
986
- execSync(`osascript -e 'tell application "System Events" to make login item at end with properties {path:"${appPath}", hidden:true}'`, { stdio: "pipe" });
1008
+ execSync(`osascript -e 'tell application "System Events" to make login item at end with properties {path:"${resolvedAppPath}", hidden:true}'`, { stdio: "pipe" });
987
1009
  console.log(chalk.green("Added to Login Items \u2014 will start on boot"));
988
1010
  } catch {
989
1011
  console.log(chalk.yellow("Could not add to Login Items \u2014 add manually in System Settings > General > Login Items"));
@@ -1108,12 +1130,12 @@ Bye.`));
1108
1130
  });
1109
1131
  });
1110
1132
  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) => {
1111
- const { writeFileSync, mkdirSync: mkdirSync2, chmodSync, existsSync: fileExists } = __require("fs");
1133
+ const { writeFileSync, mkdirSync: mkdirSync3, chmodSync, existsSync: fileExists } = __require("fs");
1112
1134
  const { join: pathJoin } = __require("path");
1113
1135
  const { homedir: getHome } = __require("os");
1114
1136
  const home = getHome();
1115
- const scriptDir = pathJoin(home, ".recordings");
1116
- mkdirSync2(scriptDir, { recursive: true });
1137
+ const scriptDir = pathJoin(home, ".hasna", "recordings");
1138
+ mkdirSync3(scriptDir, { recursive: true });
1117
1139
  const scriptPath = pathJoin(scriptDir, "record-toggle.sh");
1118
1140
  const pidFile = pathJoin(scriptDir, ".recording.pid");
1119
1141
  const recordingsBin = pathJoin(home, ".bun", "bin", "recordings");
@@ -1165,16 +1187,16 @@ fi
1165
1187
  chmodSync(scriptPath, 493);
1166
1188
  if (opts.install) {
1167
1189
  const { execSync: exec } = __require("child_process");
1168
- const appPath = pathJoin(home, ".recordings", "RecordingsHelper.app");
1190
+ const appPath = pathJoin(home, ".hasna", "recordings", "RecordingsHelper.app");
1169
1191
  const srcSwift = pathJoin(__dirname, "..", "native", "RecordingsHelper.swift");
1170
1192
  const distApp = pathJoin(__dirname, "..", "RecordingsHelper.app");
1171
1193
  if (fileExists(pathJoin(distApp, "Contents", "MacOS", "RecordingsHelper"))) {
1172
1194
  exec(`rm -rf "${appPath}" && cp -R "${distApp}" "${appPath}"`, { stdio: "pipe", shell: "/bin/bash" });
1173
1195
  } else if (fileExists(srcSwift)) {
1174
1196
  console.log(chalk.blue("Compiling native helper app..."));
1175
- const appDir = pathJoin(home, ".recordings", "RecordingsHelper.app", "Contents", "MacOS");
1176
- mkdirSync2(appDir, { recursive: true });
1177
- const plistDir = pathJoin(home, ".recordings", "RecordingsHelper.app", "Contents");
1197
+ const appDir = pathJoin(home, ".hasna", "recordings", "RecordingsHelper.app", "Contents", "MacOS");
1198
+ mkdirSync3(appDir, { recursive: true });
1199
+ const plistDir = pathJoin(home, ".hasna", "recordings", "RecordingsHelper.app", "Contents");
1178
1200
  const plist = `<?xml version="1.0" encoding="UTF-8"?>
1179
1201
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
1180
1202
  <plist version="1.0"><dict>
@@ -1213,7 +1235,7 @@ Recordings helper installed and running!
1213
1235
  }
1214
1236
  if (opts.karabiner) {
1215
1237
  const karabinerDir = pathJoin(home, ".config", "karabiner", "assets", "complex_modifications");
1216
- mkdirSync2(karabinerDir, { recursive: true });
1238
+ mkdirSync3(karabinerDir, { recursive: true });
1217
1239
  const rule = {
1218
1240
  title: "Recordings \u2014 Fn key to toggle recording",
1219
1241
  rules: [
@@ -1253,7 +1275,7 @@ Recordings helper installed and running!
1253
1275
  }
1254
1276
  if (opts.raycast) {
1255
1277
  const raycastDir = pathJoin(home, ".config", "raycast", "script-commands");
1256
- mkdirSync2(raycastDir, { recursive: true });
1278
+ mkdirSync3(raycastDir, { recursive: true });
1257
1279
  const raycastScript = `#!/bin/bash
1258
1280
 
1259
1281
  # Required parameters:
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAmEtC,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAkBrD;AAuBD,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AA2EtC,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAkBrD;AAuBD,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC"}
@@ -1 +1 @@
1
- {"version":3,"file":"recordings.d.ts","sourceRoot":"","sources":["../../src/db/recordings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,KAAK,EACV,SAAS,EACT,oBAAoB,EACpB,eAAe,EAChB,MAAM,mBAAmB,CAAC;AAuB3B,wBAAgB,eAAe,CAC7B,KAAK,EAAE,oBAAoB,EAC3B,EAAE,CAAC,EAAE,QAAQ,GACZ,SAAS,CAqCX;AAED,wBAAgB,YAAY,CAC1B,EAAE,EAAE,MAAM,EACV,EAAE,CAAC,EAAE,QAAQ,GACZ,SAAS,GAAG,IAAI,CAgBlB;AAED,wBAAgB,cAAc,CAC5B,MAAM,CAAC,EAAE,eAAe,EACxB,EAAE,CAAC,EAAE,QAAQ,GACZ,SAAS,EAAE,CAuDb;AAED,wBAAgB,eAAe,CAC7B,EAAE,EAAE,MAAM,EACV,EAAE,CAAC,EAAE,QAAQ,GACZ,OAAO,CAIT;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,eAAe,EACxB,EAAE,CAAC,EAAE,QAAQ,GACZ,SAAS,EAAE,CAEb;AAED,wBAAgB,iBAAiB,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,CA2CA"}
1
+ {"version":3,"file":"recordings.d.ts","sourceRoot":"","sources":["../../src/db/recordings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,KAAK,EACV,SAAS,EACT,oBAAoB,EACpB,eAAe,EAChB,MAAM,mBAAmB,CAAC;AA0B3B,wBAAgB,eAAe,CAC7B,KAAK,EAAE,oBAAoB,EAC3B,EAAE,CAAC,EAAE,QAAQ,GACZ,SAAS,CAwCX;AAED,wBAAgB,YAAY,CAC1B,EAAE,EAAE,MAAM,EACV,EAAE,CAAC,EAAE,QAAQ,GACZ,SAAS,GAAG,IAAI,CAgBlB;AAED,wBAAgB,cAAc,CAC5B,MAAM,CAAC,EAAE,eAAe,EACxB,EAAE,CAAC,EAAE,QAAQ,GACZ,SAAS,EAAE,CAuDb;AAED,wBAAgB,eAAe,CAC7B,EAAE,EAAE,MAAM,EACV,EAAE,CAAC,EAAE,QAAQ,GACZ,OAAO,CAIT;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,eAAe,EACxB,EAAE,CAAC,EAAE,QAAQ,GACZ,SAAS,EAAE,CAEb;AAED,wBAAgB,iBAAiB,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,CA2CA"}
package/dist/index.js CHANGED
@@ -31,11 +31,11 @@ class EnhancementError extends Error {
31
31
  }
32
32
  // src/db/database.ts
33
33
  import { Database } from "bun:sqlite";
34
- import { mkdirSync } from "fs";
34
+ import { mkdirSync as mkdirSync2 } from "fs";
35
35
  import { dirname } from "path";
36
36
 
37
37
  // src/lib/config.ts
38
- import { existsSync, readFileSync } from "fs";
38
+ import { existsSync, readFileSync, mkdirSync, cpSync } from "fs";
39
39
  import { join } from "path";
40
40
  import { homedir } from "os";
41
41
  var DEFAULT_CONFIG = {
@@ -93,7 +93,9 @@ function loadConfig(configPath) {
93
93
  if (process.env.RECORDINGS_LANGUAGE) {
94
94
  config.language = process.env.RECORDINGS_LANGUAGE;
95
95
  }
96
- if (process.env.RECORDINGS_DB_PATH) {
96
+ if (process.env.HASNA_RECORDINGS_DB_PATH) {
97
+ config.db_path = process.env.HASNA_RECORDINGS_DB_PATH;
98
+ } else if (process.env.RECORDINGS_DB_PATH) {
97
99
  config.db_path = process.env.RECORDINGS_DB_PATH;
98
100
  }
99
101
  if (process.env.RECORDINGS_AUDIO_DIR) {
@@ -142,7 +144,16 @@ function getDataDir() {
142
144
  break;
143
145
  dir = parent;
144
146
  }
145
- return join(homedir(), ".recordings");
147
+ const home = homedir();
148
+ const newDir = join(home, ".hasna", "recordings");
149
+ const oldDir = join(home, ".recordings");
150
+ if (!existsSync(newDir) && existsSync(oldDir)) {
151
+ try {
152
+ mkdirSync(join(home, ".hasna"), { recursive: true });
153
+ cpSync(oldDir, newDir, { recursive: true });
154
+ } catch {}
155
+ }
156
+ return newDir;
146
157
  }
147
158
  function loadSecretKey(keyName) {
148
159
  const secretsPath = join(homedir(), ".secrets");
@@ -163,11 +174,11 @@ function loadSecretKey(keyName) {
163
174
  return "";
164
175
  }
165
176
  function ensureDataDir(config) {
166
- const { mkdirSync } = __require("fs");
167
- mkdirSync(config.audio_dir, { recursive: true });
177
+ const { mkdirSync: mkdirSync2 } = __require("fs");
178
+ mkdirSync2(config.audio_dir, { recursive: true });
168
179
  const dbDir = config.db_path.substring(0, config.db_path.lastIndexOf("/"));
169
180
  if (dbDir)
170
- mkdirSync(dbDir, { recursive: true });
181
+ mkdirSync2(dbDir, { recursive: true });
171
182
  }
172
183
 
173
184
  // src/db/database.ts
@@ -228,6 +239,12 @@ var MIGRATIONS = [
228
239
  CREATE INDEX IF NOT EXISTS idx_recordings_created ON recordings(created_at);
229
240
  CREATE INDEX IF NOT EXISTS idx_recordings_mode ON recordings(processing_mode);
230
241
  CREATE INDEX IF NOT EXISTS idx_recording_tags_tag ON recording_tags(tag);
242
+ `,
243
+ `
244
+ ALTER TABLE recordings ADD COLUMN goal TEXT;
245
+ ALTER TABLE recordings ADD COLUMN role TEXT;
246
+ ALTER TABLE recordings ADD COLUMN task_list_id TEXT;
247
+ INSERT OR IGNORE INTO _migrations (id) VALUES (2);
231
248
  `
232
249
  ];
233
250
  function getDatabase(dbPath) {
@@ -235,7 +252,7 @@ function getDatabase(dbPath) {
235
252
  return _db;
236
253
  const path = dbPath || loadConfig().db_path;
237
254
  const dir = dirname(path);
238
- mkdirSync(dir, { recursive: true });
255
+ mkdirSync2(dir, { recursive: true });
239
256
  _db = new Database(path, { create: true });
240
257
  _db.run("PRAGMA journal_mode = WAL");
241
258
  _db.run("PRAGMA busy_timeout = 5000");
@@ -288,6 +305,9 @@ function parseRow(row) {
288
305
  agent_id: row["agent_id"] || null,
289
306
  project_id: row["project_id"] || null,
290
307
  session_id: row["session_id"] || null,
308
+ goal: row["goal"] || null,
309
+ role: row["role"] || null,
310
+ task_list_id: row["task_list_id"] || null,
291
311
  metadata: JSON.parse(row["metadata"] || "{}"),
292
312
  created_at: row["created_at"]
293
313
  };
@@ -297,8 +317,8 @@ function createRecording(input, db) {
297
317
  const id = crypto.randomUUID();
298
318
  const tagsJson = JSON.stringify(input.tags || []);
299
319
  const metadataJson = JSON.stringify(input.metadata || {});
300
- d.query(`INSERT INTO recordings (id, audio_path, raw_text, processed_text, processing_mode, model_used, enhancement_model, duration_ms, language, tags, agent_id, project_id, session_id, metadata)
301
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, input.audio_path || null, input.raw_text, input.processed_text || null, input.processing_mode || "raw", input.model_used || "gpt-4o-mini-transcribe", input.enhancement_model || null, input.duration_ms || 0, input.language || null, tagsJson, input.agent_id || null, input.project_id || null, input.session_id || null, metadataJson);
320
+ d.query(`INSERT INTO recordings (id, audio_path, raw_text, processed_text, processing_mode, model_used, enhancement_model, duration_ms, language, tags, agent_id, project_id, session_id, goal, role, task_list_id, metadata)
321
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, input.audio_path || null, input.raw_text, input.processed_text || null, input.processing_mode || "raw", input.model_used || "gpt-4o-mini-transcribe", input.enhancement_model || null, input.duration_ms || 0, input.language || null, tagsJson, input.agent_id || null, input.project_id || null, input.session_id || null, input.goal || null, input.role || null, input.task_list_id || null, metadataJson);
302
322
  if (input.tags && input.tags.length > 0) {
303
323
  const insertTag = d.query("INSERT OR IGNORE INTO recording_tags (recording_id, tag) VALUES (?, ?)");
304
324
  for (const tag of input.tags) {
@@ -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,CAmEhE;AAgBD,wBAAgB,UAAU,IAAI,MAAM,CAanC;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;AAgBD,wBAAgB,UAAU,IAAI,MAAM,CA4BnC;AA0BD,wBAAgB,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAU5D"}
package/dist/mcp/index.js CHANGED
@@ -3994,7 +3994,7 @@ var coerce = {
3994
3994
  };
3995
3995
  var NEVER = INVALID;
3996
3996
  // src/lib/config.ts
3997
- import { existsSync, readFileSync } from "fs";
3997
+ import { existsSync, readFileSync, mkdirSync, cpSync } from "fs";
3998
3998
  import { join } from "path";
3999
3999
  import { homedir } from "os";
4000
4000
  var DEFAULT_CONFIG = {
@@ -4052,7 +4052,9 @@ function loadConfig(configPath) {
4052
4052
  if (process.env.RECORDINGS_LANGUAGE) {
4053
4053
  config.language = process.env.RECORDINGS_LANGUAGE;
4054
4054
  }
4055
- if (process.env.RECORDINGS_DB_PATH) {
4055
+ if (process.env.HASNA_RECORDINGS_DB_PATH) {
4056
+ config.db_path = process.env.HASNA_RECORDINGS_DB_PATH;
4057
+ } else if (process.env.RECORDINGS_DB_PATH) {
4056
4058
  config.db_path = process.env.RECORDINGS_DB_PATH;
4057
4059
  }
4058
4060
  if (process.env.RECORDINGS_AUDIO_DIR) {
@@ -4101,7 +4103,16 @@ function getDataDir() {
4101
4103
  break;
4102
4104
  dir = parent;
4103
4105
  }
4104
- return join(homedir(), ".recordings");
4106
+ const home = homedir();
4107
+ const newDir = join(home, ".hasna", "recordings");
4108
+ const oldDir = join(home, ".recordings");
4109
+ if (!existsSync(newDir) && existsSync(oldDir)) {
4110
+ try {
4111
+ mkdirSync(join(home, ".hasna"), { recursive: true });
4112
+ cpSync(oldDir, newDir, { recursive: true });
4113
+ } catch {}
4114
+ }
4115
+ return newDir;
4105
4116
  }
4106
4117
  function loadSecretKey(keyName) {
4107
4118
  const secretsPath = join(homedir(), ".secrets");
@@ -4122,16 +4133,16 @@ function loadSecretKey(keyName) {
4122
4133
  return "";
4123
4134
  }
4124
4135
  function ensureDataDir(config) {
4125
- const { mkdirSync } = __require("fs");
4126
- mkdirSync(config.audio_dir, { recursive: true });
4136
+ const { mkdirSync: mkdirSync2 } = __require("fs");
4137
+ mkdirSync2(config.audio_dir, { recursive: true });
4127
4138
  const dbDir = config.db_path.substring(0, config.db_path.lastIndexOf("/"));
4128
4139
  if (dbDir)
4129
- mkdirSync(dbDir, { recursive: true });
4140
+ mkdirSync2(dbDir, { recursive: true });
4130
4141
  }
4131
4142
 
4132
4143
  // src/db/database.ts
4133
4144
  import { Database } from "bun:sqlite";
4134
- import { mkdirSync } from "fs";
4145
+ import { mkdirSync as mkdirSync2 } from "fs";
4135
4146
  import { dirname } from "path";
4136
4147
  var _db = null;
4137
4148
  var MIGRATIONS = [
@@ -4203,7 +4214,7 @@ function getDatabase(dbPath) {
4203
4214
  return _db;
4204
4215
  const path = dbPath || loadConfig().db_path;
4205
4216
  const dir = dirname(path);
4206
- mkdirSync(dir, { recursive: true });
4217
+ mkdirSync2(dir, { recursive: true });
4207
4218
  _db = new Database(path, { create: true });
4208
4219
  _db.run("PRAGMA journal_mode = WAL");
4209
4220
  _db.run("PRAGMA busy_timeout = 5000");
@@ -12,6 +12,9 @@ export interface Recording {
12
12
  agent_id: string | null;
13
13
  project_id: string | null;
14
14
  session_id: string | null;
15
+ goal: string | null;
16
+ role: string | null;
17
+ task_list_id: string | null;
15
18
  metadata: Record<string, unknown>;
16
19
  created_at: string;
17
20
  }
@@ -29,6 +32,9 @@ export interface CreateRecordingInput {
29
32
  agent_id?: string;
30
33
  project_id?: string;
31
34
  session_id?: string;
35
+ goal?: string;
36
+ role?: string;
37
+ task_list_id?: string;
32
38
  metadata?: Record<string, unknown>;
33
39
  }
34
40
  export interface RecordingFilter {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,cAAc,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,UAAU,CAAC;AAEhD,MAAM,WAAW,oBAAoB;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,cAAc,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,cAAc,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAID,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAID,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,OAAO,CAAC;IACtB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAID,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAID,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,EAAE,EAAE,MAAM;CAIvB;AAED,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/recordings",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "type": "module",
5
5
  "description": "Speech-to-text recording tool with MCP and CLI — records, transcribes, and optionally enhances text using AI",
6
6
  "main": "dist/index.js",
@@ -207,7 +207,7 @@ describe("getDataDir", () => {
207
207
  test("returns a string", () => {
208
208
  const dir = getDataDir();
209
209
  expect(typeof dir).toBe("string");
210
- expect(dir).toContain(".recordings");
210
+ expect(dir).toContain("recordings");
211
211
  });
212
212
  });
213
213
 
package/src/cli/index.ts CHANGED
@@ -485,24 +485,27 @@ program
485
485
  const { existsSync: fileExists } = require("node:fs") as typeof import("node:fs");
486
486
  const home = getHome();
487
487
 
488
- const appPath = pathJoin(home, ".recordings", "RecordingsHelper.app");
488
+ const appPath = pathJoin(home, ".hasna", "recordings", "RecordingsHelper.app");
489
+ const oldAppPath = pathJoin(home, ".recordings", "RecordingsHelper.app");
489
490
 
490
- if (!fileExists(appPath)) {
491
+ if (!fileExists(appPath) && !fileExists(oldAppPath)) {
491
492
  console.error(chalk.red("RecordingsHelper.app not found. Run: recordings shortcut --install"));
492
493
  process.exit(1);
493
494
  }
494
495
 
496
+ const resolvedAppPath = fileExists(appPath) ? appPath : oldAppPath;
497
+
495
498
  // Kill existing instance
496
499
  try { execSync("pkill -f RecordingsHelper", { stdio: "pipe" }); } catch { /* not running */ }
497
500
 
498
501
  // Launch
499
- execSync(`open "${appPath}"`, { stdio: "pipe" });
502
+ execSync(`open "${resolvedAppPath}"`, { stdio: "pipe" });
500
503
  console.log(chalk.green("Recordings helper launched — press F5 to record"));
501
504
 
502
505
  if (opts.login) {
503
506
  try {
504
507
  execSync(
505
- `osascript -e 'tell application "System Events" to make login item at end with properties {path:"${appPath}", hidden:true}'`,
508
+ `osascript -e 'tell application "System Events" to make login item at end with properties {path:"${resolvedAppPath}", hidden:true}'`,
506
509
  { stdio: "pipe" }
507
510
  );
508
511
  console.log(chalk.green("Added to Login Items — will start on boot"));
@@ -688,7 +691,7 @@ program
688
691
  const { homedir: getHome } = require("node:os") as typeof import("node:os");
689
692
  const home = getHome();
690
693
 
691
- const scriptDir = pathJoin(home, ".recordings");
694
+ const scriptDir = pathJoin(home, ".hasna", "recordings");
692
695
  mkdirSync(scriptDir, { recursive: true });
693
696
 
694
697
  const scriptPath = pathJoin(scriptDir, "record-toggle.sh");
@@ -747,7 +750,7 @@ fi
747
750
  // Install the native menu bar app — no config needed, just works with F5
748
751
  const { execSync: exec } = require("node:child_process") as typeof import("node:child_process");
749
752
 
750
- const appPath = pathJoin(home, ".recordings", "RecordingsHelper.app");
753
+ const appPath = pathJoin(home, ".hasna", "recordings", "RecordingsHelper.app");
751
754
  const srcSwift = pathJoin(__dirname, "..", "native", "RecordingsHelper.swift");
752
755
  const distApp = pathJoin(__dirname, "..", "RecordingsHelper.app");
753
756
 
@@ -757,10 +760,10 @@ fi
757
760
  } else if (fileExists(srcSwift)) {
758
761
  // Compile from source
759
762
  console.log(chalk.blue("Compiling native helper app..."));
760
- const appDir = pathJoin(home, ".recordings", "RecordingsHelper.app", "Contents", "MacOS");
763
+ const appDir = pathJoin(home, ".hasna", "recordings", "RecordingsHelper.app", "Contents", "MacOS");
761
764
  mkdirSync(appDir, { recursive: true });
762
765
 
763
- const plistDir = pathJoin(home, ".recordings", "RecordingsHelper.app", "Contents");
766
+ const plistDir = pathJoin(home, ".hasna", "recordings", "RecordingsHelper.app", "Contents");
764
767
  const plist = `<?xml version="1.0" encoding="UTF-8"?>
765
768
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
766
769
  <plist version="1.0"><dict>
package/src/lib/config.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, readFileSync } from "fs";
1
+ import { existsSync, readFileSync, mkdirSync, cpSync } from "fs";
2
2
  import { join } from "path";
3
3
  import { homedir } from "os";
4
4
  import type { RecordingsConfig } from "../types/index.js";
@@ -67,7 +67,9 @@ export function loadConfig(configPath?: string): RecordingsConfig {
67
67
  if (process.env.RECORDINGS_LANGUAGE) {
68
68
  config.language = process.env.RECORDINGS_LANGUAGE;
69
69
  }
70
- if (process.env.RECORDINGS_DB_PATH) {
70
+ if (process.env.HASNA_RECORDINGS_DB_PATH) {
71
+ config.db_path = process.env.HASNA_RECORDINGS_DB_PATH;
72
+ } else if (process.env.RECORDINGS_DB_PATH) {
71
73
  config.db_path = process.env.RECORDINGS_DB_PATH;
72
74
  }
73
75
  if (process.env.RECORDINGS_AUDIO_DIR) {
@@ -115,7 +117,7 @@ function findConfigFile(): string | null {
115
117
  }
116
118
 
117
119
  export function getDataDir(): string {
118
- // Check for .recordings in cwd hierarchy
120
+ // Check for .recordings in cwd hierarchy (project-local)
119
121
  let dir = process.cwd();
120
122
  const root = "/";
121
123
  while (dir !== root) {
@@ -125,8 +127,23 @@ export function getDataDir(): string {
125
127
  if (parent === dir) break;
126
128
  dir = parent;
127
129
  }
128
- // Fall back to ~/.recordings
129
- return join(homedir(), ".recordings");
130
+
131
+ // Global: ~/.hasna/recordings (with backward compat from ~/.recordings)
132
+ const home = homedir();
133
+ const newDir = join(home, ".hasna", "recordings");
134
+ const oldDir = join(home, ".recordings");
135
+
136
+ // Auto-migrate from old location if new dir doesn't exist yet
137
+ if (!existsSync(newDir) && existsSync(oldDir)) {
138
+ try {
139
+ mkdirSync(join(home, ".hasna"), { recursive: true });
140
+ cpSync(oldDir, newDir, { recursive: true });
141
+ } catch {
142
+ // Fall through to use new dir
143
+ }
144
+ }
145
+
146
+ return newDir;
130
147
  }
131
148
 
132
149
  function loadSecretKey(keyName: string): string {
@@ -69,7 +69,7 @@ final class RecordingEngine: ObservableObject {
69
69
  private let fnMonitor = FnKeyMonitor()
70
70
 
71
71
  let home = FileManager.default.homeDirectoryForCurrentUser.path
72
- private var audioDir: String { "\(home)/.recordings/audio" }
72
+ private var audioDir: String { "\(home)/.hasna/recordings/audio" }
73
73
 
74
74
  init() {
75
75
  try? FileManager.default.createDirectory(atPath: audioDir, withIntermediateDirectories: true)
@@ -22,7 +22,7 @@ final class VoiceShortcuts: ObservableObject {
22
22
 
23
23
  private let storageURL: URL = {
24
24
  let home = FileManager.default.homeDirectoryForCurrentUser
25
- return home.appendingPathComponent(".recordings/voice-shortcuts.json")
25
+ return home.appendingPathComponent(".hasna/recordings/voice-shortcuts.json")
26
26
  }()
27
27
 
28
28
  init() {
@@ -33,8 +33,8 @@ fi
33
33
 
34
34
  echo "✓ Built $APP_DIR"
35
35
  echo ""
36
- echo "To install to ~/.recordings/:"
37
- echo " cp -r $APP_DIR ~/.recordings/Recordings.app"
36
+ echo "To install to ~/.hasna/recordings/:"
37
+ echo " cp -r $APP_DIR ~/.hasna/recordings/Recordings.app"
38
38
  echo ""
39
39
  echo "To run:"
40
40
  echo " open $APP_DIR"
@@ -18,12 +18,19 @@ class AppDelegate: NSObject, NSApplicationDelegate {
18
18
 
19
19
  let recordingsDir: String = {
20
20
  let home = FileManager.default.homeDirectoryForCurrentUser.path
21
- return "\(home)/.recordings"
21
+ let newDir = "\(home)/.hasna/recordings"
22
+ let oldDir = "\(home)/.recordings"
23
+ // Auto-migrate from old location
24
+ if !FileManager.default.fileExists(atPath: newDir) && FileManager.default.fileExists(atPath: oldDir) {
25
+ try? FileManager.default.createDirectory(atPath: "\(home)/.hasna", withIntermediateDirectories: true)
26
+ try? FileManager.default.copyItem(atPath: oldDir, toPath: newDir)
27
+ }
28
+ return newDir
22
29
  }()
23
30
 
24
31
  let audioDir: String = {
25
32
  let home = FileManager.default.homeDirectoryForCurrentUser.path
26
- return "\(home)/.recordings/audio"
33
+ return "\(home)/.hasna/recordings/audio"
27
34
  }()
28
35
 
29
36
  let recordingsBin: String = {