@hasna/brains 0.0.29 → 0.0.31

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
@@ -13151,6 +13151,20 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
13151
13151
  init_adapter();
13152
13152
  });
13153
13153
 
13154
+ // src/lib/gatherers/tags.ts
13155
+ function parseTagList(value) {
13156
+ if (!value?.trim())
13157
+ return [];
13158
+ try {
13159
+ const parsed = JSON.parse(value);
13160
+ if (!Array.isArray(parsed))
13161
+ return [];
13162
+ return parsed.filter((tag) => typeof tag === "string");
13163
+ } catch {
13164
+ return [];
13165
+ }
13166
+ }
13167
+
13154
13168
  // src/lib/gatherers/todos.ts
13155
13169
  var exports_todos = {};
13156
13170
  __export(exports_todos, {
@@ -13169,7 +13183,7 @@ Description: ${task.description}` : ""}`;
13169
13183
  description: task.description ?? "",
13170
13184
  status: task.status,
13171
13185
  priority: task.priority,
13172
- tags: JSON.parse(task.tags ?? "[]"),
13186
+ tags: parseTagList(task.tags),
13173
13187
  created_at: task.created_at
13174
13188
  };
13175
13189
  return {
@@ -13208,7 +13222,7 @@ ${matched.map((t) => `- [${t.short_id ?? t.id}] ${t.title} (${t.status})`).join(
13208
13222
  };
13209
13223
  }
13210
13224
  async function gatherFromTodos(options = {}) {
13211
- const dbPath = join9(homedir8(), ".todos", "todos.db");
13225
+ const dbPath = join9(options.homeDir ?? homedir8(), ".todos", "todos.db");
13212
13226
  const db = new Database3(dbPath, { readonly: true, create: false });
13213
13227
  try {
13214
13228
  let query = "SELECT * FROM tasks WHERE 1=1";
@@ -13270,7 +13284,7 @@ Summary: ${memory.summary}` : memory.value
13270
13284
  };
13271
13285
  }
13272
13286
  function memoryToSaveExample(memory) {
13273
- const tags = JSON.parse(memory.tags ?? "[]");
13287
+ const tags = parseTagList(memory.tags);
13274
13288
  return {
13275
13289
  messages: [
13276
13290
  { role: "system", content: SYSTEM_PROMPT2 },
@@ -13301,7 +13315,7 @@ ${matched.map((m) => `- ${m.key}: ${m.value.slice(0, 120)}${m.value.length > 120
13301
13315
  };
13302
13316
  }
13303
13317
  async function gatherFromMementos(options = {}) {
13304
- const dbPath = join10(homedir9(), ".mementos", "mementos.db");
13318
+ const dbPath = join10(options.homeDir ?? homedir9(), ".mementos", "mementos.db");
13305
13319
  const db = new Database4(dbPath, { readonly: true, create: false });
13306
13320
  try {
13307
13321
  let query = "SELECT * FROM memories WHERE status = 'active'";
@@ -13372,7 +13386,7 @@ function windowToExample(window2) {
13372
13386
  return { messages };
13373
13387
  }
13374
13388
  async function gatherFromConversations(options = {}) {
13375
- const dbPath = join11(homedir10(), ".conversations", "messages.db");
13389
+ const dbPath = join11(options.homeDir ?? homedir10(), ".conversations", "messages.db");
13376
13390
  const db = new Database5(dbPath, { readonly: true, create: false });
13377
13391
  try {
13378
13392
  let query = "SELECT * FROM messages WHERE 1=1";
@@ -13433,7 +13447,7 @@ function extractText(content) {
13433
13447
  async function gatherFromSessions(options = {}) {
13434
13448
  const { limit: limit2 = 1000 } = options;
13435
13449
  const examples = [];
13436
- const claudeDir = join12(homedir11(), ".claude", "projects");
13450
+ const claudeDir = join12(options.homeDir ?? homedir11(), ".claude", "projects");
13437
13451
  if (!existsSync10(claudeDir)) {
13438
13452
  return { source: "sessions", examples: [], count: 0 };
13439
13453
  }
@@ -18128,7 +18142,7 @@ function printInfo(message) {
18128
18142
  }
18129
18143
 
18130
18144
  // src/cli/commands/models.ts
18131
- import { randomUUID as randomUUID3 } from "crypto";
18145
+ import { randomUUID as randomUUID5 } from "crypto";
18132
18146
 
18133
18147
  // node_modules/openai/internal/qs/formats.mjs
18134
18148
  var default_format = "RFC3986";
@@ -23618,16 +23632,48 @@ var openai_default = OpenAI;
23618
23632
  import { readFileSync as readFileSync5 } from "fs";
23619
23633
 
23620
23634
  // src/lib/config.ts
23621
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync7, readdirSync as readdirSync4, copyFileSync as copyFileSync2, statSync } from "fs";
23635
+ import {
23636
+ chmodSync,
23637
+ copyFileSync as copyFileSync2,
23638
+ existsSync as existsSync7,
23639
+ lstatSync,
23640
+ mkdirSync as mkdirSync4,
23641
+ readFileSync as readFileSync3,
23642
+ readdirSync as readdirSync4,
23643
+ renameSync,
23644
+ statSync,
23645
+ unlinkSync as unlinkSync2,
23646
+ writeFileSync as writeFileSync3
23647
+ } from "fs";
23648
+ import { randomUUID as randomUUID3 } from "crypto";
23622
23649
  import { join as join7, dirname as dirname2 } from "path";
23623
23650
  import { homedir as homedir6 } from "os";
23624
23651
  var CONFIG_KEYS = ["OPENAI_API_KEY", "THINKER_LABS_API_KEY", "THINKER_LABS_BASE_URL"];
23652
+ var CONFIG_FILE_NAME = "config.json";
23653
+ function restrictConfigFilePermissions(configPath) {
23654
+ try {
23655
+ if (!lstatSync(configPath).isFile())
23656
+ return;
23657
+ chmodSync(configPath, 384);
23658
+ } catch {}
23659
+ }
23660
+ function ensureConfigDirectory(dirPath) {
23661
+ mkdirSync4(dirPath, { recursive: true, mode: 448 });
23662
+ try {
23663
+ if (!statSync(dirPath).isDirectory())
23664
+ return;
23665
+ chmodSync(dirPath, 448);
23666
+ } catch {}
23667
+ }
23625
23668
  function resolveConfigPath() {
23626
23669
  const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir6();
23627
- const newDir = join7(home, ".hasna", "brains");
23670
+ const hasnaDir = join7(home, ".hasna");
23671
+ const newDir = join7(hasnaDir, "brains");
23628
23672
  const oldDir = join7(home, ".brains");
23673
+ const configPath = join7(newDir, CONFIG_FILE_NAME);
23674
+ ensureConfigDirectory(hasnaDir);
23629
23675
  if (existsSync7(oldDir) && !existsSync7(newDir)) {
23630
- mkdirSync4(newDir, { recursive: true });
23676
+ ensureConfigDirectory(newDir);
23631
23677
  try {
23632
23678
  for (const file of readdirSync4(oldDir)) {
23633
23679
  const oldPath = join7(oldDir, file);
@@ -23635,28 +23681,46 @@ function resolveConfigPath() {
23635
23681
  try {
23636
23682
  if (statSync(oldPath).isFile()) {
23637
23683
  copyFileSync2(oldPath, newPath);
23684
+ if (file === CONFIG_FILE_NAME)
23685
+ restrictConfigFilePermissions(newPath);
23638
23686
  }
23639
23687
  } catch {}
23640
23688
  }
23641
23689
  } catch {}
23642
23690
  }
23643
- mkdirSync4(newDir, { recursive: true });
23644
- return join7(newDir, "config.json");
23691
+ ensureConfigDirectory(newDir);
23692
+ return configPath;
23645
23693
  }
23646
- var CONFIG_PATH2 = resolveConfigPath();
23647
23694
  function readConfigFile() {
23648
- if (!existsSync7(CONFIG_PATH2))
23695
+ const configPath = resolveConfigPath();
23696
+ if (!existsSync7(configPath))
23649
23697
  return {};
23650
23698
  try {
23651
- return JSON.parse(readFileSync3(CONFIG_PATH2, "utf-8"));
23699
+ return JSON.parse(readFileSync3(configPath, "utf-8"));
23652
23700
  } catch {
23653
23701
  return {};
23654
23702
  }
23655
23703
  }
23656
23704
  function writeConfigFile(data) {
23657
- mkdirSync4(dirname2(CONFIG_PATH2), { recursive: true });
23658
- writeFileSync3(CONFIG_PATH2, JSON.stringify(data, null, 2) + `
23659
- `, "utf-8");
23705
+ const configPath = resolveConfigPath();
23706
+ const configDir = dirname2(configPath);
23707
+ const tempPath = join7(configDir, `.config.json.${randomUUID3()}.tmp`);
23708
+ ensureConfigDirectory(configDir);
23709
+ if (existsSync7(configPath))
23710
+ restrictConfigFilePermissions(configPath);
23711
+ try {
23712
+ writeFileSync3(tempPath, JSON.stringify(data, null, 2) + `
23713
+ `, { encoding: "utf-8", mode: 384, flag: "wx" });
23714
+ restrictConfigFilePermissions(tempPath);
23715
+ renameSync(tempPath, configPath);
23716
+ restrictConfigFilePermissions(configPath);
23717
+ } catch (err) {
23718
+ try {
23719
+ if (existsSync7(tempPath))
23720
+ unlinkSync2(tempPath);
23721
+ } catch {}
23722
+ throw err;
23723
+ }
23660
23724
  }
23661
23725
  function getConfigValue(key) {
23662
23726
  if (process.env[key])
@@ -24068,7 +24132,7 @@ function registerModelsCommands(program2) {
24068
24132
  printInfo(`Model already tracked as: ${existing.id}`);
24069
24133
  return;
24070
24134
  }
24071
- const modelId = randomUUID3();
24135
+ const modelId = randomUUID5();
24072
24136
  const now2 = Date.now();
24073
24137
  const name = opts.name ?? result.fineTunedModel ?? `imported-${jobId}`;
24074
24138
  await db.insert(fineTunedModels).values({
@@ -24082,7 +24146,7 @@ function registerModelsCommands(program2) {
24082
24146
  updatedAt: now2
24083
24147
  });
24084
24148
  await db.insert(trainingJobs).values({
24085
- id: randomUUID3(),
24149
+ id: randomUUID5(),
24086
24150
  modelId,
24087
24151
  provider: opts.provider,
24088
24152
  status: result.status,
@@ -24105,7 +24169,7 @@ function registerModelsCommands(program2) {
24105
24169
  }
24106
24170
 
24107
24171
  // src/cli/commands/finetune.ts
24108
- import { randomUUID as randomUUID5 } from "crypto";
24172
+ import { randomUUID as randomUUID6 } from "crypto";
24109
24173
  import { existsSync as existsSync9 } from "fs";
24110
24174
  function registerFinetuneCommands(program2) {
24111
24175
  const finetuneCmd = program2.command("finetune").description("Manage fine-tuning jobs");
@@ -24147,7 +24211,7 @@ function registerFinetuneCommands(program2) {
24147
24211
  ({ jobId, status: jobStatus } = await tl.createFineTuneJob(fileId, opts.baseModel, opts.name));
24148
24212
  }
24149
24213
  const db = getDb();
24150
- const modelId = randomUUID5();
24214
+ const modelId = randomUUID6();
24151
24215
  const now2 = Date.now();
24152
24216
  await db.insert(fineTunedModels).values({
24153
24217
  id: modelId,
@@ -24159,7 +24223,7 @@ function registerFinetuneCommands(program2) {
24159
24223
  createdAt: now2,
24160
24224
  updatedAt: now2
24161
24225
  });
24162
- const trainingJobId = randomUUID5();
24226
+ const trainingJobId = randomUUID6();
24163
24227
  await db.insert(trainingJobs).values({
24164
24228
  id: trainingJobId,
24165
24229
  modelId,
@@ -24299,7 +24363,7 @@ function registerFinetuneCommands(program2) {
24299
24363
  }
24300
24364
 
24301
24365
  // src/cli/commands/data.ts
24302
- import { randomUUID as randomUUID6 } from "crypto";
24366
+ import { randomUUID as randomUUID7 } from "crypto";
24303
24367
  import { readFileSync as readFileSync6, existsSync as existsSync11, mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "fs";
24304
24368
  import { dirname as dirname3, join as join13 } from "path";
24305
24369
  import { homedir as homedir12 } from "os";
@@ -24349,7 +24413,7 @@ function registerDataCommands(program2) {
24349
24413
  `) + `
24350
24414
  `, "utf8");
24351
24415
  await db.insert(trainingDatasets).values({
24352
- id: randomUUID6(),
24416
+ id: randomUUID7(),
24353
24417
  source,
24354
24418
  filePath,
24355
24419
  exampleCount: count,
@@ -24439,7 +24503,7 @@ function registerDataCommands(program2) {
24439
24503
  `, "utf8");
24440
24504
  const db = getDb();
24441
24505
  await db.insert(trainingDatasets).values({
24442
- id: randomUUID6(),
24506
+ id: randomUUID7(),
24443
24507
  source: "mixed",
24444
24508
  filePath: opts.output,
24445
24509
  exampleCount: finalLines.length,
package/dist/index.js CHANGED
@@ -18984,16 +18984,48 @@ var openai_default = OpenAI;
18984
18984
  import { readFileSync as readFileSync3 } from "fs";
18985
18985
 
18986
18986
  // src/lib/config.ts
18987
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, existsSync as existsSync3, readdirSync as readdirSync2, copyFileSync as copyFileSync2, statSync } from "fs";
18987
+ import {
18988
+ chmodSync,
18989
+ copyFileSync as copyFileSync2,
18990
+ existsSync as existsSync3,
18991
+ lstatSync,
18992
+ mkdirSync as mkdirSync3,
18993
+ readFileSync as readFileSync2,
18994
+ readdirSync as readdirSync2,
18995
+ renameSync,
18996
+ statSync,
18997
+ unlinkSync,
18998
+ writeFileSync as writeFileSync2
18999
+ } from "fs";
19000
+ import { randomUUID } from "crypto";
18988
19001
  import { join as join4, dirname as dirname2 } from "path";
18989
19002
  import { homedir as homedir6 } from "os";
18990
19003
  var CONFIG_KEYS = ["OPENAI_API_KEY", "THINKER_LABS_API_KEY", "THINKER_LABS_BASE_URL"];
19004
+ var CONFIG_FILE_NAME = "config.json";
19005
+ function restrictConfigFilePermissions(configPath) {
19006
+ try {
19007
+ if (!lstatSync(configPath).isFile())
19008
+ return;
19009
+ chmodSync(configPath, 384);
19010
+ } catch {}
19011
+ }
19012
+ function ensureConfigDirectory(dirPath) {
19013
+ mkdirSync3(dirPath, { recursive: true, mode: 448 });
19014
+ try {
19015
+ if (!statSync(dirPath).isDirectory())
19016
+ return;
19017
+ chmodSync(dirPath, 448);
19018
+ } catch {}
19019
+ }
18991
19020
  function resolveConfigPath() {
18992
19021
  const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir6();
18993
- const newDir = join4(home, ".hasna", "brains");
19022
+ const hasnaDir = join4(home, ".hasna");
19023
+ const newDir = join4(hasnaDir, "brains");
18994
19024
  const oldDir = join4(home, ".brains");
19025
+ const configPath = join4(newDir, CONFIG_FILE_NAME);
19026
+ ensureConfigDirectory(hasnaDir);
18995
19027
  if (existsSync3(oldDir) && !existsSync3(newDir)) {
18996
- mkdirSync3(newDir, { recursive: true });
19028
+ ensureConfigDirectory(newDir);
18997
19029
  try {
18998
19030
  for (const file of readdirSync2(oldDir)) {
18999
19031
  const oldPath = join4(oldDir, file);
@@ -19001,28 +19033,46 @@ function resolveConfigPath() {
19001
19033
  try {
19002
19034
  if (statSync(oldPath).isFile()) {
19003
19035
  copyFileSync2(oldPath, newPath);
19036
+ if (file === CONFIG_FILE_NAME)
19037
+ restrictConfigFilePermissions(newPath);
19004
19038
  }
19005
19039
  } catch {}
19006
19040
  }
19007
19041
  } catch {}
19008
19042
  }
19009
- mkdirSync3(newDir, { recursive: true });
19010
- return join4(newDir, "config.json");
19043
+ ensureConfigDirectory(newDir);
19044
+ return configPath;
19011
19045
  }
19012
- var CONFIG_PATH2 = resolveConfigPath();
19013
19046
  function readConfigFile() {
19014
- if (!existsSync3(CONFIG_PATH2))
19047
+ const configPath = resolveConfigPath();
19048
+ if (!existsSync3(configPath))
19015
19049
  return {};
19016
19050
  try {
19017
- return JSON.parse(readFileSync2(CONFIG_PATH2, "utf-8"));
19051
+ return JSON.parse(readFileSync2(configPath, "utf-8"));
19018
19052
  } catch {
19019
19053
  return {};
19020
19054
  }
19021
19055
  }
19022
19056
  function writeConfigFile(data) {
19023
- mkdirSync3(dirname2(CONFIG_PATH2), { recursive: true });
19024
- writeFileSync2(CONFIG_PATH2, JSON.stringify(data, null, 2) + `
19025
- `, "utf-8");
19057
+ const configPath = resolveConfigPath();
19058
+ const configDir = dirname2(configPath);
19059
+ const tempPath = join4(configDir, `.config.json.${randomUUID()}.tmp`);
19060
+ ensureConfigDirectory(configDir);
19061
+ if (existsSync3(configPath))
19062
+ restrictConfigFilePermissions(configPath);
19063
+ try {
19064
+ writeFileSync2(tempPath, JSON.stringify(data, null, 2) + `
19065
+ `, { encoding: "utf-8", mode: 384, flag: "wx" });
19066
+ restrictConfigFilePermissions(tempPath);
19067
+ renameSync(tempPath, configPath);
19068
+ restrictConfigFilePermissions(configPath);
19069
+ } catch (err) {
19070
+ try {
19071
+ if (existsSync3(tempPath))
19072
+ unlinkSync(tempPath);
19073
+ } catch {}
19074
+ throw err;
19075
+ }
19026
19076
  }
19027
19077
  function getConfigValue(key) {
19028
19078
  if (process.env[key])
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,WAAW,8EAA+E,CAAC;AACxG,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AA+CrD,wBAAgB,cAAc,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,CAIjE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAIlE;AAED,wBAAgB,UAAU,IAAI,KAAK,CAAC;IAAE,GAAG,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC,CAOvG;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAItD"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAoBA,eAAO,MAAM,WAAW,8EAA+E,CAAC;AACxG,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAuFrD,wBAAgB,cAAc,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,CAIjE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAIlE;AAED,wBAAgB,UAAU,IAAI,KAAK,CAAC;IAAE,GAAG,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC,CAOvG;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAItD"}
@@ -1 +1 @@
1
- {"version":3,"file":"mementos.d.ts","sourceRoot":"","sources":["../../../src/lib/gatherers/mementos.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAmB,MAAM,YAAY,CAAA;AAqEhF,wBAAsB,kBAAkB,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,YAAY,CAAC,CA4C7F"}
1
+ {"version":3,"file":"mementos.d.ts","sourceRoot":"","sources":["../../../src/lib/gatherers/mementos.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAmB,MAAM,YAAY,CAAA;AAsEhF,wBAAsB,kBAAkB,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,YAAY,CAAC,CA4C7F"}
@@ -0,0 +1,2 @@
1
+ export declare function parseTagList(value: string | null | undefined): string[];
2
+ //# sourceMappingURL=tags.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../../src/lib/gatherers/tags.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,EAAE,CAUvE"}
@@ -1 +1 @@
1
- {"version":3,"file":"todos.d.ts","sourceRoot":"","sources":["../../../src/lib/gatherers/todos.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAmB,MAAM,YAAY,CAAA;AAqEhF,wBAAsB,eAAe,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,YAAY,CAAC,CA+C1F"}
1
+ {"version":3,"file":"todos.d.ts","sourceRoot":"","sources":["../../../src/lib/gatherers/todos.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAmB,MAAM,YAAY,CAAA;AAsEhF,wBAAsB,eAAe,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,YAAY,CAAC,CA+C1F"}
@@ -13,5 +13,6 @@ export interface GathererOptions {
13
13
  limit?: number;
14
14
  since?: Date;
15
15
  outputDir?: string;
16
+ homeDir?: string;
16
17
  }
17
18
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/lib/gatherers/types.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,KAAK,CAAC;QACd,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAA;QACrC,OAAO,EAAE,MAAM,CAAA;KAChB,CAAC,CAAA;CACH;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,IAAI,CAAA;IACZ,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/lib/gatherers/types.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,KAAK,CAAC;QACd,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAA;QACrC,OAAO,EAAE,MAAM,CAAA;KAChB,CAAC,CAAA;CACH;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,IAAI,CAAA;IACZ,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB"}
package/dist/mcp/index.js CHANGED
@@ -19495,15 +19495,46 @@ var openai_default = OpenAI;
19495
19495
  import { readFileSync as readFileSync3 } from "fs";
19496
19496
 
19497
19497
  // src/lib/config.ts
19498
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, existsSync as existsSync3, readdirSync as readdirSync2, copyFileSync as copyFileSync2, statSync } from "fs";
19498
+ import {
19499
+ chmodSync,
19500
+ copyFileSync as copyFileSync2,
19501
+ existsSync as existsSync3,
19502
+ lstatSync,
19503
+ mkdirSync as mkdirSync3,
19504
+ readFileSync as readFileSync2,
19505
+ readdirSync as readdirSync2,
19506
+ renameSync,
19507
+ statSync,
19508
+ unlinkSync,
19509
+ writeFileSync as writeFileSync2
19510
+ } from "fs";
19499
19511
  import { join as join4, dirname as dirname2 } from "path";
19500
19512
  import { homedir as homedir6 } from "os";
19513
+ var CONFIG_FILE_NAME = "config.json";
19514
+ function restrictConfigFilePermissions(configPath) {
19515
+ try {
19516
+ if (!lstatSync(configPath).isFile())
19517
+ return;
19518
+ chmodSync(configPath, 384);
19519
+ } catch {}
19520
+ }
19521
+ function ensureConfigDirectory(dirPath) {
19522
+ mkdirSync3(dirPath, { recursive: true, mode: 448 });
19523
+ try {
19524
+ if (!statSync(dirPath).isDirectory())
19525
+ return;
19526
+ chmodSync(dirPath, 448);
19527
+ } catch {}
19528
+ }
19501
19529
  function resolveConfigPath() {
19502
19530
  const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir6();
19503
- const newDir = join4(home, ".hasna", "brains");
19531
+ const hasnaDir = join4(home, ".hasna");
19532
+ const newDir = join4(hasnaDir, "brains");
19504
19533
  const oldDir = join4(home, ".brains");
19534
+ const configPath = join4(newDir, CONFIG_FILE_NAME);
19535
+ ensureConfigDirectory(hasnaDir);
19505
19536
  if (existsSync3(oldDir) && !existsSync3(newDir)) {
19506
- mkdirSync3(newDir, { recursive: true });
19537
+ ensureConfigDirectory(newDir);
19507
19538
  try {
19508
19539
  for (const file of readdirSync2(oldDir)) {
19509
19540
  const oldPath = join4(oldDir, file);
@@ -19511,20 +19542,22 @@ function resolveConfigPath() {
19511
19542
  try {
19512
19543
  if (statSync(oldPath).isFile()) {
19513
19544
  copyFileSync2(oldPath, newPath);
19545
+ if (file === CONFIG_FILE_NAME)
19546
+ restrictConfigFilePermissions(newPath);
19514
19547
  }
19515
19548
  } catch {}
19516
19549
  }
19517
19550
  } catch {}
19518
19551
  }
19519
- mkdirSync3(newDir, { recursive: true });
19520
- return join4(newDir, "config.json");
19552
+ ensureConfigDirectory(newDir);
19553
+ return configPath;
19521
19554
  }
19522
- var CONFIG_PATH2 = resolveConfigPath();
19523
19555
  function readConfigFile() {
19524
- if (!existsSync3(CONFIG_PATH2))
19556
+ const configPath = resolveConfigPath();
19557
+ if (!existsSync3(configPath))
19525
19558
  return {};
19526
19559
  try {
19527
- return JSON.parse(readFileSync2(CONFIG_PATH2, "utf-8"));
19560
+ return JSON.parse(readFileSync2(configPath, "utf-8"));
19528
19561
  } catch {
19529
19562
  return {};
19530
19563
  }
@@ -19756,6 +19789,22 @@ class ThinkerLabsProvider {
19756
19789
  import { Database as Database3 } from "bun:sqlite";
19757
19790
  import { homedir as homedir8 } from "os";
19758
19791
  import { join as join7 } from "path";
19792
+
19793
+ // src/lib/gatherers/tags.ts
19794
+ function parseTagList(value) {
19795
+ if (!value?.trim())
19796
+ return [];
19797
+ try {
19798
+ const parsed = JSON.parse(value);
19799
+ if (!Array.isArray(parsed))
19800
+ return [];
19801
+ return parsed.filter((tag) => typeof tag === "string");
19802
+ } catch {
19803
+ return [];
19804
+ }
19805
+ }
19806
+
19807
+ // src/lib/gatherers/todos.ts
19759
19808
  var SYSTEM_PROMPT = "You are a task management assistant that helps users create, update, search, and manage tasks and projects.";
19760
19809
  function taskToCreateExample(task) {
19761
19810
  const userMsg = `Create a task: ${task.title}${task.description ? `
@@ -19767,7 +19816,7 @@ Description: ${task.description}` : ""}`;
19767
19816
  description: task.description ?? "",
19768
19817
  status: task.status,
19769
19818
  priority: task.priority,
19770
- tags: JSON.parse(task.tags ?? "[]"),
19819
+ tags: parseTagList(task.tags),
19771
19820
  created_at: task.created_at
19772
19821
  };
19773
19822
  return {
@@ -19806,7 +19855,7 @@ ${matched.map((t) => `- [${t.short_id ?? t.id}] ${t.title} (${t.status})`).join(
19806
19855
  };
19807
19856
  }
19808
19857
  async function gatherFromTodos(options = {}) {
19809
- const dbPath = join7(homedir8(), ".todos", "todos.db");
19858
+ const dbPath = join7(options.homeDir ?? homedir8(), ".todos", "todos.db");
19810
19859
  const db = new Database3(dbPath, { readonly: true, create: false });
19811
19860
  try {
19812
19861
  let query = "SELECT * FROM tasks WHERE 1=1";
@@ -19863,7 +19912,7 @@ Summary: ${memory.summary}` : memory.value
19863
19912
  };
19864
19913
  }
19865
19914
  function memoryToSaveExample(memory) {
19866
- const tags = JSON.parse(memory.tags ?? "[]");
19915
+ const tags = parseTagList(memory.tags);
19867
19916
  return {
19868
19917
  messages: [
19869
19918
  { role: "system", content: SYSTEM_PROMPT2 },
@@ -19894,7 +19943,7 @@ ${matched.map((m) => `- ${m.key}: ${m.value.slice(0, 120)}${m.value.length > 120
19894
19943
  };
19895
19944
  }
19896
19945
  async function gatherFromMementos(options = {}) {
19897
- const dbPath = join9(homedir9(), ".mementos", "mementos.db");
19946
+ const dbPath = join9(options.homeDir ?? homedir9(), ".mementos", "mementos.db");
19898
19947
  const db = new Database4(dbPath, { readonly: true, create: false });
19899
19948
  try {
19900
19949
  let query = "SELECT * FROM memories WHERE status = 'active'";
@@ -19960,7 +20009,7 @@ function windowToExample(window2) {
19960
20009
  return { messages };
19961
20010
  }
19962
20011
  async function gatherFromConversations(options = {}) {
19963
- const dbPath = join10(homedir10(), ".conversations", "messages.db");
20012
+ const dbPath = join10(options.homeDir ?? homedir10(), ".conversations", "messages.db");
19964
20013
  const db = new Database5(dbPath, { readonly: true, create: false });
19965
20014
  try {
19966
20015
  let query = "SELECT * FROM messages WHERE 1=1";
@@ -20016,7 +20065,7 @@ function extractText(content) {
20016
20065
  async function gatherFromSessions(options = {}) {
20017
20066
  const { limit: limit2 = 1000 } = options;
20018
20067
  const examples = [];
20019
- const claudeDir = join11(homedir11(), ".claude", "projects");
20068
+ const claudeDir = join11(options.homeDir ?? homedir11(), ".claude", "projects");
20020
20069
  if (!existsSync5(claudeDir)) {
20021
20070
  return { source: "sessions", examples: [], count: 0 };
20022
20071
  }
@@ -13392,6 +13392,22 @@ function getPackageVersion() {
13392
13392
  import { Database as Database3 } from "bun:sqlite";
13393
13393
  import { homedir as homedir6 } from "os";
13394
13394
  import { join as join4 } from "path";
13395
+
13396
+ // src/lib/gatherers/tags.ts
13397
+ function parseTagList(value) {
13398
+ if (!value?.trim())
13399
+ return [];
13400
+ try {
13401
+ const parsed = JSON.parse(value);
13402
+ if (!Array.isArray(parsed))
13403
+ return [];
13404
+ return parsed.filter((tag) => typeof tag === "string");
13405
+ } catch {
13406
+ return [];
13407
+ }
13408
+ }
13409
+
13410
+ // src/lib/gatherers/todos.ts
13395
13411
  var SYSTEM_PROMPT = "You are a task management assistant that helps users create, update, search, and manage tasks and projects.";
13396
13412
  function taskToCreateExample(task) {
13397
13413
  const userMsg = `Create a task: ${task.title}${task.description ? `
@@ -13403,7 +13419,7 @@ Description: ${task.description}` : ""}`;
13403
13419
  description: task.description ?? "",
13404
13420
  status: task.status,
13405
13421
  priority: task.priority,
13406
- tags: JSON.parse(task.tags ?? "[]"),
13422
+ tags: parseTagList(task.tags),
13407
13423
  created_at: task.created_at
13408
13424
  };
13409
13425
  return {
@@ -13442,7 +13458,7 @@ ${matched.map((t) => `- [${t.short_id ?? t.id}] ${t.title} (${t.status})`).join(
13442
13458
  };
13443
13459
  }
13444
13460
  async function gatherFromTodos(options = {}) {
13445
- const dbPath = join4(homedir6(), ".todos", "todos.db");
13461
+ const dbPath = join4(options.homeDir ?? homedir6(), ".todos", "todos.db");
13446
13462
  const db = new Database3(dbPath, { readonly: true, create: false });
13447
13463
  try {
13448
13464
  let query = "SELECT * FROM tasks WHERE 1=1";
@@ -13499,7 +13515,7 @@ Summary: ${memory.summary}` : memory.value
13499
13515
  };
13500
13516
  }
13501
13517
  function memoryToSaveExample(memory) {
13502
- const tags = JSON.parse(memory.tags ?? "[]");
13518
+ const tags = parseTagList(memory.tags);
13503
13519
  return {
13504
13520
  messages: [
13505
13521
  { role: "system", content: SYSTEM_PROMPT2 },
@@ -13530,7 +13546,7 @@ ${matched.map((m) => `- ${m.key}: ${m.value.slice(0, 120)}${m.value.length > 120
13530
13546
  };
13531
13547
  }
13532
13548
  async function gatherFromMementos(options = {}) {
13533
- const dbPath = join7(homedir8(), ".mementos", "mementos.db");
13549
+ const dbPath = join7(options.homeDir ?? homedir8(), ".mementos", "mementos.db");
13534
13550
  const db = new Database4(dbPath, { readonly: true, create: false });
13535
13551
  try {
13536
13552
  let query = "SELECT * FROM memories WHERE status = 'active'";
@@ -13596,7 +13612,7 @@ function windowToExample(window) {
13596
13612
  return { messages };
13597
13613
  }
13598
13614
  async function gatherFromConversations(options = {}) {
13599
- const dbPath = join9(homedir9(), ".conversations", "messages.db");
13615
+ const dbPath = join9(options.homeDir ?? homedir9(), ".conversations", "messages.db");
13600
13616
  const db = new Database5(dbPath, { readonly: true, create: false });
13601
13617
  try {
13602
13618
  let query = "SELECT * FROM messages WHERE 1=1";
@@ -13652,7 +13668,7 @@ function extractText(content) {
13652
13668
  async function gatherFromSessions(options = {}) {
13653
13669
  const { limit = 1000 } = options;
13654
13670
  const examples = [];
13655
- const claudeDir = join10(homedir10(), ".claude", "projects");
13671
+ const claudeDir = join10(options.homeDir ?? homedir10(), ".claude", "projects");
13656
13672
  if (!existsSync5(claudeDir)) {
13657
13673
  return { source: "sessions", examples: [], count: 0 };
13658
13674
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/brains",
3
- "version": "0.0.29",
3
+ "version": "0.0.31",
4
4
  "description": "Fine-tuned model tracker and trainer — wraps OpenAI + Thinker Labs, gathers training data from todos/mementos/conversations/sessions",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",