memoryai-claude 0.1.2 → 0.1.4

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 MemoryAI
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MemoryAI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,69 +1,69 @@
1
- # MemoryAI for Claude Code
2
-
3
- Your AI keeps forgetting you. MemoryAI gives Claude Code a real long-term
4
- memory: one that follows you across every model.
5
-
6
- ```text
7
- You move models. You move IDEs. Your memory stays.
8
- ```
9
-
10
- ## Install (one command)
11
-
12
- ```bash
13
- npx memoryai-claude install
14
- ```
15
-
16
- That's it. After it finishes, restart Claude Code once and just work. Past
17
- decisions, preferences, and recent project context come back at the start of
18
- each prompt; important moments save automatically when each turn ends.
19
-
20
- ## Get a key
21
-
22
- Pick whichever is easier:
23
-
24
- - Skip the prompt and a free key is created for you on install
25
- - Or grab one upfront at https://memoryai.dev/connect and paste it
26
-
27
- ## Health check
28
-
29
- ```bash
30
- npx memoryai-claude doctor
31
- ```
32
-
33
- Confirms the three hooks are wired and that each endpoint actually responds.
34
-
35
- ## Status
36
-
37
- ```bash
38
- npx memoryai-claude status
39
- ```
40
-
41
- Tells you how many hooks are wired in user-scope and project-scope settings.
42
-
43
- ## Uninstall
44
-
45
- ```bash
46
- npx memoryai-claude uninstall
47
- ```
48
-
49
- Removes the hooks from `settings.json`. Foreign hooks stay untouched. Memory
50
- on the server is preserved unless you delete it from the dashboard.
51
-
52
- ## What gets touched
53
-
54
- - `~/.claude/settings.json` (or `./.claude/settings.json` with `--project`)
55
- - `~/.claude/CLAUDE.md` (or `./CLAUDE.md` with `--project`) — a short note
56
- is appended so the agent knows memory is wired.
57
-
58
- The original file is backed up before each write.
59
-
60
- ## Privacy
61
-
62
- - API keys live in your local `settings.json`. They never leave your machine
63
- except to talk to the endpoint you configured.
64
- - No telemetry from this CLI itself.
65
- - Add `MEMORYAI_NONINTERACTIVE=1` to skip prompts during scripted installs.
66
-
67
- ## License
68
-
69
- MIT.
1
+ # MemoryAI for Claude Code
2
+
3
+ Your AI keeps forgetting you. MemoryAI gives Claude Code a real long-term
4
+ memory: one that follows you across every model.
5
+
6
+ ```text
7
+ You move models. You move IDEs. Your memory stays.
8
+ ```
9
+
10
+ ## Install (one command)
11
+
12
+ ```bash
13
+ npx memoryai-claude install
14
+ ```
15
+
16
+ That's it. After it finishes, restart Claude Code once and just work. Past
17
+ decisions, preferences, and recent project context come back at the start of
18
+ each prompt; important moments save automatically when each turn ends.
19
+
20
+ ## Get a key
21
+
22
+ Pick whichever is easier:
23
+
24
+ - Skip the prompt and a free key is created for you on install
25
+ - Or grab one upfront at https://memoryai.dev/connect and paste it
26
+
27
+ ## Health check
28
+
29
+ ```bash
30
+ npx memoryai-claude doctor
31
+ ```
32
+
33
+ Confirms the three hooks are wired and that each endpoint actually responds.
34
+
35
+ ## Status
36
+
37
+ ```bash
38
+ npx memoryai-claude status
39
+ ```
40
+
41
+ Tells you how many hooks are wired in user-scope and project-scope settings.
42
+
43
+ ## Uninstall
44
+
45
+ ```bash
46
+ npx memoryai-claude uninstall
47
+ ```
48
+
49
+ Removes the hooks from `settings.json`. Foreign hooks stay untouched. Memory
50
+ on the server is preserved unless you delete it from the dashboard.
51
+
52
+ ## What gets touched
53
+
54
+ - `~/.claude/settings.json` (or `./.claude/settings.json` with `--project`)
55
+ - `~/.claude/CLAUDE.md` (or `./CLAUDE.md` with `--project`) — a short note
56
+ is appended so the agent knows memory is wired.
57
+
58
+ The original file is backed up before each write.
59
+
60
+ ## Privacy
61
+
62
+ - API keys live in your local `settings.json`. They never leave your machine
63
+ except to talk to the endpoint you configured.
64
+ - No telemetry from this CLI itself.
65
+ - Add `MEMORYAI_NONINTERACTIVE=1` to skip prompts during scripted installs.
66
+
67
+ ## License
68
+
69
+ MIT.
package/dist/cli.js CHANGED
@@ -1,258 +1,24 @@
1
1
  #!/usr/bin/env node
2
- "use strict";
3
-
4
- // src/cli.ts
5
- var import_node_fs3 = require("node:fs");
6
- var import_node_path2 = require("node:path");
7
- var import_node_os2 = require("node:os");
8
-
9
- // src/prompts.ts
10
- var import_promises = require("node:readline/promises");
11
- var import_node_process = require("node:process");
12
- async function ask(question, fallback) {
13
- const rl = (0, import_promises.createInterface)({ input: import_node_process.stdin, output: import_node_process.stdout });
14
- try {
15
- const suffix = fallback ? ` [${fallback}]` : "";
16
- const answer = (await rl.question(`${question}${suffix}: `)).trim();
17
- return answer || fallback || "";
18
- } finally {
19
- rl.close();
20
- }
21
- }
22
- async function askYesNo(question, defYes = true) {
23
- const ans = (await ask(`${question} (${defYes ? "Y/n" : "y/N"})`, defYes ? "y" : "n")).toLowerCase();
24
- return ans.startsWith("y");
25
- }
26
- function isNonInteractive() {
27
- return process.env.MEMORYAI_NONINTERACTIVE === "1" || process.env.CI === "true" || !process.stdin.isTTY;
28
- }
29
-
30
- // src/settings.ts
31
- var import_node_fs = require("node:fs");
32
- var import_node_path = require("node:path");
33
- var import_node_os = require("node:os");
34
- function settingsPath(scope) {
35
- return scope === "project" ? (0, import_node_path.join)(process.cwd(), ".claude", "settings.json") : (0, import_node_path.join)((0, import_node_os.homedir)(), ".claude", "settings.json");
36
- }
37
- function claudeMdPath(scope) {
38
- return scope === "project" ? (0, import_node_path.join)(process.cwd(), "CLAUDE.md") : (0, import_node_path.join)((0, import_node_os.homedir)(), ".claude", "CLAUDE.md");
39
- }
40
- function readJsonSafe(path) {
41
- if (!(0, import_node_fs.existsSync)(path)) return {};
42
- try {
43
- return JSON.parse((0, import_node_fs.readFileSync)(path, "utf-8")) || {};
44
- } catch {
45
- throw new Error(`${path} is not valid JSON. Fix it manually before re-running.`);
46
- }
47
- }
48
- function writeJson(path, data) {
49
- (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(path), { recursive: true });
50
- (0, import_node_fs.writeFileSync)(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
51
- }
52
- function backup(path) {
53
- if (!(0, import_node_fs.existsSync)(path)) return null;
54
- const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
55
- const dest = `${path}.bak-${stamp}`;
56
- (0, import_node_fs.copyFileSync)(path, dest);
57
- return dest;
58
- }
59
-
60
- // src/hooks.ts
61
- var MEMORYAI_PATH_MARKER = "/v1/hooks/claude/";
62
- var MEMORYAI_COMMAND_MARKER = "memoryai/session-start-runner";
63
- function httpHook(endpoint, apiKey, timeout) {
64
- return {
65
- type: "http",
66
- url: endpoint,
67
- timeout,
68
- headers: { Authorization: `Bearer ${apiKey}` },
69
- allowedEnvVars: []
70
- };
71
- }
72
- function commandHook(nodeBinary, runnerPath, timeout) {
73
- return {
74
- type: "command",
75
- command: nodeBinary,
76
- args: [runnerPath],
77
- timeout
78
- };
79
- }
80
- function isMemoryAiHandler(h) {
81
- if (!h) return false;
82
- if (typeof h.url === "string" && h.url.includes(MEMORYAI_PATH_MARKER)) return true;
83
- if (typeof h.command === "string") {
84
- const args = Array.isArray(h.args) ? h.args.join(" ") : "";
85
- const blob = `${h.command} ${args}`;
86
- if (blob.includes(MEMORYAI_COMMAND_MARKER) || blob.includes("session-start-runner")) return true;
87
- }
88
- return false;
89
- }
90
- function groupHasMemoryAi(group) {
91
- const handlers = group && group.hooks || [];
92
- return handlers.some(isMemoryAiHandler);
93
- }
94
- function ensureHook(settings, event, handler) {
95
- settings.hooks = settings.hooks || {};
96
- settings.hooks[event] = settings.hooks[event] || [];
97
- if (settings.hooks[event].some(groupHasMemoryAi)) return false;
98
- settings.hooks[event].push({ hooks: [handler] });
99
- return true;
100
- }
101
- function removeMemoryAiHooks(settings) {
102
- if (!settings.hooks) return 0;
103
- let removed = 0;
104
- for (const event of Object.keys(settings.hooks)) {
105
- const before = (settings.hooks[event] || []).length;
106
- settings.hooks[event] = (settings.hooks[event] || []).filter((g) => !groupHasMemoryAi(g));
107
- removed += before - settings.hooks[event].length;
108
- if (settings.hooks[event].length === 0) delete settings.hooks[event];
109
- }
110
- if (Object.keys(settings.hooks).length === 0) delete settings.hooks;
111
- return removed;
112
- }
113
- function describeHooks(settings) {
114
- const out = {
115
- SessionStart: { present: false },
116
- UserPromptSubmit: { present: false },
117
- Stop: { present: false }
118
- };
119
- for (const event of Object.keys(out)) {
120
- const groups = settings?.hooks?.[event] || [];
121
- for (const g of groups) {
122
- const h = (g.hooks || []).find(isMemoryAiHandler);
123
- if (h) {
124
- out[event] = {
125
- present: true,
126
- url: h.url,
127
- command: h.command ? `${h.command} ${(h.args || []).join(" ")}`.trim() : void 0,
128
- timeout: h.timeout
129
- };
130
- break;
131
- }
132
- }
133
- }
134
- return out;
135
- }
136
-
137
- // src/notes.ts
138
- var import_node_fs2 = require("node:fs");
139
- var MARKER = "<!-- memoryai:auto-note -->";
140
- var NOTE = `
141
- ${MARKER}
2
+ "use strict";var l=require("node:fs"),g=require("node:path"),ne=require("node:os");var G=require("node:readline/promises"),A=require("node:process");async function w(e,o){let t=(0,G.createInterface)({input:A.stdin,output:A.stdout});try{let n=o?` [${o}]`:"";return(await t.question(`${e}${n}: `)).trim()||o||""}finally{t.close()}}async function M(e,o=!0){return(await w(`${e} (${o?"Y/n":"y/N"})`,o?"y":"n")).toLowerCase().startsWith("y")}function $(){return process.env.MEMORYAI_NONINTERACTIVE==="1"||process.env.CI==="true"||!process.stdin.isTTY}var f=require("node:fs"),b=require("node:path"),R=require("node:os");function S(e){return e==="project"?(0,b.join)(process.cwd(),".claude","settings.json"):(0,b.join)((0,R.homedir)(),".claude","settings.json")}function I(e){return e==="project"?(0,b.join)(process.cwd(),"CLAUDE.md"):(0,b.join)((0,R.homedir)(),".claude","CLAUDE.md")}function _(e){if(!(0,f.existsSync)(e))return{};try{return JSON.parse((0,f.readFileSync)(e,"utf-8"))||{}}catch{throw new Error(`${e} is not valid JSON. Fix it manually before re-running.`)}}function J(e,o){try{(0,f.chmodSync)(e,o)}catch{}}function O(e,o){(0,f.mkdirSync)((0,b.dirname)(e),{recursive:!0}),(0,f.writeFileSync)(e,JSON.stringify(o,null,2)+`
3
+ `,"utf-8"),J(e,384)}function T(e){if(!(0,f.existsSync)(e))return null;let o=new Date().toISOString().replace(/[:.]/g,"-"),t=`${e}.bak-${o}`;return(0,f.copyFileSync)(e,t),J(t,384),t}var ae="/v1/hooks/claude/",le="memoryai/session-start-runner";function H(e,o,t){return{type:"http",url:e,timeout:t,headers:{Authorization:`Bearer ${o}`},allowedEnvVars:[]}}function Y(e,o,t){return{type:"command",command:e,args:[o],timeout:t}}function V(e){if(!e)return!1;if(typeof e.url=="string"&&e.url.includes(ae))return!0;if(typeof e.command=="string"){let o=Array.isArray(e.args)?e.args.join(" "):"",t=`${e.command} ${o}`;if(t.includes(le)||t.includes("session-start-runner"))return!0}return!1}function z(e){return(e&&e.hooks||[]).some(V)}function P(e,o,t){return e.hooks=e.hooks||{},e.hooks[o]=e.hooks[o]||[],e.hooks[o].some(z)?!1:(e.hooks[o].push({hooks:[t]}),!0)}function B(e){if(!e.hooks)return 0;let o=0;for(let t of Object.keys(e.hooks)){let n=(e.hooks[t]||[]).length;e.hooks[t]=(e.hooks[t]||[]).filter(r=>!z(r)),o+=n-e.hooks[t].length,e.hooks[t].length===0&&delete e.hooks[t]}return Object.keys(e.hooks).length===0&&delete e.hooks,o}function C(e){let o={SessionStart:{present:!1},UserPromptSubmit:{present:!1},Stop:{present:!1}};for(let t of Object.keys(o)){let n=e?.hooks?.[t]||[];for(let r of n){let s=(r.hooks||[]).find(V);if(s){o[t]={present:!0,url:s.url,command:s.command?`${s.command} ${(s.args||[]).join(" ")}`.trim():void 0,timeout:s.timeout};break}}}return o}var k=require("node:fs"),E="<!-- memoryai:auto-note -->",W=`
4
+ ${E}
142
5
  ## MemoryAI
143
6
 
144
7
  Memory works automatically here. Past decisions, preferences, and recent project
145
8
  context are recalled before each prompt and saved when each turn ends. Nothing
146
9
  to call by hand \u2014 just work normally.
147
- `;
148
- function ensureNote(path) {
149
- const existing = (0, import_node_fs2.existsSync)(path) ? (0, import_node_fs2.readFileSync)(path, "utf-8") : "";
150
- if (existing.includes(MARKER)) return "skipped";
151
- const next = existing ? `${existing.replace(/\s*$/, "")}
152
- ${NOTE}` : NOTE;
153
- (0, import_node_fs2.writeFileSync)(path, next, "utf-8");
154
- return existing ? "appended" : "created";
155
- }
156
- function removeNote(path) {
157
- if (!(0, import_node_fs2.existsSync)(path)) return false;
158
- const existing = (0, import_node_fs2.readFileSync)(path, "utf-8");
159
- if (!existing.includes(MARKER)) return false;
160
- const cleaned = existing.replace(new RegExp(`\\n*${MARKER}[\\s\\S]*$`, "m"), "").replace(/\s*$/, "\n");
161
- (0, import_node_fs2.writeFileSync)(path, cleaned, "utf-8");
162
- return true;
163
- }
164
-
165
- // src/api.ts
166
- async function healthcheck(endpoint, apiKey) {
167
- const base = endpoint.replace(/\/+$/, "");
168
- try {
169
- const resp = await fetch(`${base}/v1/stats`, {
170
- method: "GET",
171
- headers: { Authorization: `Bearer ${apiKey}` }
172
- });
173
- return resp.ok;
174
- } catch {
175
- return false;
176
- }
177
- }
178
- async function provisionKey(endpoint, name) {
179
- const base = endpoint.replace(/\/+$/, "");
180
- try {
181
- const resp = await fetch(`${base}/v1/admin/provision`, {
182
- method: "POST",
183
- headers: { "Content-Type": "application/json" },
184
- body: JSON.stringify({ name: name || "claude-code", tos_accepted: true })
185
- });
186
- if (!resp.ok) {
187
- return null;
188
- }
189
- const data = await resp.json();
190
- if (data && typeof data.api_key === "string") {
191
- return { api_key: data.api_key, plan: data.plan };
192
- }
193
- return null;
194
- } catch {
195
- return null;
196
- }
197
- }
198
- async function pingHooks(endpoint, apiKey) {
199
- const base = endpoint.replace(/\/+$/, "");
200
- const results = {};
201
- const events = [
202
- // SessionStart hook now uses /v1/ide/guard/bootstrap (called by the
203
- // local Node runner), not /v1/hooks/claude/session-start. The runner
204
- // wakes Claude Code's session by printing the bootstrap context block.
205
- { event: "SessionStart", path: "/v1/ide/guard/bootstrap", body: { task: "", limit: 1 } },
206
- { event: "UserPromptSubmit", path: "/v1/hooks/claude/user-prompt", body: { prompt: "health check from memoryai-claude doctor" } },
207
- { event: "Stop", path: "/v1/hooks/claude/stop", body: { last_assistant_message: "" } }
208
- ];
209
- for (const { event, path, body } of events) {
210
- try {
211
- const resp = await fetch(`${base}${path}`, {
212
- method: "POST",
213
- headers: {
214
- "Content-Type": "application/json",
215
- Authorization: `Bearer ${apiKey}`
216
- },
217
- body: JSON.stringify(body)
218
- });
219
- results[event] = resp.ok;
220
- } catch {
221
- results[event] = false;
222
- }
223
- }
224
- return results;
225
- }
226
-
227
- // src/cli.ts
228
- var VERSION = "0.1.1";
229
- var DEFAULT_ENDPOINT = "https://memoryai.dev";
230
- var RUNTIME_DIR = (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".memoryai");
231
- var RUNTIME_CONFIG = (0, import_node_path2.join)(RUNTIME_DIR, "claude.json");
232
- var RUNTIME_RUNNER = (0, import_node_path2.join)(RUNTIME_DIR, "session-start-runner.js");
233
- function parseArgs(argv) {
234
- const out = { command: argv[0] || "help" };
235
- for (let i = 1; i < argv.length; i++) {
236
- const a = argv[i];
237
- if (a === "--user") out.scope = "user";
238
- else if (a === "--project") out.scope = "project";
239
- else if (a === "--endpoint") out.endpoint = argv[++i];
240
- else if (a === "--key") out.key = argv[++i];
241
- else if (a === "--yes" || a === "-y") out.yes = true;
242
- }
243
- return out;
244
- }
245
- function printHeader() {
246
- console.log(`MemoryAI for Claude Code v${VERSION}`);
247
- console.log(`One brain. Every AI you use. Forever.`);
248
- console.log("");
249
- }
250
- function printHelp() {
251
- printHeader();
252
- console.log(`Usage:
253
- memoryai-claude install [--user|--project] [--endpoint URL] [--key KEY] [--yes]
10
+ `;function q(e){let o=(0,k.existsSync)(e)?(0,k.readFileSync)(e,"utf-8"):"";if(o.includes(E))return"skipped";let t=o?`${o.replace(/\s*$/,"")}
11
+ ${W}`:W;return(0,k.writeFileSync)(e,t,"utf-8"),o?"appended":"created"}function X(e){if(!(0,k.existsSync)(e))return!1;let o=(0,k.readFileSync)(e,"utf-8");if(!o.includes(E))return!1;let t=o.replace(new RegExp(`\\n*${E}[\\s\\S]*$`,"m"),"").replace(/\s*$/,`
12
+ `);return(0,k.writeFileSync)(e,t,"utf-8"),!0}async function Q(e,o){let t=e.replace(/\/+$/,"");try{return(await fetch(`${t}/v1/stats`,{method:"GET",headers:{Authorization:`Bearer ${o}`}})).ok}catch{return!1}}async function Z(e,o){let t=e.replace(/\/+$/,"");try{let n=await fetch(`${t}/v1/admin/provision`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:o||"claude-code",tos_accepted:!0})});if(!n.ok)return null;let r=await n.json();return r&&typeof r.api_key=="string"?{api_key:r.api_key,plan:r.plan}:null}catch{return null}}async function ee(e,o){let t=e.replace(/\/+$/,""),n=[{event:"SessionStart",path:"/v1/ide/guard/bootstrap",body:{task:"",limit:1,source:"doctor-probe"}},{event:"UserPromptSubmit",path:"/v1/hooks/claude/user-prompt",body:{prompt:""}},{event:"Stop",path:"/v1/hooks/claude/stop",body:{last_assistant_message:""}}],r=async({event:a,path:p,body:d})=>{try{let i=await fetch(`${t}${p}`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`,"X-MemoryAI-Probe":"1"},body:JSON.stringify(d)});return[a,i.ok]}catch{return[a,!1]}},s=await Promise.all(n.map(r));return Object.fromEntries(s)}async function oe(e,o){let t=e.replace(/\/+$/,"");try{let n=await fetch(`${t}/v1/context/guard/settings`,{method:"GET",headers:{Authorization:`Bearer ${o}`}});return n.ok?await n.json():null}catch{return null}}async function x(e,o,t){let n=e.replace(/\/+$/,""),r={};t.compactAtTokens!==void 0&&(r.compact_at_tokens=t.compactAtTokens),t.criticalAtTokens!==void 0&&(r.critical_at_tokens=t.criticalAtTokens);try{let s=await fetch(`${n}/v1/context/guard/settings`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify(r)});if(!s.ok)try{let a=await s.json();return{ok:!1,settings:null,errors:a?.errors||(a?.error?[a.error]:[`HTTP ${s.status}`])}}catch{return{ok:!1,settings:null,errors:[`HTTP ${s.status}`]}}return{ok:!0,settings:await s.json()}}catch(s){return{ok:!1,settings:null,errors:[s.message]}}}var re=(()=>{try{let e=require((0,g.join)(__dirname,"..","package.json"));if(e&&typeof e.version=="string")return e.version}catch{}return"0.0.0-dev"})(),U="https://memoryai.dev",j=(0,g.join)((0,ne.homedir)(),".memoryai"),m=(0,g.join)(j,"claude.json"),L=(0,g.join)(j,"session-start-runner.js");function ue(e){let o={command:e[0]||"help"};o.command==="config"&&e[1]&&!e[1].startsWith("-")&&(o.configSub=e[1],e[2]&&!e[2].startsWith("-")&&(o.configValue=e[2]));for(let t=1;t<e.length;t++){let n=e[t];n==="--user"?o.scope="user":n==="--project"?o.scope="project":n==="--endpoint"?o.endpoint=e[++t]:n==="--key"?o.key=e[++t]:n==="--model"?o.model=e[++t]:(n==="--yes"||n==="-y")&&(o.yes=!0)}return o}function v(){console.log(`MemoryAI for Claude Code v${re}`),console.log("One brain. Every AI you use. Forever."),console.log("")}function te(){v(),console.log(`Usage:
13
+ memoryai-claude install [--user|--project] [--endpoint URL] [--key KEY] [--model NAME] [--yes]
254
14
  memoryai-claude doctor
255
15
  memoryai-claude status
16
+ memoryai-claude config (show current compact thresholds)
17
+ memoryai-claude config compact <N> (set soft warning threshold, tokens)
18
+ memoryai-claude config critical <N> (set hard ceiling threshold, tokens)
19
+ memoryai-claude config compact reset (clear soft warning override)
20
+ memoryai-claude config critical reset (clear hard ceiling override)
21
+ memoryai-claude config reset (clear both overrides \u2192 defaults)
256
22
  memoryai-claude logs
257
23
  memoryai-claude uninstall [--user|--project]
258
24
  memoryai-claude help
@@ -261,289 +27,12 @@ Memory works automatically once installed: relevant context comes back at the
261
27
  start of each prompt, and important moments save when each turn ends. There is
262
28
  nothing to call by hand.
263
29
 
30
+ Threshold settings live on the SERVER per tenant \u2014 once set here, they apply
31
+ across every IDE / host you connect from (Kiro, Cursor, Claude Code, ...).
32
+ Defaults: compact at 150,000 tokens, critical at 200,000 tokens.
33
+
264
34
  Get a free key at https://memoryai.dev (or leave it blank during install and a
265
35
  key will be created for you).
266
- `);
267
- }
268
- async function resolveScope(args) {
269
- if (args.scope) return args.scope;
270
- if (isNonInteractive()) return "user";
271
- const ans = (await ask("Apply to (u)ser globally or this (p)roject?", "u")).toLowerCase();
272
- return ans.startsWith("p") ? "project" : "user";
273
- }
274
- async function resolveEndpoint(args) {
275
- if (args.endpoint) return args.endpoint;
276
- const envEndpoint = process.env.HM_ENDPOINT || process.env.MEMORYAI_ENDPOINT;
277
- if (envEndpoint) return envEndpoint;
278
- if (isNonInteractive()) return DEFAULT_ENDPOINT;
279
- return await ask("Endpoint", DEFAULT_ENDPOINT);
280
- }
281
- async function resolveKey(args, endpoint) {
282
- if (args.key) return args.key;
283
- const envKey = process.env.HM_API_KEY || process.env.MEMORYAI_API_KEY;
284
- if (envKey) return envKey;
285
- let key = "";
286
- if (!isNonInteractive()) {
287
- key = (await ask("API key (leave blank to auto-create a free one")).trim();
288
- }
289
- if (!key) {
290
- process.stdout.write(" ... creating a free key for you ...");
291
- const provisioned = await provisionKey(endpoint, "claude-code");
292
- if (provisioned) {
293
- console.log(` ok (plan=${provisioned.plan || "free"})`);
294
- return provisioned.api_key;
295
- }
296
- console.log(" failed");
297
- throw new Error(
298
- "Could not create a key automatically. Get one from https://memoryai.dev/connect and rerun with --key."
299
- );
300
- }
301
- return key;
302
- }
303
- function relativizeUrl(url) {
304
- try {
305
- const u = new URL(url);
306
- return `${u.origin}${u.pathname.replace("/v1/hooks/claude/", "/v1/hooks/claude/")}`;
307
- } catch {
308
- return url;
309
- }
310
- }
311
- async function cmdInstall(args) {
312
- printHeader();
313
- const scope = await resolveScope(args);
314
- const endpoint = await resolveEndpoint(args);
315
- const apiKey = await resolveKey(args, endpoint);
316
- process.stdout.write(" ... verifying key ...");
317
- const ok = await healthcheck(endpoint, apiKey);
318
- if (!ok) {
319
- console.log(" failed");
320
- if (!args.yes && !isNonInteractive()) {
321
- const proceed = await askYesNo("Server did not accept the key (or is offline). Continue anyway?", false);
322
- if (!proceed) throw new Error("Aborted by user.");
323
- }
324
- } else {
325
- console.log(" ok");
326
- }
327
- const sPath = settingsPath(scope);
328
- const cMdPath = claudeMdPath(scope);
329
- let settings = readJsonSafe(sPath);
330
- const backupPath = backup(sPath);
331
- if (backupPath) console.log(` back ${backupPath}`);
332
- (0, import_node_fs3.mkdirSync)(RUNTIME_DIR, { recursive: true });
333
- (0, import_node_fs3.writeFileSync)(
334
- RUNTIME_CONFIG,
335
- JSON.stringify({ endpoint, apiKey }, null, 2) + "\n",
336
- "utf-8"
337
- );
338
- try {
339
- require("node:fs").chmodSync(RUNTIME_CONFIG, 384);
340
- } catch {
341
- }
342
- const runnerSrc = locateRunner();
343
- if (!runnerSrc) {
344
- throw new Error(
345
- "session-start-runner.js not found alongside the CLI. If running from source, run `npm run build` first."
346
- );
347
- }
348
- (0, import_node_fs3.copyFileSync)(runnerSrc, RUNTIME_RUNNER);
349
- console.log(` setup ${RUNTIME_RUNNER}`);
350
- const nodeBin = process.execPath;
351
- const base = endpoint.replace(/\/+$/, "");
352
- const added = {
353
- SessionStart: ensureHook(settings, "SessionStart", commandHook(nodeBin, RUNTIME_RUNNER, 12)),
354
- UserPromptSubmit: ensureHook(settings, "UserPromptSubmit", httpHook(`${base}/v1/hooks/claude/user-prompt`, apiKey, 10)),
355
- Stop: ensureHook(settings, "Stop", httpHook(`${base}/v1/hooks/claude/stop`, apiKey, 15))
356
- };
357
- for (const [event, didAdd] of Object.entries(added)) {
358
- console.log(` ${didAdd ? "add " : "skip "} hook ${event}${didAdd ? "" : " (already present)"}`);
359
- }
360
- writeJson(sPath, settings);
361
- console.log(` write ${sPath}`);
362
- const noteResult = ensureNote(cMdPath);
363
- if (noteResult === "created") console.log(` create ${cMdPath}`);
364
- else if (noteResult === "appended") console.log(` append ${cMdPath}`);
365
- else console.log(` skip ${cMdPath} (note already present)`);
366
- console.log("");
367
- console.log("Installed. Restart Claude Code once, then just work.");
368
- console.log(" - Past context returns at the start of each prompt.");
369
- console.log(" - Important moments save when each turn ends.");
370
- console.log(" - Run `memoryai-claude doctor` any time to verify health.");
371
- }
372
- function locateRunner() {
373
- const candidates = [
374
- (0, import_node_path2.join)((0, import_node_path2.dirname)(process.argv[1] || ""), "session-start-runner.js"),
375
- (0, import_node_path2.join)(__dirname, "session-start-runner.js")
376
- ];
377
- for (const c of candidates) {
378
- if (c && (0, import_node_fs3.existsSync)(c)) return c;
379
- }
380
- return null;
381
- }
382
- async function cmdDoctor() {
383
- printHeader();
384
- console.log("Diagnostics:");
385
- const userPath = settingsPath("user");
386
- const projectPath = settingsPath("project");
387
- let foundAny = false;
388
- for (const [scope, path] of [
389
- ["user", userPath],
390
- ["project", projectPath]
391
- ]) {
392
- if (!(0, import_node_fs3.existsSync)(path)) {
393
- console.log(` -- ${scope}: ${path} (not present)`);
394
- continue;
395
- }
396
- const settings = readJsonSafe(path);
397
- const desc = describeHooks(settings);
398
- const present = Object.values(desc).filter((d) => d.present).length;
399
- if (present === 0) {
400
- console.log(` -- ${scope}: ${path} (no MemoryAI hooks)`);
401
- continue;
402
- }
403
- foundAny = true;
404
- console.log(` ok ${scope}: ${path}`);
405
- for (const [event, info] of Object.entries(desc)) {
406
- const detail = info.url ? ` ${relativizeUrl(info.url)}` : info.command ? ` command: ${info.command}` : "";
407
- console.log(` ${info.present ? "present" : "MISSING"} ${event}${detail}`);
408
- }
409
- let endpoint = "";
410
- let apiKey = "";
411
- if ((0, import_node_fs3.existsSync)(RUNTIME_CONFIG)) {
412
- try {
413
- const cfg = JSON.parse((0, import_node_fs3.readFileSync)(RUNTIME_CONFIG, "utf-8"));
414
- if (cfg && typeof cfg.endpoint === "string") endpoint = cfg.endpoint;
415
- if (cfg && typeof cfg.apiKey === "string") apiKey = cfg.apiKey;
416
- } catch {
417
- }
418
- }
419
- if (!endpoint || !apiKey) {
420
- const handler = findFirstHttpHandler(settings);
421
- if (handler?.url) {
422
- endpoint = new URL(handler.url).origin;
423
- apiKey = (handler.headers?.Authorization || "").replace(/^Bearer\s+/i, "");
424
- }
425
- }
426
- if (endpoint && apiKey) {
427
- process.stdout.write(` ... ping endpoints ...`);
428
- const pings = await pingHooks(endpoint, apiKey);
429
- console.log("");
430
- for (const [event, ok] of Object.entries(pings)) {
431
- console.log(` ${ok ? "ok" : "FAIL"} ${event}`);
432
- }
433
- }
434
- }
435
- if (!foundAny) {
436
- console.log("");
437
- console.log("No MemoryAI hooks found in either user or project settings.");
438
- console.log("Run `memoryai-claude install` to wire them up.");
439
- }
440
- }
441
- function findFirstHttpHandler(settings) {
442
- const events = settings?.hooks || {};
443
- for (const event of Object.keys(events)) {
444
- for (const group of events[event] || []) {
445
- for (const h of group.hooks || []) {
446
- if (typeof h?.url === "string" && h.url.includes("/v1/hooks/claude/")) return h;
447
- }
448
- }
449
- }
450
- return null;
451
- }
452
- async function cmdLogs() {
453
- const logPath = (0, import_node_path2.join)(RUNTIME_DIR, "runner.log");
454
- if (!(0, import_node_fs3.existsSync)(logPath)) {
455
- printHeader();
456
- console.log(`No SessionStart hook activity yet (${logPath} does not exist).`);
457
- console.log("This means Claude Code has not fired SessionStart since install.");
458
- console.log("Open a fresh terminal and run `claude` to trigger it.");
459
- return;
460
- }
461
- const text = (0, import_node_fs3.readFileSync)(logPath, "utf-8");
462
- const lines = text.trim().split(/\r?\n/);
463
- const tail = lines.slice(-20).join("\n");
464
- printHeader();
465
- console.log(`SessionStart runner log (${logPath}):`);
466
- console.log("");
467
- console.log(tail);
468
- }
469
- async function cmdStatus() {
470
- printHeader();
471
- for (const scope of ["user", "project"]) {
472
- const path = settingsPath(scope);
473
- if (!(0, import_node_fs3.existsSync)(path)) {
474
- console.log(` ${scope}: not present`);
475
- continue;
476
- }
477
- const settings = readJsonSafe(path);
478
- const desc = describeHooks(settings);
479
- const wired = Object.values(desc).filter((d) => d.present).length;
480
- console.log(` ${scope}: ${wired}/3 hooks wired (${path})`);
481
- }
482
- }
483
- async function cmdUninstall(args) {
484
- printHeader();
485
- const scope = await resolveScope(args);
486
- const sPath = settingsPath(scope);
487
- const cMdPath = claudeMdPath(scope);
488
- if (!(0, import_node_fs3.existsSync)(sPath)) {
489
- console.log(` Nothing to do \u2014 ${sPath} does not exist.`);
490
- return;
491
- }
492
- if (!args.yes && !isNonInteractive()) {
493
- const ok = await askYesNo(`Remove MemoryAI hooks from ${sPath}?`, true);
494
- if (!ok) return;
495
- }
496
- const settings = readJsonSafe(sPath);
497
- const backupPath = backup(sPath);
498
- if (backupPath) console.log(` back ${backupPath}`);
499
- const removed = removeMemoryAiHooks(settings);
500
- writeJson(sPath, settings);
501
- console.log(` removed ${removed} hook${removed === 1 ? "" : "s"} from ${sPath}`);
502
- if (removeNote(cMdPath)) {
503
- console.log(` cleaned ${cMdPath}`);
504
- }
505
- console.log("");
506
- console.log("Uninstalled. Restart Claude Code once. Memory still lives on the server until you delete it.");
507
- }
508
- async function main() {
509
- const args = parseArgs(process.argv.slice(2));
510
- try {
511
- switch (args.command) {
512
- case "install":
513
- await cmdInstall(args);
514
- break;
515
- case "doctor":
516
- await cmdDoctor();
517
- break;
518
- case "status":
519
- await cmdStatus();
520
- break;
521
- case "logs":
522
- await cmdLogs();
523
- break;
524
- case "uninstall":
525
- await cmdUninstall(args);
526
- break;
527
- case "-v":
528
- case "--version":
529
- console.log(VERSION);
530
- break;
531
- case "help":
532
- case "--help":
533
- case "-h":
534
- case void 0:
535
- case "":
536
- printHelp();
537
- break;
538
- default:
539
- console.error(`Unknown command: ${args.command}`);
540
- printHelp();
541
- process.exit(2);
542
- }
543
- } catch (e) {
544
- console.error("");
545
- console.error(`Error: ${e.message}`);
546
- process.exit(1);
547
- }
548
- }
549
- main();
36
+ `)}async function se(e){return e.scope?e.scope:$()?"user":(await w("Apply to (u)ser globally or this (p)roject?","u")).toLowerCase().startsWith("p")?"project":"user"}async function de(e){if(e.endpoint)return e.endpoint;let o=process.env.HM_ENDPOINT||process.env.MEMORYAI_ENDPOINT;return o||($()?U:await w("Endpoint",U))}async function pe(e,o){if(e.key)return e.key;let t=process.env.HM_API_KEY||process.env.MEMORYAI_API_KEY;if(t)return t;let n="";if($()||(n=(await w("API key (leave blank to auto-create a free one")).trim()),!n){process.stdout.write(" ... creating a free key for you ...");let r=await Z(o,"claude-code");if(r)return console.log(` ok (plan=${r.plan||"free"})`),r.api_key;throw console.log(" failed"),new Error("Could not create a key automatically. Get one from https://memoryai.dev/connect and rerun with --key.")}return n}function fe(e){try{let o=new URL(e);return`${o.origin}${o.pathname}`}catch{return e}}async function me(e){v();let o=await se(e),t=await de(e),n=await pe(e,t),r=(e.model||process.env.MEMORYAI_MODEL||"").trim()||void 0;if(process.stdout.write(" ... verifying key ..."),await Q(t,n))console.log(" ok");else if(console.log(" failed"),!e.yes&&!$()&&!await M("Server did not accept the key (or is offline). Continue anyway?",!1))throw new Error("Aborted by user.");let a=S(o),p=I(o),d=_(a),i=T(a);i&&console.log(` back ${i}`),(0,l.mkdirSync)(j,{recursive:!0});let c,u;try{if((0,l.existsSync)(m)){let y=JSON.parse((0,l.readFileSync)(m,"utf-8"));y&&typeof y.first_met_at=="string"&&(c=y.first_met_at),y&&typeof y.total_sessions=="number"&&(u=y.total_sessions)}}catch{}c||(c=new Date().toISOString()),typeof u!="number"&&(u=0),(0,l.writeFileSync)(m,JSON.stringify({endpoint:t,apiKey:n,...r?{model:r}:{},first_met_at:c,total_sessions:u},null,2)+`
37
+ `,"utf-8");try{require("node:fs").chmodSync(m,384)}catch{}let h=ge();if(!h)throw new Error("session-start-runner.js not found alongside the CLI. If running from source, run `npm run build` first.");(0,l.copyFileSync)(h,L),console.log(` setup ${L}`);let N=process.execPath,D=t.replace(/\/+$/,""),ce={SessionStart:P(d,"SessionStart",Y(N,L,12)),UserPromptSubmit:P(d,"UserPromptSubmit",H(`${D}/v1/hooks/claude/user-prompt`,n,10)),Stop:P(d,"Stop",H(`${D}/v1/hooks/claude/stop`,n,15))};for(let[y,F]of Object.entries(ce))console.log(` ${F?"add ":"skip "} hook ${y}${F?"":" (already present)"}`);O(a,d),console.log(` write ${a}`);let K=q(p);console.log(K==="created"?` create ${p}`:K==="appended"?` append ${p}`:` skip ${p} (note already present)`),console.log(""),console.log("Installed. Restart Claude Code once, then just work."),console.log(" - Past context returns at the start of each prompt."),console.log(" - Important moments save when each turn ends."),console.log(" - Run `memoryai-claude doctor` any time to verify health.")}function ge(){let e=[(0,g.join)((0,g.dirname)(process.argv[1]||""),"session-start-runner.js"),(0,g.join)(__dirname,"session-start-runner.js")];for(let o of e)if(o&&(0,l.existsSync)(o))return o;return null}async function ye(){v(),console.log("Diagnostics:");let e=S("user"),o=S("project"),t=!1;for(let[n,r]of[["user",e],["project",o]]){if(!(0,l.existsSync)(r)){console.log(` -- ${n}: ${r} (not present)`);continue}let s=_(r),a=C(s);if(Object.values(a).filter(c=>c.present).length===0){console.log(` -- ${n}: ${r} (no MemoryAI hooks)`);continue}t=!0,console.log(` ok ${n}: ${r}`);for(let[c,u]of Object.entries(a)){let h=u.url?` ${fe(u.url)}`:u.command?` command: ${u.command}`:"";console.log(` ${u.present?"present":"MISSING"} ${c}${h}`)}let d="",i="";if((0,l.existsSync)(m))try{let c=JSON.parse((0,l.readFileSync)(m,"utf-8"));c&&typeof c.endpoint=="string"&&(d=c.endpoint),c&&typeof c.apiKey=="string"&&(i=c.apiKey)}catch{}if(!d||!i){let c=ke(s);c?.url&&(d=new URL(c.url).origin,i=(c.headers?.Authorization||"").replace(/^Bearer\s+/i,""))}if(d&&i){process.stdout.write(" ... ping endpoints ...");let c=await ee(d,i);console.log("");for(let[u,h]of Object.entries(c))console.log(` ${h?"ok":"FAIL"} ${u}`)}}t||(console.log(""),console.log("No MemoryAI hooks found in either user or project settings."),console.log("Run `memoryai-claude install` to wire them up.")),ie()}function ke(e){let o=e?.hooks||{};for(let t of Object.keys(o))for(let n of o[t]||[])for(let r of n.hooks||[])if(typeof r?.url=="string"&&r.url.includes("/v1/hooks/claude/"))return r;return null}async function he(){let e=(0,g.join)(j,"runner.log");if(!(0,l.existsSync)(e)){v(),console.log(`No SessionStart hook activity yet (${e} does not exist).`),console.log("This means Claude Code has not fired SessionStart since install."),console.log("Open a fresh terminal and run `claude` to trigger it.");return}let n=(0,l.readFileSync)(e,"utf-8").trim().split(/\r?\n/).slice(-20).join(`
38
+ `);v(),console.log(`SessionStart runner log (${e}):`),console.log(""),console.log(n)}async function ve(){v();for(let e of["user","project"]){let o=S(e);if(!(0,l.existsSync)(o)){console.log(` ${e}: not present`);continue}let t=_(o),n=C(t),r=Object.values(n).filter(s=>s.present).length;console.log(` ${e}: ${r}/3 hooks wired (${o})`)}ie()}function ie(){if((0,l.existsSync)(m))try{let e=JSON.parse((0,l.readFileSync)(m,"utf-8")),o=typeof e.first_met_at=="string"?e.first_met_at:"",t=typeof e.total_sessions=="number"?e.total_sessions:0;if(!o&&t===0)return;let n=o?Date.parse(o):NaN,r="";if(!isNaN(n)){let a=Math.floor((Date.now()-n)/864e5);r=a===0?"today":a===1?"yesterday":`${a} days ago`}let s=t===1?"1 session":`${t} sessions`;console.log(r?` brain: met ${r} \xB7 ${s}`:` brain: ${s}`)}catch{}}async function be(e){v(),(0,l.existsSync)(m)||(console.error("MemoryAI not installed yet. Run `memoryai-claude install` first."),process.exit(2));let o=U,t="";try{let i=JSON.parse((0,l.readFileSync)(m,"utf-8"));typeof i.endpoint=="string"&&i.endpoint&&(o=i.endpoint),typeof i.key=="string"&&i.key&&(t=i.key)}catch(i){console.error(`Could not read runtime config: ${i.message}`),process.exit(2)}t||(console.error("No API key found. Run `memoryai-claude install` again."),process.exit(2));let n=(e.configSub||"").toLowerCase(),r=e.configValue;if(!n){let i=await oe(o,t);i||(console.error("Could not reach MemoryAI server. Check connectivity and key."),process.exit(2)),console.log(` plan: ${i.plan}`),console.log(` defaults: compact ${i.defaults.compact_at_tokens.toLocaleString()} / critical ${i.defaults.critical_at_tokens.toLocaleString()}`);let c=i.user_overrides.compact_at_tokens,u=i.user_overrides.critical_at_tokens,h=c!=null?c.toLocaleString():"(default)",N=u!=null?u.toLocaleString():"(default)";console.log(` your overrides: compact ${h} / critical ${N}`),console.log(` effective: compact ${i.effective.compact_at_tokens.toLocaleString()} / critical ${i.effective.critical_at_tokens.toLocaleString()}`),console.log(""),console.log(" Set with: memoryai-claude config compact <tokens>"),console.log(" memoryai-claude config critical <tokens>");return}if(n==="reset"){let i=await x(o,t,{compactAtTokens:null,criticalAtTokens:null});i.ok||(console.error(`Reset rejected: ${(i.errors||["unknown"]).join("; ")}`),process.exit(2));let c=i.settings.effective;console.log(` Reset both. Effective: compact ${c.compact_at_tokens.toLocaleString()} / critical ${c.critical_at_tokens.toLocaleString()} tokens (defaults).`);return}if(n!=="compact"&&n!=="critical"&&(console.error(`Unknown config sub-command: ${n}. Use 'compact', 'critical', or 'reset'.`),process.exit(2)),(r||"").toLowerCase()==="reset"){let c=await x(o,t,n==="compact"?{compactAtTokens:null}:{criticalAtTokens:null});c.ok||(console.error(`Reset rejected: ${(c.errors||["unknown"]).join("; ")}`),process.exit(2));let u=c.settings.effective;console.log(` Reset ${n}. Effective: compact ${u.compact_at_tokens.toLocaleString()} / critical ${u.critical_at_tokens.toLocaleString()} tokens.`);return}let s=Number(r);(!Number.isFinite(s)||s<1e3)&&(console.error(`Invalid value '${r??""}'. Must be an integer >= 1000, or 'reset'.`),process.exit(2));let a=n==="compact"?{compactAtTokens:Math.round(s)}:{criticalAtTokens:Math.round(s)},p=await x(o,t,a);p.ok||(console.error(`Update rejected: ${(p.errors||["unknown"]).join("; ")}`),process.exit(2));let d=p.settings.effective;console.log(` Updated. Effective: compact ${d.compact_at_tokens.toLocaleString()} / critical ${d.critical_at_tokens.toLocaleString()} tokens.`),console.log(" Applies across every IDE/host that uses this MemoryAI key.")}async function $e(e){v();let o=await se(e),t=S(o),n=I(o);if(!(0,l.existsSync)(t)){console.log(` Nothing to do \u2014 ${t} does not exist.`);return}if(!e.yes&&!$()&&!await M(`Remove MemoryAI hooks from ${t}?`,!0))return;let r=_(t),s=T(t);s&&console.log(` back ${s}`);let a=B(r);O(t,r),console.log(` removed ${a} hook${a===1?"":"s"} from ${t}`),X(n)&&console.log(` cleaned ${n}`),console.log(""),console.log("Uninstalled. Restart Claude Code once. Memory still lives on the server until you delete it.")}async function Se(){let e=ue(process.argv.slice(2));try{switch(e.command){case"install":await me(e);break;case"doctor":await ye();break;case"status":await ve();break;case"logs":await he();break;case"config":await be(e);break;case"uninstall":await $e(e);break;case"-v":case"--version":console.log(re);break;case"help":case"--help":case"-h":case void 0:case"":te();break;default:console.error(`Unknown command: ${e.command}`),te(),process.exit(2)}}catch(o){console.error(""),console.error(`Error: ${o.message}`),process.exit(1)}}Se();
@@ -1,112 +1,6 @@
1
1
  #!/usr/bin/env node
2
- "use strict";
2
+ "use strict";var r=require("node:fs"),d=require("node:path"),l=require("node:os"),f=process.env.MEMORYAI_CLAUDE_CONFIG||(0,d.join)((0,l.homedir)(),".memoryai","claude.json"),g=(0,d.join)((0,l.homedir)(),".memoryai","runner.log");function s(t){try{(0,r.mkdirSync)((0,d.dirname)(g),{recursive:!0}),(0,r.appendFileSync)(g,`${new Date().toISOString()} ${t}
3
+ `,"utf-8")}catch{}}function _(){if(!(0,r.existsSync)(f))return s("config-missing"),null;try{let t=JSON.parse((0,r.readFileSync)(f,"utf-8"));return t&&typeof t.endpoint=="string"&&typeof t.apiKey=="string"?t:(s("config-malformed"),null)}catch(t){return s(`config-parse-error: ${t.message}`),null}}function h(t){try{let n={...t,total_sessions:(t.total_sessions??0)+1,last_session_at:new Date().toISOString()};(0,r.writeFileSync)(f,JSON.stringify(n,null,2)+`
4
+ `,"utf-8")}catch(n){s(`session-counter-bump-failed: ${n.message}`)}}async function b(){return new Promise(t=>{let n="";if(process.stdin.isTTY){t({});return}process.stdin.setEncoding("utf-8"),process.stdin.on("data",e=>{n+=e}),process.stdin.on("end",()=>{try{t(n?JSON.parse(n):{})}catch{t({})}}),setTimeout(()=>t(n?S(n):{}),800)})}function S(t){try{return JSON.parse(t)}catch{return{}}}function $(t){let e={hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:t.slice(0,9500)}};process.stdout.write(JSON.stringify(e))}function O(t){if(!t||typeof t!="object")return"";let n=typeof t.brain_name=="string"?t.brain_name.trim():"",e=typeof t.brain_age_days=="number"?t.brain_age_days:null,a=typeof t.last_dream_at=="string"?t.last_dream_at:"";if(!n&&e===null&&!a)return"";let i="";if(a){let u=Date.parse(a);if(!isNaN(u)){let m=Date.now()-u,o=Math.round(m/36e5);o<1?i="dreamed within the hour":o<24?i=`dreamed ${o}h ago`:i=`dreamed ${Math.round(o/24)}d ago`}}let c=[];if(n&&c.push(`Brain "${n}"`),e!==null){let u=e===0?"born today":e===1?"1 day old":`${e} days old`;c.push(u)}return i&&c.push(i),c.length===0?"":`[Memory] ${c.join(" \xB7 ")}
3
5
 
4
- // src/session-start-runner.ts
5
- var import_node_fs = require("node:fs");
6
- var import_node_path = require("node:path");
7
- var import_node_os = require("node:os");
8
- var CONFIG_PATH = process.env.MEMORYAI_CLAUDE_CONFIG || (0, import_node_path.join)((0, import_node_os.homedir)(), ".memoryai", "claude.json");
9
- var LOG_PATH = (0, import_node_path.join)((0, import_node_os.homedir)(), ".memoryai", "runner.log");
10
- function log(line) {
11
- try {
12
- (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(LOG_PATH), { recursive: true });
13
- (0, import_node_fs.appendFileSync)(LOG_PATH, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
14
- `, "utf-8");
15
- } catch {
16
- }
17
- }
18
- function loadConfig() {
19
- if (!(0, import_node_fs.existsSync)(CONFIG_PATH)) {
20
- log("config-missing");
21
- return null;
22
- }
23
- try {
24
- const raw = JSON.parse((0, import_node_fs.readFileSync)(CONFIG_PATH, "utf-8"));
25
- if (raw && typeof raw.endpoint === "string" && typeof raw.apiKey === "string") {
26
- return raw;
27
- }
28
- log("config-malformed");
29
- return null;
30
- } catch (e) {
31
- log(`config-parse-error: ${e.message}`);
32
- return null;
33
- }
34
- }
35
- async function readStdinJson() {
36
- return new Promise((resolve) => {
37
- let data = "";
38
- if (process.stdin.isTTY) {
39
- resolve({});
40
- return;
41
- }
42
- process.stdin.setEncoding("utf-8");
43
- process.stdin.on("data", (chunk) => {
44
- data += chunk;
45
- });
46
- process.stdin.on("end", () => {
47
- try {
48
- resolve(data ? JSON.parse(data) : {});
49
- } catch {
50
- resolve({});
51
- }
52
- });
53
- setTimeout(() => resolve(data ? safeParse(data) : {}), 800);
54
- });
55
- }
56
- function safeParse(s) {
57
- try {
58
- return JSON.parse(s);
59
- } catch {
60
- return {};
61
- }
62
- }
63
- function emitContext(block) {
64
- const trimmed = block.slice(0, 9500);
65
- const out = {
66
- hookSpecificOutput: {
67
- hookEventName: "SessionStart",
68
- additionalContext: trimmed
69
- }
70
- };
71
- process.stdout.write(JSON.stringify(out));
72
- }
73
- async function main() {
74
- const input = await readStdinJson();
75
- const source = input?.source || "unknown";
76
- log(`fired source=${source}`);
77
- const cfg = loadConfig();
78
- if (!cfg) {
79
- process.exit(0);
80
- }
81
- try {
82
- const base = cfg.endpoint.replace(/\/+$/, "");
83
- const ctrl = new AbortController();
84
- const timer = setTimeout(() => ctrl.abort(), 1e4);
85
- const resp = await fetch(`${base}/v1/ide/guard/bootstrap`, {
86
- method: "POST",
87
- headers: {
88
- "Content-Type": "application/json",
89
- Authorization: `Bearer ${cfg.apiKey}`
90
- },
91
- body: JSON.stringify({ task: "", limit: 14 }),
92
- signal: ctrl.signal
93
- });
94
- clearTimeout(timer);
95
- if (!resp.ok) {
96
- log(`bootstrap-http-${resp.status}`);
97
- process.exit(0);
98
- }
99
- const data = await resp.json();
100
- const block = data && typeof data.context_block === "string" ? data.context_block.trim() : "";
101
- if (!block) {
102
- log("bootstrap-empty");
103
- process.exit(0);
104
- }
105
- emitContext(block);
106
- log(`bootstrap-ok memories=${data.memories_restored ?? "?"} tokens=${data.tokens_used ?? "?"}`);
107
- } catch (e) {
108
- log(`exception: ${e.message}`);
109
- }
110
- process.exit(0);
111
- }
112
- main();
6
+ `}async function w(){let n=(await b())?.source||"unknown";s(`fired source=${n}`);let e=_();e||process.exit(0),h(e);try{let a=e.endpoint.replace(/\/+$/,""),i=new AbortController,c=setTimeout(()=>i.abort(),2e3),u=n==="compact"?4:14,m=await fetch(`${a}/v1/ide/guard/bootstrap`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e.apiKey}`},body:JSON.stringify({task:"",limit:u,source:n,model:e.model||null}),signal:i.signal});clearTimeout(c),m.ok||(s(`bootstrap-http-${m.status}`),process.exit(0));let o=await m.json(),p=o&&typeof o.context_block=="string"?o.context_block.trim():"";p||(s("bootstrap-empty"),process.exit(0));let y=O(o);$(y+p),s(`bootstrap-ok memories=${o.memories_restored??"?"} tokens=${o.tokens_used??"?"} brain=${o.brain_name??"-"}`)}catch(a){s(`exception: ${a.message}`)}process.exit(0)}w();
package/package.json CHANGED
@@ -1,44 +1,44 @@
1
- {
2
- "name": "memoryai-claude",
3
- "version": "0.1.2",
4
- "description": "Your AI keeps forgetting you. MemoryAI gives Claude Code a real long-term memory — one that follows you across every model.",
5
- "license": "MIT",
6
- "homepage": "https://memoryai.dev",
7
- "repository": {
8
- "type": "git",
9
- "url": "https://github.com/memoryai-dev/memoryai-claude"
10
- },
11
- "keywords": [
12
- "claude-code",
13
- "memory",
14
- "context",
15
- "llm",
16
- "ai"
17
- ],
18
- "engines": {
19
- "node": ">=18"
20
- },
21
- "bin": {
22
- "memoryai-claude": "dist/cli.js"
23
- },
24
- "files": [
25
- "dist/",
26
- "README.md",
27
- "LICENSE"
28
- ],
29
- "scripts": {
30
- "vscode:prepublish": "npm run package",
31
- "build:cli": "esbuild ./src/cli.ts --bundle --outfile=dist/cli.js --platform=node --target=node18 --format=cjs --banner:js=\"#!/usr/bin/env node\"",
32
- "build:runner": "esbuild ./src/session-start-runner.ts --bundle --outfile=dist/session-start-runner.js --platform=node --target=node18 --format=cjs --banner:js=\"#!/usr/bin/env node\"",
33
- "build": "npm run build:cli && npm run build:runner",
34
- "package:cli": "npm run build:cli -- --minify",
35
- "package:runner": "npm run build:runner -- --minify",
36
- "package": "npm run package:cli && npm run package:runner",
37
- "lint": "tsc --noEmit"
38
- },
39
- "devDependencies": {
40
- "@types/node": "^22.0.0",
41
- "esbuild": "^0.25.12",
42
- "typescript": "^5.5.0"
43
- }
44
- }
1
+ {
2
+ "name": "memoryai-claude",
3
+ "version": "0.1.4",
4
+ "description": "Your AI keeps forgetting you. MemoryAI gives Claude Code a real long-term memory — one that follows you across every model.",
5
+ "license": "MIT",
6
+ "homepage": "https://memoryai.dev",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/memoryai-dev/memoryai-claude"
10
+ },
11
+ "keywords": [
12
+ "claude-code",
13
+ "memory",
14
+ "context",
15
+ "llm",
16
+ "ai"
17
+ ],
18
+ "engines": {
19
+ "node": ">=18"
20
+ },
21
+ "bin": {
22
+ "memoryai-claude": "dist/cli.js"
23
+ },
24
+ "files": [
25
+ "dist/",
26
+ "README.md",
27
+ "LICENSE"
28
+ ],
29
+ "scripts": {
30
+ "vscode:prepublish": "npm run package",
31
+ "build:cli": "esbuild ./src/cli.ts --bundle --outfile=dist/cli.js --platform=node --target=node18 --format=cjs --banner:js=\"#!/usr/bin/env node\"",
32
+ "build:runner": "esbuild ./src/session-start-runner.ts --bundle --outfile=dist/session-start-runner.js --platform=node --target=node18 --format=cjs --banner:js=\"#!/usr/bin/env node\"",
33
+ "build": "npm run build:cli && npm run build:runner",
34
+ "package:cli": "npm run build:cli -- --minify",
35
+ "package:runner": "npm run build:runner -- --minify",
36
+ "package": "npm run package:cli && npm run package:runner",
37
+ "lint": "tsc --noEmit"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^22.0.0",
41
+ "esbuild": "^0.25.12",
42
+ "typescript": "^5.5.0"
43
+ }
44
+ }