@mingxy/cerebro-claude-code 0.3.6 → 0.3.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.
Files changed (39) hide show
  1. package/.claude-plugin/marketplace.json +0 -0
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.mcp.json +0 -0
  4. package/README.md +0 -0
  5. package/config.json +0 -0
  6. package/hooks/apply-dream.mjs +0 -0
  7. package/hooks/common.mjs +30 -2
  8. package/hooks/common.sh +0 -0
  9. package/hooks/dream.mjs +0 -0
  10. package/hooks/flush-detached.mjs +4 -1
  11. package/hooks/hooks.json +0 -0
  12. package/hooks/post-compact.mjs +0 -0
  13. package/hooks/pre-compact.mjs +0 -0
  14. package/hooks/recall-approve.mjs +0 -0
  15. package/hooks/session-end.mjs +12 -2
  16. package/hooks/session-start.mjs +24 -1
  17. package/hooks/stop.mjs +0 -0
  18. package/hooks/user-prompt-submit.mjs +0 -0
  19. package/package.json +1 -1
  20. package/scripts/memory-profile.sh +0 -0
  21. package/scripts/memory-save.sh +0 -0
  22. package/scripts/memory-search.sh +0 -0
  23. package/scripts/web-server.mjs +0 -0
  24. package/skills/apply-dream/SKILL.md +0 -0
  25. package/skills/dream/SKILL.md +0 -0
  26. package/skills/memory-profile/SKILL.md +0 -0
  27. package/skills/memory-save/SKILL.md +0 -0
  28. package/skills/memory-search/SKILL.md +0 -0
  29. package/tests/common.test.mjs +0 -0
  30. package/tests/hooks.test.mjs +0 -0
  31. package/tests/test_smoke.sh +0 -0
  32. package/web/assets/geist-cyrillic-wght-normal-CHSlOQsW.woff2 +0 -0
  33. package/web/assets/geist-latin-ext-wght-normal-DMtmJ5ZE.woff2 +0 -0
  34. package/web/assets/geist-latin-wght-normal-Dm3htQBi.woff2 +0 -0
  35. package/web/assets/index-DVguCuEA.css +0 -0
  36. package/web/assets/index-zOJZZn-i.js +0 -0
  37. package/web/favicon.svg +0 -0
  38. package/web/icons.svg +0 -0
  39. package/web/index.html +0 -0
File without changes
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cerebro",
3
3
  "description": "Persistent memory for Claude Code — memories survive across sessions, projects, and machines",
4
- "version": "0.3.6",
4
+ "version": "0.3.7",
5
5
  "author": {
6
6
  "name": "mingxy-cerebro",
7
7
  "email": "hi@cerebro.dev"
package/.mcp.json CHANGED
File without changes
package/README.md CHANGED
File without changes
package/config.json CHANGED
File without changes
File without changes
package/hooks/common.mjs CHANGED
@@ -268,6 +268,34 @@ export function readCompactResult() {
268
268
  }
269
269
  }
270
270
 
271
+ // ─── Pending clear flush (SessionEnd:clear → SessionStart:clear) ────────────
272
+ // /clear swaps session_id, so the old transcript can only be flushed by the
273
+ // NEW session's SessionStart(clear) hook (detached flush can't emit a toast).
274
+ const PENDING_CLEAR_FLUSH_FILE = join(HOME, ".config/cerebro/pending-clear-flush.json");
275
+
276
+ export function writePendingClearFlush(tp, sid) {
277
+ try {
278
+ mkdirSync(dirname(PENDING_CLEAR_FLUSH_FILE), { recursive: true });
279
+ writeFileSync(PENDING_CLEAR_FLUSH_FILE, JSON.stringify({ tp, sid, ts: Date.now() }));
280
+ } catch {}
281
+ }
282
+
283
+ export function readPendingClearFlush() {
284
+ try {
285
+ if (!existsSync(PENDING_CLEAR_FLUSH_FILE)) return null;
286
+ const data = JSON.parse(readFileSync(PENDING_CLEAR_FLUSH_FILE, "utf-8"));
287
+ // Stale after 60s — user cleared but never resumed in this window
288
+ if (Date.now() - (data.ts || 0) > 60_000) return null;
289
+ return data;
290
+ } catch {
291
+ return null;
292
+ }
293
+ }
294
+
295
+ export function clearPendingClearFlush() {
296
+ try { unlinkSync(PENDING_CLEAR_FLUSH_FILE); } catch {}
297
+ }
298
+
271
299
  // ─── Cursor (session ingest dedup) ───────────────────────────────────────────
272
300
  const TRACKER_DIR = join(HOME, ".config/cerebro/trackers");
273
301
 
@@ -418,7 +446,7 @@ function contentText(content) {
418
446
  return "";
419
447
  }
420
448
 
421
- export async function flushSessionIngest(transcriptPath, sessionId) {
449
+ export async function flushSessionIngest(transcriptPath, sessionId, timeoutSec = 25) {
422
450
  if (!transcriptPath || !existsSync(transcriptPath) || !sessionId || !config.apiKey) return { ok: false, count: 0 };
423
451
 
424
452
  const cursor = cursorGet(sessionId);
@@ -476,7 +504,7 @@ export async function flushSessionIngest(transcriptPath, sessionId) {
476
504
  if (pn) body.project_name = pn;
477
505
  if (pp) body.project_path = pp;
478
506
 
479
- const result = await omPost("/v1/memories/session-ingest", body, 25);
507
+ const result = await omPost("/v1/memories/session-ingest", body, timeoutSec);
480
508
  if (result.status >= 200 && result.status < 300) {
481
509
  cursorSet(sessionId, lastUid);
482
510
  logDebug(`flush_session_ingest: ok http=${result.status} cursor=${lastUid}`);
package/hooks/common.sh CHANGED
File without changes
package/hooks/dream.mjs CHANGED
File without changes
@@ -2,7 +2,7 @@
2
2
  // cerebro detached flush — spawned by session-end.mjs to survive process exit
3
3
  // Runs independently after Claude Code terminates. No stdin/stdout to Claude.
4
4
  import { existsSync } from "node:fs";
5
- import { config, flushSessionIngest, logDebug, logError } from "./common.mjs";
5
+ import { config, flushSessionIngest, clearPendingClearFlush, logDebug, logError } from "./common.mjs";
6
6
  import { judgeMaterial, readState, runDream } from "./dream.mjs";
7
7
 
8
8
  const tp = process.env.CEREBRO_TP || "";
@@ -25,6 +25,9 @@ const result = await flushSessionIngest(tp, sid).catch((err) => {
25
25
 
26
26
  if (result.ok) {
27
27
  logDebug(`detached flush: ok count=${result.count} sid=${sid}`);
28
+ // Retrying a SessionStart(clear) handoff — consume it so the next SessionStart
29
+ // doesn't replay a 0-delta toast from the stale pending file.
30
+ if (process.env.CEREBRO_CLEAR_PENDING) clearPendingClearFlush();
28
31
  } else {
29
32
  logError(`detached flush: failed sid=${sid} http=${result.status || "?"} (cursor NOT advanced, will retry next session)`);
30
33
  }
package/hooks/hooks.json CHANGED
File without changes
File without changes
File without changes
File without changes
@@ -5,15 +5,25 @@
5
5
  // Fix: spawn detached child process, parent exits immediately.
6
6
  import { spawn } from "node:child_process";
7
7
  import { join } from "node:path";
8
- import { config, PLUGIN_ROOT, refCountDec, parseStdinJSON, emit, logDebug } from "./common.mjs";
8
+ import { config, PLUGIN_ROOT, refCountDec, parseStdinJSON, emit, logDebug, writePendingClearFlush } from "./common.mjs";
9
9
 
10
10
  if (!config.apiKey) { emit({}); process.exit(0); }
11
11
 
12
12
  const input = parseStdinJSON();
13
13
  const tp = input.transcript_path || "";
14
14
  const sid = input.session_id || input.sessionId || "";
15
+ const reason = input.reason || "";
15
16
 
16
- logDebug(`session-end: sid=${sid} tp=${tp ? tp.slice(-40) : "EMPTY"}`);
17
+ logDebug(`session-end: sid=${sid} reason=${reason} tp=${tp ? tp.slice(-40) : "EMPTY"}`);
18
+
19
+ // /clear swaps session_id and fires SessionStart(clear) right after — hand the
20
+ // flush to it so the result lands in that hook's toast (detached flush is mute).
21
+ if (reason === "clear" && tp && sid) {
22
+ writePendingClearFlush(tp, sid);
23
+ refCountDec();
24
+ emit({});
25
+ process.exit(0);
26
+ }
17
27
 
18
28
  // Spawn detached flush script — survives parent exit
19
29
  if (tp && sid) {
@@ -4,7 +4,7 @@ import { spawn } from "node:child_process";
4
4
  import { join } from "node:path";
5
5
  import { readFileSync } from "node:fs";
6
6
  import {
7
- config, PLUGIN_ROOT, PLUGIN_VERSION, detectProjectPath, parseStdinJSON, emit, buildMemoryInjection, postRecallEvent, refCountInc, readCompactResult, injectionConfig,
7
+ config, PLUGIN_ROOT, PLUGIN_VERSION, detectProjectPath, parseStdinJSON, emit, buildMemoryInjection, postRecallEvent, refCountInc, readCompactResult, readPendingClearFlush, clearPendingClearFlush, flushSessionIngest, injectionConfig,
8
8
  } from "./common.mjs";
9
9
  import { judgeMaterial, readState, writeStateForReport, fetchOrphanResult } from "./dream.mjs";
10
10
 
@@ -131,6 +131,29 @@ if (startSource === "compact") {
131
131
  }
132
132
  }
133
133
 
134
+ // After /clear, the OLD session's tail flush runs here (SessionEnd:clear only
135
+ // wrote a handoff file) so its result shows in this toast like stop/compact.
136
+ if (startSource === "clear") {
137
+ const pending = readPendingClearFlush();
138
+ if (pending) {
139
+ const r = await flushSessionIngest(pending.tp, pending.sid, 8).catch(() => ({ ok: false, count: 0 }));
140
+ if (r.ok) {
141
+ clearPendingClearFlush();
142
+ statusMsg += ` · Clear ingest ✓ ${r.count} msgs`;
143
+ } else {
144
+ // Out of hook budget — detached retry with full 25s timeout, silent
145
+ try {
146
+ const child = spawn(process.execPath, [join(PLUGIN_ROOT, "hooks", "flush-detached.mjs")], {
147
+ detached: true, stdio: "ignore",
148
+ env: { ...process.env, CEREBRO_TP: pending.tp, CEREBRO_SID: pending.sid, CEREBRO_CLEAR_PENDING: "1" },
149
+ });
150
+ child.unref();
151
+ } catch {}
152
+ statusMsg += ` · Clear ingest ↻ background`;
153
+ }
154
+ }
155
+ }
156
+
134
157
  // ─── POST recall event(让 web sessions 页面看到 CC session + 完整注入内容)─────
135
158
  await postRecallEvent({
136
159
  sessionId: sid,
package/hooks/stop.mjs CHANGED
File without changes
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mingxy/cerebro-claude-code",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "Persistent memory for Claude Code — memories survive across sessions, projects, and machines",
5
5
  "author": {
6
6
  "name": "mingxy-cerebro",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/web/favicon.svg CHANGED
File without changes
package/web/icons.svg CHANGED
File without changes
package/web/index.html CHANGED
File without changes