@rlabs-inc/memory 0.1.0 → 0.2.1

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.
@@ -10773,9 +10773,15 @@ class Database {
10773
10773
  if (!id)
10774
10774
  continue;
10775
10775
  const record = this._columns.getRecord(index);
10776
- const withId = { id, ...record };
10777
- if (filter(withId)) {
10778
- results.push(withId);
10776
+ const withMeta = {
10777
+ id,
10778
+ ...record,
10779
+ created: this._created[index] || 0,
10780
+ updated: this._updated[index] || 0,
10781
+ stale: this._staleFlags[index] || false
10782
+ };
10783
+ if (filter(withMeta)) {
10784
+ results.push(withMeta);
10779
10785
  }
10780
10786
  }
10781
10787
  return results;
@@ -10786,9 +10792,15 @@ class Database {
10786
10792
  if (!id)
10787
10793
  continue;
10788
10794
  const record = this._columns.getRecord(index);
10789
- const withId = { id, ...record };
10790
- if (filter(withId)) {
10791
- return withId;
10795
+ const withMeta = {
10796
+ id,
10797
+ ...record,
10798
+ created: this._created[index] || 0,
10799
+ updated: this._updated[index] || 0,
10800
+ stale: this._staleFlags[index] || false
10801
+ };
10802
+ if (filter(withMeta)) {
10803
+ return withMeta;
10792
10804
  }
10793
10805
  }
10794
10806
  return null;
@@ -11142,9 +11154,12 @@ class MemoryStore {
11142
11154
  }
11143
11155
  async getProject(projectId) {
11144
11156
  if (this._projects.has(projectId)) {
11157
+ console.log(`\uD83D\uDD04 [DEBUG] Returning cached databases for ${projectId}`);
11145
11158
  return this._projects.get(projectId);
11146
11159
  }
11160
+ console.log(`\uD83C\uDD95 [DEBUG] Creating NEW databases for ${projectId}`);
11147
11161
  const projectPath = import_path2.join(this._config.basePath, projectId);
11162
+ console.log(` Path: ${projectPath}`);
11148
11163
  const [memories, summaries, snapshots, sessions] = await Promise.all([
11149
11164
  createDatabase({
11150
11165
  path: import_path2.join(projectPath, "memories"),
@@ -11329,20 +11344,31 @@ class MemoryStore {
11329
11344
  }
11330
11345
  async storeSessionSummary(projectId, sessionId, summary, interactionTone = "") {
11331
11346
  const { summaries } = await this.getProject(projectId);
11332
- return summaries.insert({
11347
+ console.log(`\uD83D\uDCDD [DEBUG] Storing summary for ${projectId}:`);
11348
+ console.log(` Summary length: ${summary.length} chars`);
11349
+ console.log(` Summaries count before: ${summaries.all().length}`);
11350
+ const id = await summaries.insert({
11333
11351
  session_id: sessionId,
11334
11352
  project_id: projectId,
11335
11353
  summary,
11336
11354
  interaction_tone: interactionTone
11337
11355
  });
11356
+ console.log(` Summaries count after: ${summaries.all().length}`);
11357
+ console.log(` Inserted ID: ${id}`);
11358
+ return id;
11338
11359
  }
11339
11360
  async getLatestSummary(projectId) {
11340
11361
  const { summaries } = await this.getProject(projectId);
11362
+ console.log(`\uD83D\uDCD6 [DEBUG] Getting latest summary for ${projectId}:`);
11341
11363
  const all = summaries.all();
11342
- if (!all.length)
11364
+ console.log(` Summaries found: ${all.length}`);
11365
+ if (!all.length) {
11366
+ console.log(` No summaries found!`);
11343
11367
  return null;
11368
+ }
11344
11369
  all.sort((a, b) => b.created - a.created);
11345
11370
  const latest = all[0];
11371
+ console.log(` Latest summary: ${latest.summary.slice(0, 50)}...`);
11346
11372
  return {
11347
11373
  id: latest.id,
11348
11374
  session_id: latest.session_id,
@@ -11727,6 +11753,12 @@ class MemoryEngine {
11727
11753
  }
11728
11754
  async _getStore(projectId, projectPath) {
11729
11755
  const key = this._config.storageMode === "local" && projectPath ? projectPath : projectId;
11756
+ console.log(`\uD83C\uDFEA [DEBUG] _getStore called:`);
11757
+ console.log(` projectId: ${projectId}`);
11758
+ console.log(` projectPath: ${projectPath}`);
11759
+ console.log(` storageMode: ${this._config.storageMode}`);
11760
+ console.log(` cache key: ${key}`);
11761
+ console.log(` cached: ${this._stores.has(key)}`);
11730
11762
  if (this._stores.has(key)) {
11731
11763
  return this._stores.get(key);
11732
11764
  }
@@ -11900,14 +11932,31 @@ function createEngine(config) {
11900
11932
  }
11901
11933
 
11902
11934
  // src/core/curator.ts
11935
+ var import_os3 = require("os");
11936
+ var import_path5 = require("path");
11937
+ var import_fs3 = require("fs");
11938
+ function getClaudeCommand() {
11939
+ const envCommand = process.env.CURATOR_COMMAND;
11940
+ if (envCommand) {
11941
+ return envCommand;
11942
+ }
11943
+ const claudeLocal = import_path5.join(import_os3.homedir(), ".claude", "local", "claude");
11944
+ if (import_fs3.existsSync(claudeLocal)) {
11945
+ return claudeLocal;
11946
+ }
11947
+ return "claude";
11948
+ }
11949
+
11903
11950
  class Curator {
11904
11951
  _config;
11905
11952
  constructor(config = {}) {
11953
+ const cliCommand = config.cliCommand ?? getClaudeCommand();
11906
11954
  this._config = {
11907
11955
  apiKey: config.apiKey ?? "",
11908
- cliCommand: config.cliCommand ?? "claude",
11956
+ cliCommand,
11909
11957
  cliType: config.cliType ?? "claude-code"
11910
11958
  };
11959
+ console.log(`\uD83E\uDDE0 Curator initialized with CLI: ${cliCommand}`);
11911
11960
  }
11912
11961
  buildCurationPrompt(triggerType = "session_end") {
11913
11962
  return `You have just had a conversation. As this session is ending (${triggerType}), please curate memories for the Claude Tools Memory System.
@@ -12101,33 +12150,96 @@ ${prompt}`
12101
12150
  return this.parseCurationResponse(content.text);
12102
12151
  }
12103
12152
  async curateWithCLI(sessionId, triggerType = "session_end", cwd) {
12104
- const prompt = this.buildCurationPrompt(triggerType);
12153
+ const systemPrompt = this.buildCurationPrompt(triggerType);
12154
+ const userMessage = "This session has ended. Please curate the memories from our conversation according to the instructions in your system prompt. Return ONLY the JSON structure.";
12105
12155
  const args = [];
12106
12156
  if (this._config.cliType === "claude-code") {
12107
- args.push("--resume", sessionId, "-p", prompt, "--output-format", "json", "--max-turns", "1");
12157
+ args.push("--resume", sessionId, "-p", userMessage, "--append-system-prompt", systemPrompt, "--output-format", "json", "--max-turns", "1");
12108
12158
  } else {
12109
- args.push("--resume", sessionId, "-p", prompt, "--output-format", "json");
12159
+ args.push("--resume", sessionId, "-p", `${systemPrompt}
12160
+
12161
+ ${userMessage}`, "--output-format", "json");
12110
12162
  }
12163
+ console.log(`
12164
+ \uD83D\uDCCB Executing CLI command:`);
12165
+ console.log(` Command: ${this._config.cliCommand}`);
12166
+ console.log(` Args: --resume ${sessionId} -p [user_message] --append-system-prompt [curation_instructions] --output-format json --max-turns 1`);
12167
+ console.log(` CWD: ${cwd || "not set"}`);
12168
+ console.log(` User message: "${userMessage.slice(0, 50)}..."`);
12169
+ console.log(` System prompt length: ${systemPrompt.length} chars`);
12111
12170
  const proc = Bun.spawn([this._config.cliCommand, ...args], {
12112
12171
  cwd,
12113
12172
  env: {
12114
12173
  ...process.env,
12115
12174
  MEMORY_CURATOR_ACTIVE: "1"
12116
- }
12175
+ },
12176
+ stderr: "pipe"
12117
12177
  });
12118
- const output = await new Response(proc.stdout).text();
12178
+ const [stdout, stderr] = await Promise.all([
12179
+ new Response(proc.stdout).text(),
12180
+ new Response(proc.stderr).text()
12181
+ ]);
12119
12182
  const exitCode = await proc.exited;
12183
+ console.log(`
12184
+ \uD83D\uDCE4 CLI Response:`);
12185
+ console.log(` Exit code: ${exitCode}`);
12186
+ console.log(` Stdout length: ${stdout.length} chars`);
12187
+ console.log(` Stderr length: ${stderr.length} chars`);
12188
+ if (stderr) {
12189
+ console.log(`
12190
+ ⚠️ Stderr output:`);
12191
+ console.log(stderr.slice(0, 1000));
12192
+ }
12193
+ if (stdout) {
12194
+ console.log(`
12195
+ \uD83D\uDCE5 Stdout output (first 500 chars):`);
12196
+ console.log(stdout.slice(0, 500));
12197
+ }
12120
12198
  if (exitCode !== 0) {
12121
- console.error(`CLI exited with code ${exitCode}`);
12199
+ console.error(`
12200
+ ❌ CLI exited with code ${exitCode}`);
12122
12201
  return { session_summary: "", memories: [] };
12123
12202
  }
12124
12203
  try {
12125
- const jsonMatch = output.match(/\{[\s\S]*\}/)?.[0];
12204
+ const cliOutput = JSON.parse(stdout);
12205
+ if (cliOutput.type === "error" || cliOutput.is_error === true) {
12206
+ console.log(`
12207
+ ❌ CLI returned error:`);
12208
+ console.log(` Type: ${cliOutput.type}`);
12209
+ console.log(` Message: ${cliOutput.message || cliOutput.error || "Unknown error"}`);
12210
+ return { session_summary: "", memories: [] };
12211
+ }
12212
+ let aiResponse = "";
12213
+ if (typeof cliOutput.result === "string") {
12214
+ aiResponse = cliOutput.result;
12215
+ console.log(`
12216
+ \uD83D\uDCE6 Extracted result from CLI wrapper (${aiResponse.length} chars)`);
12217
+ } else {
12218
+ console.log(`
12219
+ ⚠️ No result field in CLI output`);
12220
+ console.log(` Keys: ${Object.keys(cliOutput).join(", ")}`);
12221
+ return { session_summary: "", memories: [] };
12222
+ }
12223
+ const codeBlockMatch = aiResponse.match(/```(?:json)?\s*([\s\S]*?)```/);
12224
+ if (codeBlockMatch) {
12225
+ aiResponse = codeBlockMatch[1].trim();
12226
+ console.log(`\uD83D\uDCDD Extracted JSON from markdown code block`);
12227
+ }
12228
+ const jsonMatch = aiResponse.match(/\{[\s\S]*\}/)?.[0];
12126
12229
  if (jsonMatch) {
12127
- return this.parseCurationResponse(jsonMatch);
12230
+ console.log(`✅ Found JSON object (${jsonMatch.length} chars)`);
12231
+ const result = this.parseCurationResponse(jsonMatch);
12232
+ console.log(` Parsed ${result.memories.length} memories`);
12233
+ return result;
12234
+ } else {
12235
+ console.log(`
12236
+ ⚠️ No JSON object found in AI response`);
12237
+ console.log(` Response preview: ${aiResponse.slice(0, 200)}...`);
12128
12238
  }
12129
12239
  } catch (error) {
12130
- console.error("Failed to parse CLI output:", error);
12240
+ console.error(`
12241
+ ❌ Failed to parse CLI output:`, error);
12242
+ console.log(` Raw stdout (first 500 chars): ${stdout.slice(0, 500)}`);
12131
12243
  }
12132
12244
  return { session_summary: "", memories: [] };
12133
12245
  }
@@ -12283,6 +12395,17 @@ function createServer(config = {}) {
12283
12395
  context_type: m2.context_type
12284
12396
  })), body.current_message ?? "");
12285
12397
  }
12398
+ console.log(`
12399
+ \uD83D\uDCE4 [DEBUG] Response to hook:`);
12400
+ console.log(` memories_count: ${result.memories.length}`);
12401
+ console.log(` has_primer: ${!!result.primer}`);
12402
+ console.log(` formatted length: ${result.formatted.length} chars`);
12403
+ if (result.formatted) {
12404
+ console.log(` formatted preview:
12405
+ ${result.formatted.slice(0, 300)}...`);
12406
+ } else {
12407
+ console.log(` ⚠️ formatted is EMPTY!`);
12408
+ }
12286
12409
  return Response.json({
12287
12410
  success: true,
12288
12411
  context: result.formatted,
@@ -10753,9 +10753,15 @@ class Database {
10753
10753
  if (!id)
10754
10754
  continue;
10755
10755
  const record = this._columns.getRecord(index);
10756
- const withId = { id, ...record };
10757
- if (filter(withId)) {
10758
- results.push(withId);
10756
+ const withMeta = {
10757
+ id,
10758
+ ...record,
10759
+ created: this._created[index] || 0,
10760
+ updated: this._updated[index] || 0,
10761
+ stale: this._staleFlags[index] || false
10762
+ };
10763
+ if (filter(withMeta)) {
10764
+ results.push(withMeta);
10759
10765
  }
10760
10766
  }
10761
10767
  return results;
@@ -10766,9 +10772,15 @@ class Database {
10766
10772
  if (!id)
10767
10773
  continue;
10768
10774
  const record = this._columns.getRecord(index);
10769
- const withId = { id, ...record };
10770
- if (filter(withId)) {
10771
- return withId;
10775
+ const withMeta = {
10776
+ id,
10777
+ ...record,
10778
+ created: this._created[index] || 0,
10779
+ updated: this._updated[index] || 0,
10780
+ stale: this._staleFlags[index] || false
10781
+ };
10782
+ if (filter(withMeta)) {
10783
+ return withMeta;
10772
10784
  }
10773
10785
  }
10774
10786
  return null;
@@ -11122,9 +11134,12 @@ class MemoryStore {
11122
11134
  }
11123
11135
  async getProject(projectId) {
11124
11136
  if (this._projects.has(projectId)) {
11137
+ console.log(`\uD83D\uDD04 [DEBUG] Returning cached databases for ${projectId}`);
11125
11138
  return this._projects.get(projectId);
11126
11139
  }
11140
+ console.log(`\uD83C\uDD95 [DEBUG] Creating NEW databases for ${projectId}`);
11127
11141
  const projectPath = join2(this._config.basePath, projectId);
11142
+ console.log(` Path: ${projectPath}`);
11128
11143
  const [memories, summaries, snapshots, sessions] = await Promise.all([
11129
11144
  createDatabase({
11130
11145
  path: join2(projectPath, "memories"),
@@ -11309,20 +11324,31 @@ class MemoryStore {
11309
11324
  }
11310
11325
  async storeSessionSummary(projectId, sessionId, summary, interactionTone = "") {
11311
11326
  const { summaries } = await this.getProject(projectId);
11312
- return summaries.insert({
11327
+ console.log(`\uD83D\uDCDD [DEBUG] Storing summary for ${projectId}:`);
11328
+ console.log(` Summary length: ${summary.length} chars`);
11329
+ console.log(` Summaries count before: ${summaries.all().length}`);
11330
+ const id = await summaries.insert({
11313
11331
  session_id: sessionId,
11314
11332
  project_id: projectId,
11315
11333
  summary,
11316
11334
  interaction_tone: interactionTone
11317
11335
  });
11336
+ console.log(` Summaries count after: ${summaries.all().length}`);
11337
+ console.log(` Inserted ID: ${id}`);
11338
+ return id;
11318
11339
  }
11319
11340
  async getLatestSummary(projectId) {
11320
11341
  const { summaries } = await this.getProject(projectId);
11342
+ console.log(`\uD83D\uDCD6 [DEBUG] Getting latest summary for ${projectId}:`);
11321
11343
  const all = summaries.all();
11322
- if (!all.length)
11344
+ console.log(` Summaries found: ${all.length}`);
11345
+ if (!all.length) {
11346
+ console.log(` No summaries found!`);
11323
11347
  return null;
11348
+ }
11324
11349
  all.sort((a, b) => b.created - a.created);
11325
11350
  const latest = all[0];
11351
+ console.log(` Latest summary: ${latest.summary.slice(0, 50)}...`);
11326
11352
  return {
11327
11353
  id: latest.id,
11328
11354
  session_id: latest.session_id,
@@ -11707,6 +11733,12 @@ class MemoryEngine {
11707
11733
  }
11708
11734
  async _getStore(projectId, projectPath) {
11709
11735
  const key = this._config.storageMode === "local" && projectPath ? projectPath : projectId;
11736
+ console.log(`\uD83C\uDFEA [DEBUG] _getStore called:`);
11737
+ console.log(` projectId: ${projectId}`);
11738
+ console.log(` projectPath: ${projectPath}`);
11739
+ console.log(` storageMode: ${this._config.storageMode}`);
11740
+ console.log(` cache key: ${key}`);
11741
+ console.log(` cached: ${this._stores.has(key)}`);
11710
11742
  if (this._stores.has(key)) {
11711
11743
  return this._stores.get(key);
11712
11744
  }
@@ -11880,14 +11912,31 @@ function createEngine(config) {
11880
11912
  }
11881
11913
 
11882
11914
  // src/core/curator.ts
11915
+ import { homedir as homedir3 } from "os";
11916
+ import { join as join4 } from "path";
11917
+ import { existsSync } from "fs";
11918
+ function getClaudeCommand() {
11919
+ const envCommand = process.env.CURATOR_COMMAND;
11920
+ if (envCommand) {
11921
+ return envCommand;
11922
+ }
11923
+ const claudeLocal = join4(homedir3(), ".claude", "local", "claude");
11924
+ if (existsSync(claudeLocal)) {
11925
+ return claudeLocal;
11926
+ }
11927
+ return "claude";
11928
+ }
11929
+
11883
11930
  class Curator {
11884
11931
  _config;
11885
11932
  constructor(config = {}) {
11933
+ const cliCommand = config.cliCommand ?? getClaudeCommand();
11886
11934
  this._config = {
11887
11935
  apiKey: config.apiKey ?? "",
11888
- cliCommand: config.cliCommand ?? "claude",
11936
+ cliCommand,
11889
11937
  cliType: config.cliType ?? "claude-code"
11890
11938
  };
11939
+ console.log(`\uD83E\uDDE0 Curator initialized with CLI: ${cliCommand}`);
11891
11940
  }
11892
11941
  buildCurationPrompt(triggerType = "session_end") {
11893
11942
  return `You have just had a conversation. As this session is ending (${triggerType}), please curate memories for the Claude Tools Memory System.
@@ -12081,33 +12130,96 @@ ${prompt}`
12081
12130
  return this.parseCurationResponse(content.text);
12082
12131
  }
12083
12132
  async curateWithCLI(sessionId, triggerType = "session_end", cwd) {
12084
- const prompt = this.buildCurationPrompt(triggerType);
12133
+ const systemPrompt = this.buildCurationPrompt(triggerType);
12134
+ const userMessage = "This session has ended. Please curate the memories from our conversation according to the instructions in your system prompt. Return ONLY the JSON structure.";
12085
12135
  const args = [];
12086
12136
  if (this._config.cliType === "claude-code") {
12087
- args.push("--resume", sessionId, "-p", prompt, "--output-format", "json", "--max-turns", "1");
12137
+ args.push("--resume", sessionId, "-p", userMessage, "--append-system-prompt", systemPrompt, "--output-format", "json", "--max-turns", "1");
12088
12138
  } else {
12089
- args.push("--resume", sessionId, "-p", prompt, "--output-format", "json");
12139
+ args.push("--resume", sessionId, "-p", `${systemPrompt}
12140
+
12141
+ ${userMessage}`, "--output-format", "json");
12090
12142
  }
12143
+ console.log(`
12144
+ \uD83D\uDCCB Executing CLI command:`);
12145
+ console.log(` Command: ${this._config.cliCommand}`);
12146
+ console.log(` Args: --resume ${sessionId} -p [user_message] --append-system-prompt [curation_instructions] --output-format json --max-turns 1`);
12147
+ console.log(` CWD: ${cwd || "not set"}`);
12148
+ console.log(` User message: "${userMessage.slice(0, 50)}..."`);
12149
+ console.log(` System prompt length: ${systemPrompt.length} chars`);
12091
12150
  const proc = Bun.spawn([this._config.cliCommand, ...args], {
12092
12151
  cwd,
12093
12152
  env: {
12094
12153
  ...process.env,
12095
12154
  MEMORY_CURATOR_ACTIVE: "1"
12096
- }
12155
+ },
12156
+ stderr: "pipe"
12097
12157
  });
12098
- const output = await new Response(proc.stdout).text();
12158
+ const [stdout, stderr] = await Promise.all([
12159
+ new Response(proc.stdout).text(),
12160
+ new Response(proc.stderr).text()
12161
+ ]);
12099
12162
  const exitCode = await proc.exited;
12163
+ console.log(`
12164
+ \uD83D\uDCE4 CLI Response:`);
12165
+ console.log(` Exit code: ${exitCode}`);
12166
+ console.log(` Stdout length: ${stdout.length} chars`);
12167
+ console.log(` Stderr length: ${stderr.length} chars`);
12168
+ if (stderr) {
12169
+ console.log(`
12170
+ ⚠️ Stderr output:`);
12171
+ console.log(stderr.slice(0, 1000));
12172
+ }
12173
+ if (stdout) {
12174
+ console.log(`
12175
+ \uD83D\uDCE5 Stdout output (first 500 chars):`);
12176
+ console.log(stdout.slice(0, 500));
12177
+ }
12100
12178
  if (exitCode !== 0) {
12101
- console.error(`CLI exited with code ${exitCode}`);
12179
+ console.error(`
12180
+ ❌ CLI exited with code ${exitCode}`);
12102
12181
  return { session_summary: "", memories: [] };
12103
12182
  }
12104
12183
  try {
12105
- const jsonMatch = output.match(/\{[\s\S]*\}/)?.[0];
12184
+ const cliOutput = JSON.parse(stdout);
12185
+ if (cliOutput.type === "error" || cliOutput.is_error === true) {
12186
+ console.log(`
12187
+ ❌ CLI returned error:`);
12188
+ console.log(` Type: ${cliOutput.type}`);
12189
+ console.log(` Message: ${cliOutput.message || cliOutput.error || "Unknown error"}`);
12190
+ return { session_summary: "", memories: [] };
12191
+ }
12192
+ let aiResponse = "";
12193
+ if (typeof cliOutput.result === "string") {
12194
+ aiResponse = cliOutput.result;
12195
+ console.log(`
12196
+ \uD83D\uDCE6 Extracted result from CLI wrapper (${aiResponse.length} chars)`);
12197
+ } else {
12198
+ console.log(`
12199
+ ⚠️ No result field in CLI output`);
12200
+ console.log(` Keys: ${Object.keys(cliOutput).join(", ")}`);
12201
+ return { session_summary: "", memories: [] };
12202
+ }
12203
+ const codeBlockMatch = aiResponse.match(/```(?:json)?\s*([\s\S]*?)```/);
12204
+ if (codeBlockMatch) {
12205
+ aiResponse = codeBlockMatch[1].trim();
12206
+ console.log(`\uD83D\uDCDD Extracted JSON from markdown code block`);
12207
+ }
12208
+ const jsonMatch = aiResponse.match(/\{[\s\S]*\}/)?.[0];
12106
12209
  if (jsonMatch) {
12107
- return this.parseCurationResponse(jsonMatch);
12210
+ console.log(`✅ Found JSON object (${jsonMatch.length} chars)`);
12211
+ const result = this.parseCurationResponse(jsonMatch);
12212
+ console.log(` Parsed ${result.memories.length} memories`);
12213
+ return result;
12214
+ } else {
12215
+ console.log(`
12216
+ ⚠️ No JSON object found in AI response`);
12217
+ console.log(` Response preview: ${aiResponse.slice(0, 200)}...`);
12108
12218
  }
12109
12219
  } catch (error) {
12110
- console.error("Failed to parse CLI output:", error);
12220
+ console.error(`
12221
+ ❌ Failed to parse CLI output:`, error);
12222
+ console.log(` Raw stdout (first 500 chars): ${stdout.slice(0, 500)}`);
12111
12223
  }
12112
12224
  return { session_summary: "", memories: [] };
12113
12225
  }
@@ -12263,6 +12375,17 @@ function createServer(config = {}) {
12263
12375
  context_type: m2.context_type
12264
12376
  })), body.current_message ?? "");
12265
12377
  }
12378
+ console.log(`
12379
+ \uD83D\uDCE4 [DEBUG] Response to hook:`);
12380
+ console.log(` memories_count: ${result.memories.length}`);
12381
+ console.log(` has_primer: ${!!result.primer}`);
12382
+ console.log(` formatted length: ${result.formatted.length} chars`);
12383
+ if (result.formatted) {
12384
+ console.log(` formatted preview:
12385
+ ${result.formatted.slice(0, 300)}...`);
12386
+ } else {
12387
+ console.log(` ⚠️ formatted is EMPTY!`);
12388
+ }
12266
12389
  return Response.json({
12267
12390
  success: true,
12268
12391
  context: result.formatted,
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env bun
2
+ // ============================================================================
3
+ // CURATION HOOK - Trigger memory curation
4
+ // Hook: PreCompact (auto|manual)
5
+ //
6
+ // Triggers memory curation when context is about to be compacted.
7
+ // This ensures memories are captured before context is lost.
8
+ // ============================================================================
9
+
10
+ import { styleText } from 'util'
11
+
12
+ // Configuration
13
+ const MEMORY_API_URL = process.env.MEMORY_API_URL || 'http://localhost:8765'
14
+
15
+ // Styled output helpers (for stderr feedback)
16
+ const info = (text: string) => styleText('cyan', text)
17
+ const success = (text: string) => styleText('green', text)
18
+ const warn = (text: string) => styleText('yellow', text)
19
+
20
+ /**
21
+ * Get project ID from working directory
22
+ */
23
+ function getProjectId(cwd: string): string {
24
+ return cwd.split('/').pop() || 'default'
25
+ }
26
+
27
+ /**
28
+ * Main hook entry point
29
+ */
30
+ async function main() {
31
+ // Skip if called from memory curator subprocess
32
+ if (process.env.MEMORY_CURATOR_ACTIVE === '1') return
33
+
34
+ try {
35
+ // Read input from stdin
36
+ const inputText = await Bun.stdin.text()
37
+ const input = JSON.parse(inputText)
38
+
39
+ const sessionId = input.session_id || 'unknown'
40
+ const cwd = process.env.CLAUDE_PROJECT_DIR || input.cwd || process.cwd()
41
+ const trigger = input.trigger || 'pre_compact'
42
+ const hookEvent = input.hook_event_name || 'PreCompact'
43
+
44
+ const projectId = getProjectId(cwd)
45
+
46
+ console.error(info(`🧠 Curating memories (${hookEvent})...`))
47
+
48
+ // Fire and forget - trigger curation
49
+ // The server handles the actual curation asynchronously
50
+ const response = await fetch(`${MEMORY_API_URL}/memory/checkpoint`, {
51
+ method: 'POST',
52
+ headers: { 'Content-Type': 'application/json' },
53
+ body: JSON.stringify({
54
+ session_id: sessionId,
55
+ project_id: projectId,
56
+ claude_session_id: sessionId,
57
+ trigger: trigger === 'pre_compact' || trigger === 'manual' ? 'pre_compact' : 'session_end',
58
+ cwd,
59
+ }),
60
+ signal: AbortSignal.timeout(5000),
61
+ }).catch(() => null)
62
+
63
+ if (response?.ok) {
64
+ console.error(success('✨ Memory curation started'))
65
+ } else {
66
+ console.error(warn('⚠️ Memory server not available'))
67
+ }
68
+
69
+ } catch (error: any) {
70
+ console.error(warn(`⚠️ Hook error: ${error.message}`))
71
+ }
72
+ }
73
+
74
+ main()
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env bun
2
+ // ============================================================================
3
+ // SESSION START HOOK - Inject session primer
4
+ // Hook: SessionStart (startup|resume)
5
+ //
6
+ // Injects session primer when a new session begins.
7
+ // The primer provides temporal context - when we last spoke,
8
+ // what we were working on, project status.
9
+ // ============================================================================
10
+
11
+ // Configuration
12
+ const MEMORY_API_URL = process.env.MEMORY_API_URL || 'http://localhost:8765'
13
+ const TIMEOUT_MS = 5000
14
+
15
+ /**
16
+ * Get project ID from working directory
17
+ */
18
+ function getProjectId(cwd: string): string {
19
+ return cwd.split('/').pop() || 'default'
20
+ }
21
+
22
+ /**
23
+ * HTTP POST with timeout
24
+ */
25
+ async function httpPost(url: string, data: object): Promise<any> {
26
+ try {
27
+ const response = await fetch(url, {
28
+ method: 'POST',
29
+ headers: { 'Content-Type': 'application/json' },
30
+ body: JSON.stringify(data),
31
+ signal: AbortSignal.timeout(TIMEOUT_MS),
32
+ })
33
+ return response.ok ? response.json() : {}
34
+ } catch {
35
+ return {}
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Main hook entry point
41
+ */
42
+ async function main() {
43
+ // Skip if called from memory curator subprocess
44
+ if (process.env.MEMORY_CURATOR_ACTIVE === '1') return
45
+
46
+ try {
47
+ // Read input from stdin
48
+ const inputText = await Bun.stdin.text()
49
+ const input = JSON.parse(inputText)
50
+
51
+ const sessionId = input.session_id || 'unknown'
52
+ const cwd = process.env.CLAUDE_PROJECT_DIR || input.cwd || process.cwd()
53
+
54
+ const projectId = getProjectId(cwd)
55
+
56
+ // Get session primer from memory system
57
+ const result = await httpPost(`${MEMORY_API_URL}/memory/context`, {
58
+ session_id: sessionId,
59
+ project_id: projectId,
60
+ current_message: '', // Empty to get just primer
61
+ max_memories: 0, // No memories, just primer
62
+ })
63
+
64
+ // Register session so inject hook knows to get memories, not primer
65
+ await httpPost(`${MEMORY_API_URL}/memory/process`, {
66
+ session_id: sessionId,
67
+ project_id: projectId,
68
+ metadata: { event: 'session_start' },
69
+ })
70
+
71
+ // Output primer to stdout (will be injected into session)
72
+ const primer = result.context_text || ''
73
+ if (primer) {
74
+ console.log(primer)
75
+ }
76
+
77
+ } catch {
78
+ // Never crash - just output nothing
79
+ }
80
+ }
81
+
82
+ main()