claude-memory-layer 1.0.15 → 1.0.17

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/README.md CHANGED
@@ -71,6 +71,9 @@ npx claude-memory-layer search "배포 이슈"
71
71
  - `GET /health` (서버 헬스)
72
72
  - `GET /api/health` (outbox pending/failed 포함 상세 헬스)
73
73
  - `GET /api/stats/retrieval-traces` (검색→컨텍스트 채택 추적)
74
+ - 주입 임계값 튜닝(환경변수):
75
+ - `CLAUDE_MEMORY_MIN_SCORE` (기본 0.4)
76
+ - `CLAUDE_MEMORY_FALLBACK_MIN_SCORE` (기본 0.3, 결과 0건일 때 재시도)
74
77
 
75
78
  ---
76
79
 
package/dist/cli/index.js CHANGED
@@ -2046,49 +2046,62 @@ var SQLiteEventStore = class {
2046
2046
  }
2047
2047
  async getRecentRetrievalTraces(limit = 50) {
2048
2048
  await this.initialize();
2049
- const rows = sqliteAll(
2050
- this.db,
2051
- `SELECT * FROM retrieval_traces ORDER BY created_at DESC LIMIT ?`,
2052
- [limit]
2053
- );
2054
- return rows.map((row) => ({
2055
- traceId: row.trace_id,
2056
- sessionId: row.session_id || void 0,
2057
- projectHash: row.project_hash || void 0,
2058
- queryText: row.query_text,
2059
- strategy: row.strategy || void 0,
2060
- candidateEventIds: row.candidate_event_ids ? JSON.parse(row.candidate_event_ids) : [],
2061
- selectedEventIds: row.selected_event_ids ? JSON.parse(row.selected_event_ids) : [],
2062
- candidateDetails: row.candidate_details_json ? JSON.parse(row.candidate_details_json) : [],
2063
- selectedDetails: row.selected_details_json ? JSON.parse(row.selected_details_json) : [],
2064
- candidateCount: Number(row.candidate_count || 0),
2065
- selectedCount: Number(row.selected_count || 0),
2066
- confidence: row.confidence || void 0,
2067
- fallbackTrace: row.fallback_trace ? JSON.parse(row.fallback_trace) : [],
2068
- createdAt: toDateFromSQLite(row.created_at)
2069
- }));
2049
+ try {
2050
+ const rows = sqliteAll(
2051
+ this.db,
2052
+ `SELECT * FROM retrieval_traces ORDER BY created_at DESC LIMIT ?`,
2053
+ [limit]
2054
+ );
2055
+ return rows.map((row) => ({
2056
+ traceId: row.trace_id,
2057
+ sessionId: row.session_id || void 0,
2058
+ projectHash: row.project_hash || void 0,
2059
+ queryText: row.query_text,
2060
+ strategy: row.strategy || void 0,
2061
+ candidateEventIds: row.candidate_event_ids ? JSON.parse(row.candidate_event_ids) : [],
2062
+ selectedEventIds: row.selected_event_ids ? JSON.parse(row.selected_event_ids) : [],
2063
+ candidateDetails: row.candidate_details_json ? JSON.parse(row.candidate_details_json) : [],
2064
+ selectedDetails: row.selected_details_json ? JSON.parse(row.selected_details_json) : [],
2065
+ candidateCount: Number(row.candidate_count || 0),
2066
+ selectedCount: Number(row.selected_count || 0),
2067
+ confidence: row.confidence || void 0,
2068
+ fallbackTrace: row.fallback_trace ? JSON.parse(row.fallback_trace) : [],
2069
+ createdAt: toDateFromSQLite(row.created_at)
2070
+ }));
2071
+ } catch (err) {
2072
+ if (err?.message?.includes("no such table"))
2073
+ return [];
2074
+ throw err;
2075
+ }
2070
2076
  }
2071
2077
  async getRetrievalTraceStats() {
2072
2078
  await this.initialize();
2073
- const row = sqliteGet(
2074
- this.db,
2075
- `SELECT
2076
- COUNT(*) as total_queries,
2077
- AVG(candidate_count) as avg_candidate_count,
2078
- AVG(selected_count) as avg_selected_count,
2079
- CASE
2080
- WHEN SUM(candidate_count) > 0 THEN (SUM(selected_count) * 1.0 / SUM(candidate_count))
2081
- ELSE 0
2082
- END as selection_rate
2083
- FROM retrieval_traces`,
2084
- []
2085
- );
2086
- return {
2087
- totalQueries: Number(row?.total_queries || 0),
2088
- avgCandidateCount: Number(row?.avg_candidate_count || 0),
2089
- avgSelectedCount: Number(row?.avg_selected_count || 0),
2090
- selectionRate: Number(row?.selection_rate || 0)
2091
- };
2079
+ try {
2080
+ const row = sqliteGet(
2081
+ this.db,
2082
+ `SELECT
2083
+ COUNT(*) as total_queries,
2084
+ AVG(candidate_count) as avg_candidate_count,
2085
+ AVG(selected_count) as avg_selected_count,
2086
+ CASE
2087
+ WHEN SUM(candidate_count) > 0 THEN (SUM(selected_count) * 1.0 / SUM(candidate_count))
2088
+ ELSE 0
2089
+ END as selection_rate
2090
+ FROM retrieval_traces`,
2091
+ []
2092
+ );
2093
+ return {
2094
+ totalQueries: Number(row?.total_queries || 0),
2095
+ avgCandidateCount: Number(row?.avg_candidate_count || 0),
2096
+ avgSelectedCount: Number(row?.avg_selected_count || 0),
2097
+ selectionRate: Number(row?.selection_rate || 0)
2098
+ };
2099
+ } catch (err) {
2100
+ if (err?.message?.includes("no such table")) {
2101
+ return { totalQueries: 0, avgCandidateCount: 0, avgSelectedCount: 0, selectionRate: 0 };
2102
+ }
2103
+ throw err;
2104
+ }
2092
2105
  }
2093
2106
  /**
2094
2107
  * Close database connection
@@ -9214,41 +9227,51 @@ function saveClaudeSettings(settings) {
9214
9227
  fs9.writeFileSync(tempPath, JSON.stringify(settings, null, 2));
9215
9228
  fs9.renameSync(tempPath, CLAUDE_SETTINGS_PATH);
9216
9229
  }
9230
+ var REQUIRED_HOOK_FILES = [
9231
+ "user-prompt-submit.js",
9232
+ "post-tool-use.js",
9233
+ "session-start.js",
9234
+ "stop.js",
9235
+ "session-end.js"
9236
+ ];
9237
+ function hasHook(settings, hookName, commandFragment) {
9238
+ const hookEntries = settings.hooks?.[hookName];
9239
+ if (!hookEntries)
9240
+ return false;
9241
+ return hookEntries.some((entry) => entry.hooks?.some((hook) => hook.command?.includes(commandFragment)));
9242
+ }
9217
9243
  function getHooksConfig(pluginPath) {
9244
+ const makeHook = (fileName) => [
9245
+ {
9246
+ matcher: "",
9247
+ hooks: [
9248
+ {
9249
+ type: "command",
9250
+ command: `node ${path9.join(pluginPath, "hooks", fileName)}`
9251
+ }
9252
+ ]
9253
+ }
9254
+ ];
9218
9255
  return {
9219
- UserPromptSubmit: [
9220
- {
9221
- matcher: "",
9222
- hooks: [
9223
- {
9224
- type: "command",
9225
- command: `node ${path9.join(pluginPath, "hooks", "user-prompt-submit.js")}`
9226
- }
9227
- ]
9228
- }
9229
- ],
9230
- PostToolUse: [
9231
- {
9232
- matcher: "",
9233
- hooks: [
9234
- {
9235
- type: "command",
9236
- command: `node ${path9.join(pluginPath, "hooks", "post-tool-use.js")}`
9237
- }
9238
- ]
9239
- }
9240
- ]
9256
+ SessionStart: makeHook("session-start.js"),
9257
+ UserPromptSubmit: makeHook("user-prompt-submit.js"),
9258
+ PostToolUse: makeHook("post-tool-use.js"),
9259
+ Stop: makeHook("stop.js"),
9260
+ SessionEnd: makeHook("session-end.js")
9241
9261
  };
9242
9262
  }
9243
9263
  var program = new Command();
9244
- program.name("claude-memory-layer").description("Claude Code Memory Plugin CLI").version("1.0.15");
9264
+ program.name("claude-memory-layer").description("Claude Code Memory Plugin CLI").version("1.0.17");
9245
9265
  program.command("install").description("Install hooks into Claude Code settings").option("--path <path>", "Custom plugin path (defaults to auto-detect)").action(async (options) => {
9246
9266
  try {
9247
9267
  const pluginPath = options.path || getPluginPath();
9248
- const userPromptHook = path9.join(pluginPath, "hooks", "user-prompt-submit.js");
9249
- if (!fs9.existsSync(userPromptHook)) {
9268
+ const missingHooks = REQUIRED_HOOK_FILES.filter(
9269
+ (file) => !fs9.existsSync(path9.join(pluginPath, "hooks", file))
9270
+ );
9271
+ if (missingHooks.length > 0) {
9250
9272
  console.error(`
9251
9273
  \u274C Hook files not found at: ${pluginPath}`);
9274
+ console.error(` Missing: ${missingHooks.join(", ")}`);
9252
9275
  console.error(' Make sure you have built the plugin with "npm run build"');
9253
9276
  process.exit(1);
9254
9277
  }
@@ -9261,8 +9284,11 @@ program.command("install").description("Install hooks into Claude Code settings"
9261
9284
  saveClaudeSettings(settings);
9262
9285
  console.log("\n\u2705 Claude Memory Layer installed!\n");
9263
9286
  console.log("Hooks registered:");
9287
+ console.log(" - SessionStart: Register session -> project mapping");
9264
9288
  console.log(" - UserPromptSubmit: Memory retrieval on user input");
9265
- console.log(" - PostToolUse: Store tool observations\n");
9289
+ console.log(" - PostToolUse: Store tool observations");
9290
+ console.log(" - Stop: Store assistant responses");
9291
+ console.log(" - SessionEnd: Persist session summary\n");
9266
9292
  console.log("Plugin path:", pluginPath);
9267
9293
  console.log("\n\u26A0\uFE0F Restart Claude Code for changes to take effect.\n");
9268
9294
  console.log("Commands:");
@@ -9282,8 +9308,11 @@ program.command("uninstall").description("Remove hooks from Claude Code settings
9282
9308
  console.log("\n\u{1F4CB} No hooks installed.\n");
9283
9309
  return;
9284
9310
  }
9311
+ delete settings.hooks.SessionStart;
9285
9312
  delete settings.hooks.UserPromptSubmit;
9286
9313
  delete settings.hooks.PostToolUse;
9314
+ delete settings.hooks.Stop;
9315
+ delete settings.hooks.SessionEnd;
9287
9316
  if (Object.keys(settings.hooks).length === 0) {
9288
9317
  delete settings.hooks;
9289
9318
  }
@@ -9303,23 +9332,25 @@ program.command("status").description("Check plugin installation status").action
9303
9332
  const settings = loadClaudeSettings();
9304
9333
  const pluginPath = getPluginPath();
9305
9334
  console.log("\n\u{1F9E0} Claude Memory Layer Status\n");
9306
- const hasUserPromptHook = settings.hooks?.UserPromptSubmit?.some(
9307
- (h) => h.hooks?.some((hook) => hook.command?.includes("user-prompt-submit"))
9308
- );
9309
- const hasPostToolHook = settings.hooks?.PostToolUse?.some(
9310
- (h) => h.hooks?.some((hook) => hook.command?.includes("post-tool-use"))
9311
- );
9335
+ const hasSessionStartHook = hasHook(settings, "SessionStart", "session-start");
9336
+ const hasUserPromptHook = hasHook(settings, "UserPromptSubmit", "user-prompt-submit");
9337
+ const hasPostToolHook = hasHook(settings, "PostToolUse", "post-tool-use");
9338
+ const hasStopHook = hasHook(settings, "Stop", "stop");
9339
+ const hasSessionEndHook = hasHook(settings, "SessionEnd", "session-end");
9312
9340
  console.log("Hooks:");
9341
+ console.log(` SessionStart: ${hasSessionStartHook ? "\u2705 Installed" : "\u274C Not installed"}`);
9313
9342
  console.log(` UserPromptSubmit: ${hasUserPromptHook ? "\u2705 Installed" : "\u274C Not installed"}`);
9314
9343
  console.log(` PostToolUse: ${hasPostToolHook ? "\u2705 Installed" : "\u274C Not installed"}`);
9315
- const hooksExist = fs9.existsSync(path9.join(pluginPath, "hooks", "user-prompt-submit.js"));
9344
+ console.log(` Stop: ${hasStopHook ? "\u2705 Installed" : "\u274C Not installed"}`);
9345
+ console.log(` SessionEnd: ${hasSessionEndHook ? "\u2705 Installed" : "\u274C Not installed"}`);
9346
+ const hooksExist = REQUIRED_HOOK_FILES.every((file) => fs9.existsSync(path9.join(pluginPath, "hooks", file)));
9316
9347
  console.log(`
9317
9348
  Plugin files: ${hooksExist ? "\u2705 Found" : "\u274C Not found"}`);
9318
9349
  console.log(` Path: ${pluginPath}`);
9319
9350
  const dashboardRunning = await isServerRunning(37777);
9320
9351
  console.log(`
9321
9352
  Dashboard: ${dashboardRunning ? "\u2705 Running at http://localhost:37777" : "\u23F9\uFE0F Not running"}`);
9322
- if (!hasUserPromptHook || !hasPostToolHook) {
9353
+ if (!hasSessionStartHook || !hasUserPromptHook || !hasPostToolHook || !hasStopHook || !hasSessionEndHook) {
9323
9354
  console.log('\n\u{1F4A1} Run "claude-memory-layer install" to set up hooks.\n');
9324
9355
  } else {
9325
9356
  console.log("\n\u2705 Plugin is fully installed and configured.\n");