peon-mem 1.0.4 → 1.0.5
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 +5 -4
- package/bin/peon-mem.mjs +19 -4
- package/dist/monitor.js +47 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,8 +20,9 @@ prompt. It runs as a daemon on your machine, and nothing leaves it.
|
|
|
20
20
|
tools. Each project gets an isolated child brain. Every injection carries both.
|
|
21
21
|
- **Two memory layers, honestly measured.** Consolidated *beliefs* (decisions, preferences,
|
|
22
22
|
facts, artifacts) give you the gist. An *episodic* verbatim layer recovers the exact details
|
|
23
|
-
that lossy summaries drop.
|
|
24
|
-
scored 17
|
|
23
|
+
that lossy summaries drop. In my own LongMemEval run, raw-episodic recall scored 61% where
|
|
24
|
+
belief-only scored 17% — self-measured, not an independent benchmark; reproduce it with
|
|
25
|
+
`npm run eval`.
|
|
25
26
|
- **Automatic capture and injection.** Claude Code hooks record messages and events, then
|
|
26
27
|
inject a query-ranked memory block (with an `⚠ MOST RELEVANT` headline) into every prompt.
|
|
27
28
|
You never have to remember to save anything.
|
|
@@ -73,7 +74,7 @@ upstairs (the global brain). Quiet clerk. Perfect memory. The office runs on him
|
|
|
73
74
|
| Hierarchy | **global parent brain → per-project child brains, inherited on every prompt** | user/agent/session scopes | per-agent | per-user | per-project file |
|
|
74
75
|
| Capture | **automatic via hooks** (zero effort) | SDK calls you write | agent-managed | SDK calls | agent must remember to write |
|
|
75
76
|
| Conflict handling | supersede/merge, **recoverable — never hard-deletes** | LLM may DELETE | self-edit | invalidation | overwrite |
|
|
76
|
-
| Exact recall | episodic layer regression-tested (61% vs 17% belief-only,
|
|
77
|
+
| Exact recall | episodic layer regression-tested (61% vs 17% belief-only on LongMemEval, self-measured) | gist only | gist only | graph facts | whatever was written |
|
|
77
78
|
| Observability | **live Neural Universe monitor + daily self-audit (STL) + serve telemetry** | dashboard | — | — | — |
|
|
78
79
|
| Verification | **committed eval ledger; negative results kept** | vendor benchmarks | — | vendor benchmarks | — |
|
|
79
80
|
|
|
@@ -122,7 +123,7 @@ The guided setup asks four things:
|
|
|
122
123
|
3. Installs the **daemon** as an auto-start service (launchd on macOS, systemd user unit on Linux)
|
|
123
124
|
4. **Detects your AI apps** and wires the MCP server (plus hooks for Claude Code) into the
|
|
124
125
|
ones you pick. Auto-configured: Claude Code, Claude Desktop, Codex, Gemini CLI, Cursor,
|
|
125
|
-
Windsurf, VS Code (Copilot MCP), Zed, LM Studio. Detected with in-app instructions: ChatGPT
|
|
126
|
+
Windsurf, Cline, VS Code (Copilot MCP), Zed, LM Studio. Detected with in-app instructions: ChatGPT
|
|
126
127
|
Desktop, Perplexity Desktop. Every touched config gets a `.peon-backup`.
|
|
127
128
|
|
|
128
129
|
That builds the package, starts the daemon as a service, wires your Claude Code hooks + MCP
|
package/bin/peon-mem.mjs
CHANGED
|
@@ -48,6 +48,11 @@ function backupWrite(file, content) {
|
|
|
48
48
|
// wire kinds: hooks+CLI (claude code) · toml (codex) · mcpServers JSON (most apps) ·
|
|
49
49
|
// vscode "servers" JSON · zed context_servers · manual (UI-configured apps get instructions)
|
|
50
50
|
const APP_SUPPORT = MAC ? join(HOME, "Library", "Application Support") : join(HOME, ".config");
|
|
51
|
+
const CODE_USER = join(APP_SUPPORT, "Code", "User");
|
|
52
|
+
const CLINE_EXTENSION_HOME = join(CODE_USER, "globalStorage", "saoudrizwan.claude-dev");
|
|
53
|
+
const CLINE_EXTENSION_FILE = join(CLINE_EXTENSION_HOME, "settings", "cline_mcp_settings.json");
|
|
54
|
+
const CLINE_CLI_HOME = join(HOME, ".cline");
|
|
55
|
+
const CLINE_CLI_FILE = join(CLINE_CLI_HOME, "data", "settings", "cline_mcp_settings.json");
|
|
51
56
|
function detectApps() {
|
|
52
57
|
return [
|
|
53
58
|
{ id: "claude", name: "Claude Code", kind: "claude-code",
|
|
@@ -66,9 +71,12 @@ function detectApps() {
|
|
|
66
71
|
{ id: "windsurf", name: "Windsurf", kind: "json",
|
|
67
72
|
file: join(HOME, ".codeium", "windsurf", "mcp_config.json"),
|
|
68
73
|
found: existsSync(join(HOME, ".codeium", "windsurf")) || existsSync("/Applications/Windsurf.app") },
|
|
74
|
+
{ id: "cline", name: "Cline", kind: "json",
|
|
75
|
+
file: existsSync(CLINE_EXTENSION_HOME) ? CLINE_EXTENSION_FILE : CLINE_CLI_FILE,
|
|
76
|
+
found: existsSync(CLINE_EXTENSION_HOME) || which("cline") || existsSync(CLINE_CLI_HOME) },
|
|
69
77
|
{ id: "vscode", name: "VS Code (Copilot MCP)", kind: "vscode",
|
|
70
|
-
file:
|
|
71
|
-
found: which("code") || existsSync(
|
|
78
|
+
file: join(CODE_USER, "mcp.json"),
|
|
79
|
+
found: which("code") || existsSync(join(APP_SUPPORT, "Code")) },
|
|
72
80
|
{ id: "zed", name: "Zed", kind: "zed",
|
|
73
81
|
file: join(HOME, ".config", "zed", "settings.json"),
|
|
74
82
|
found: existsSync(join(HOME, ".config", "zed")) || existsSync("/Applications/Zed.app") },
|
|
@@ -272,7 +280,14 @@ if (cmd === "install") {
|
|
|
272
280
|
log("\n" + ((await health()) ? "✔ daemon healthy — http://127.0.0.1:3737" : "⚠ daemon not answering — check " + memoryHome + "/daemon.err.log"));
|
|
273
281
|
}
|
|
274
282
|
log("🌌 Monitor (the Neural Universe): http://127.0.0.1:3737/monitor");
|
|
275
|
-
log("Memory lives in <project>/.peon/ (child brains) + " + memoryHome + " (global brain)
|
|
283
|
+
log("Memory lives in <project>/.peon/ (child brains) + " + memoryHome + " (global brain)");
|
|
284
|
+
// People who just installed are the ones most able to say whether this works. Ask once, here —
|
|
285
|
+
// never nag from the daemon or the hooks, which run constantly and would become noise.
|
|
286
|
+
log("");
|
|
287
|
+
log(" Peon is built by one person. Two things that genuinely help:");
|
|
288
|
+
log(" ★ star it, so others find it https://github.com/VineetV2/peon-mem");
|
|
289
|
+
log(" ✎ tell me what broke or felt confusing — that is a bug report");
|
|
290
|
+
log(" https://github.com/VineetV2/peon-mem/issues\n");
|
|
276
291
|
rl?.close();
|
|
277
292
|
} else if (cmd === "uninstall") {
|
|
278
293
|
if (MAC && existsSync(PLIST)) act("stop + remove daemon service", () => spawnSync("launchctl", ["unload", PLIST], { stdio: "ignore" }));
|
|
@@ -288,7 +303,7 @@ if (cmd === "install") {
|
|
|
288
303
|
s.hooks[ev] = s.hooks[ev].filter((h) => !JSON.stringify(h).includes("claude-peon-hook.mjs"));
|
|
289
304
|
act("remove Peon hooks from " + settings, () => backupWrite(settings, JSON.stringify(s, null, 2) + "\n"));
|
|
290
305
|
} catch {}
|
|
291
|
-
log("Remove [mcp_servers.peon] / mcpServers.peon from Codex/Gemini/Cursor configs if you added them.");
|
|
306
|
+
log("Remove [mcp_servers.peon] / mcpServers.peon from Codex/Gemini/Cursor/Cline configs if you added them.");
|
|
292
307
|
log("Memory data untouched: <project>/.peon/ and " + DEFAULT_HOME);
|
|
293
308
|
rl?.close();
|
|
294
309
|
} else if (cmd === "daemon") {
|
package/dist/monitor.js
CHANGED
|
@@ -27,6 +27,10 @@ const CLIENT_SCRIPT = String.raw `
|
|
|
27
27
|
var ui = { project:"", filter:"all", search:null, focus:null, editing:null };
|
|
28
28
|
var latest = null, overview = null, network = null, dash = null, busy = false;
|
|
29
29
|
var ovLoadedAt = 0, netLoadedAt = 0, dashLoadedAt = 0, HEAVY_TTL = 12000;
|
|
30
|
+
// Load state for the heavy endpoints. Without this, a page renders its EMPTY state while data is
|
|
31
|
+
// still in flight — /network takes seconds on a large brain, so "No projects yet" was shown as
|
|
32
|
+
// fact for the whole wait, and stayed forever if the request failed (the catch was silent).
|
|
33
|
+
var loadState = { net:"idle", ov:"idle", dash:"idle" }; // idle | loading | ok | error
|
|
30
34
|
var seenActivity = {}, activityFirstLoad = true, recentActivity = []; // for live popups
|
|
31
35
|
function freshNow(){ try{ return Date.now(); }catch(e){ return 0; } }
|
|
32
36
|
var KIND_ICON = { resolve_conflict:"⚖", merge_duplicate:"⛓", compress_cluster:"🗜", reinforce:"✦" };
|
|
@@ -111,7 +115,15 @@ const CLIENT_SCRIPT = String.raw `
|
|
|
111
115
|
async function loadDashboard(force){
|
|
112
116
|
if(!force && (freshNow()-dashLoadedAt)<HEAVY_TTL) return;
|
|
113
117
|
dashLoadedAt=freshNow();
|
|
114
|
-
|
|
118
|
+
loadState.dash="loading";
|
|
119
|
+
try{
|
|
120
|
+
dash=await fetch("/global/dashboard",{cache:"no-store"}).then(function(r){
|
|
121
|
+
if(!r.ok) throw new Error("HTTP "+r.status);
|
|
122
|
+
return r.json();
|
|
123
|
+
});
|
|
124
|
+
loadState.dash="ok";
|
|
125
|
+
}catch(e){ loadState.dash="error"; dashLoadedAt=0; } // failed load retries on next visit
|
|
126
|
+
renderBrainHome();
|
|
115
127
|
}
|
|
116
128
|
// ===== NEURAL UNIVERSE — every real belief is a star; projects are galaxies =====
|
|
117
129
|
var UNI = { nodes:[], clusters:[], grid:{}, cell:46, cam:{x:0,y:0,z:1}, tx:{x:0,y:0,z:1},
|
|
@@ -503,7 +515,13 @@ const CLIENT_SCRIPT = String.raw `
|
|
|
503
515
|
'<div class="pcname">'+esc(p.projectName)+dis+'</div>'+
|
|
504
516
|
'<div class="pcstats"><span class="pcg">'+p.active+'</span> active beliefs'+(p.pinned?' · '+p.pinned+' pinned':'')+'</div>'+
|
|
505
517
|
'<div class="pcgo">open insights →</div></button>';
|
|
506
|
-
}).join(""):
|
|
518
|
+
}).join(""):(
|
|
519
|
+
// Never claim "no projects" while we are still asking, or when the ask failed — on a large
|
|
520
|
+
// brain /network takes seconds, and a silent failure used to look identical to an empty box.
|
|
521
|
+
loadState.net==="loading" ? '<div class="empty">Reading project brains…</div>' :
|
|
522
|
+
loadState.net==="error" ? '<div class="empty">Could not reach the daemon. <button class="btn sm" id="proj-retry">Retry</button></div>' :
|
|
523
|
+
'<div class="empty">No projects yet</div>');
|
|
524
|
+
var pr=EL("proj-retry"); if(pr) pr.addEventListener("click",function(){ loadNetwork(true); });
|
|
507
525
|
Array.prototype.forEach.call(document.querySelectorAll(".pcard"),function(b){ b.addEventListener("click",function(){ ui.project=b.getAttribute("data-p"); ui.search=null; ui.focus=null; overview=null; syncSwitcher(); location.hash="#/overview"; }); });
|
|
508
526
|
}
|
|
509
527
|
|
|
@@ -512,7 +530,15 @@ const CLIENT_SCRIPT = String.raw `
|
|
|
512
530
|
if(!ui.project) return;
|
|
513
531
|
if(!force && (freshNow()-ovLoadedAt)<HEAVY_TTL) return; // throttle the heavy endpoint
|
|
514
532
|
ovLoadedAt=freshNow();
|
|
515
|
-
|
|
533
|
+
loadState.ov="loading";
|
|
534
|
+
try{
|
|
535
|
+
overview=await fetch("/overview?projectPath="+encodeURIComponent(ui.project),{cache:"no-store"}).then(function(r){
|
|
536
|
+
if(!r.ok) throw new Error("HTTP "+r.status);
|
|
537
|
+
return r.json();
|
|
538
|
+
});
|
|
539
|
+
loadState.ov="ok";
|
|
540
|
+
}catch(e){ loadState.ov="error"; ovLoadedAt=0; } // failed load retries on next visit
|
|
541
|
+
renderOverviewPage();
|
|
516
542
|
}
|
|
517
543
|
function renderOverviewPage(){
|
|
518
544
|
var sel=selected();
|
|
@@ -634,7 +660,18 @@ const CLIENT_SCRIPT = String.raw `
|
|
|
634
660
|
async function loadNetwork(force){
|
|
635
661
|
if(!force && (freshNow()-netLoadedAt)<HEAVY_TTL) return; // throttle the heavy endpoint
|
|
636
662
|
netLoadedAt=freshNow();
|
|
637
|
-
|
|
663
|
+
loadState.net="loading"; renderProjectsGrid();
|
|
664
|
+
try{
|
|
665
|
+
network=await fetch("/network",{cache:"no-store"}).then(function(r){
|
|
666
|
+
if(!r.ok) throw new Error("HTTP "+r.status);
|
|
667
|
+
return r.json();
|
|
668
|
+
});
|
|
669
|
+
loadState.net="ok";
|
|
670
|
+
}catch(e){
|
|
671
|
+
loadState.net="error";
|
|
672
|
+
netLoadedAt=0; // a failed load must not be throttled — let the next visit retry
|
|
673
|
+
}
|
|
674
|
+
renderProjectsGrid();
|
|
638
675
|
}
|
|
639
676
|
|
|
640
677
|
// ===== OPS (tokens + activity) =====
|
|
@@ -746,6 +783,10 @@ const DOCUMENT = String.raw `<!doctype html>
|
|
|
746
783
|
box-shadow:0 0 18px -4px rgba(89,227,255,.7), inset 0 0 12px rgba(89,227,255,.12); text-shadow:0 0 10px rgba(89,227,255,.8);}
|
|
747
784
|
.switcher{background:rgba(5,18,32,.9); border:1px solid var(--line2); color:var(--cyan-ink); clip-path:var(--cham);
|
|
748
785
|
padding:7px 12px; max-width:230px; font-size:12px; font-family:var(--mono);}
|
|
786
|
+
/* Quiet star link — same weight as the status text, never competes with the field. */
|
|
787
|
+
.ghlink{flex:none; font-family:var(--mono); font-size:10px; letter-spacing:.12em; text-transform:uppercase;
|
|
788
|
+
color:var(--muted); text-decoration:none; border:1px solid rgba(89,227,255,.25); padding:4px 9px; clip-path:var(--cham);}
|
|
789
|
+
.ghlink:hover{color:var(--cyan); border-color:var(--cyan); box-shadow:0 0 16px -6px rgba(89,227,255,.8);}
|
|
749
790
|
.live{display:flex; align-items:center; gap:8px; font-size:10.5px; color:var(--muted); flex:none;
|
|
750
791
|
font-family:var(--mono); text-transform:uppercase; letter-spacing:.14em;}
|
|
751
792
|
.dot{width:8px; height:8px; border-radius:50%; background:var(--cyan); box-shadow:0 0 12px var(--cyan); animation:beat 2s infinite;}
|
|
@@ -1026,6 +1067,8 @@ const DOCUMENT = String.raw `<!doctype html>
|
|
|
1026
1067
|
<a id="nav-ops" href="#/ops">Systems</a>
|
|
1027
1068
|
</nav>
|
|
1028
1069
|
<select class="switcher" id="switcher" aria-label="Active project"></select>
|
|
1070
|
+
<a class="ghlink" href="https://github.com/VineetV2/peon-mem" target="_blank" rel="noopener noreferrer"
|
|
1071
|
+
title="Peon is open source — star it so others find it">★ Star</a>
|
|
1029
1072
|
<div class="live"><span class="dot" id="dot"></span><span id="status">linking…</span></div>
|
|
1030
1073
|
</header>
|
|
1031
1074
|
|