memoryai-claude 0.1.2 → 0.1.3

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,255 +1,15 @@
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 a=require("node:fs"),f=require("node:path"),ee=require("node:os");var F=require("node:readline/promises"),A=require("node:process");async function w(e,o){let t=(0,F.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 j(e,o=!0){return(await w(`${e} (${o?"Y/n":"y/N"})`,o?"y":"n")).toLowerCase().startsWith("y")}function b(){return process.env.MEMORYAI_NONINTERACTIVE==="1"||process.env.CI==="true"||!process.stdin.isTTY}var l=require("node:fs"),k=require("node:path"),M=require("node:os");function $(e){return e==="project"?(0,k.join)(process.cwd(),".claude","settings.json"):(0,k.join)((0,M.homedir)(),".claude","settings.json")}function R(e){return e==="project"?(0,k.join)(process.cwd(),"CLAUDE.md"):(0,k.join)((0,M.homedir)(),".claude","CLAUDE.md")}function P(e){if(!(0,l.existsSync)(e))return{};try{return JSON.parse((0,l.readFileSync)(e,"utf-8"))||{}}catch{throw new Error(`${e} is not valid JSON. Fix it manually before re-running.`)}}function K(e,o){try{(0,l.chmodSync)(e,o)}catch{}}function x(e,o){(0,l.mkdirSync)((0,k.dirname)(e),{recursive:!0}),(0,l.writeFileSync)(e,JSON.stringify(o,null,2)+`
3
+ `,"utf-8"),K(e,384)}function I(e){if(!(0,l.existsSync)(e))return null;let o=new Date().toISOString().replace(/[:.]/g,"-"),t=`${e}.bak-${o}`;return(0,l.copyFileSync)(e,t),K(t,384),t}var se="/v1/hooks/claude/",ie="memoryai/session-start-runner";function O(e,o,t){return{type:"http",url:e,timeout:t,headers:{Authorization:`Bearer ${o}`},allowedEnvVars:[]}}function J(e,o,t){return{type:"command",command:e,args:[o],timeout:t}}function L(e){if(!e)return!1;if(typeof e.url=="string"&&e.url.includes(se))return!0;if(typeof e.command=="string"){let o=Array.isArray(e.args)?e.args.join(" "):"",t=`${e.command} ${o}`;if(t.includes(ie)||t.includes("session-start-runner"))return!0}return!1}function Y(e){return(e&&e.hooks||[]).some(L)}function N(e,o,t){return e.hooks=e.hooks||{},e.hooks[o]=e.hooks[o]||[],e.hooks[o].some(Y)?!1:(e.hooks[o].push({hooks:[t]}),!0)}function z(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=>!Y(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 H(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(L);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 y=require("node:fs"),E="<!-- memoryai:auto-note -->",B=`
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:
10
+ `;function G(e){let o=(0,y.existsSync)(e)?(0,y.readFileSync)(e,"utf-8"):"";if(o.includes(E))return"skipped";let t=o?`${o.replace(/\s*$/,"")}
11
+ ${B}`:B;return(0,y.writeFileSync)(e,t,"utf-8"),o?"appended":"created"}function V(e){if(!(0,y.existsSync)(e))return!1;let o=(0,y.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,y.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 W(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 X(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:c,path:g,body:p})=>{try{let u=await fetch(`${t}${g}`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`,"X-MemoryAI-Probe":"1"},body:JSON.stringify(p)});return[c,u.ok]}catch{return[c,!1]}},s=await Promise.all(n.map(r));return Object.fromEntries(s)}var oe=(()=>{try{let e=require((0,f.join)(__dirname,"..","package.json"));if(e&&typeof e.version=="string")return e.version}catch{}return"0.0.0-dev"})(),Q="https://memoryai.dev",_=(0,f.join)((0,ee.homedir)(),".memoryai"),h=(0,f.join)(_,"claude.json"),C=(0,f.join)(_,"session-start-runner.js");function ae(e){let o={command:e[0]||"help"};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==="--yes"||n==="-y")&&(o.yes=!0)}return o}function v(){console.log(`MemoryAI for Claude Code v${oe}`),console.log("One brain. Every AI you use. Forever."),console.log("")}function Z(){v(),console.log(`Usage:
253
13
  memoryai-claude install [--user|--project] [--endpoint URL] [--key KEY] [--yes]
254
14
  memoryai-claude doctor
255
15
  memoryai-claude status
@@ -263,287 +23,6 @@ nothing to call by hand.
263
23
 
264
24
  Get a free key at https://memoryai.dev (or leave it blank during install and a
265
25
  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();
26
+ `)}async function te(e){return e.scope?e.scope:b()?"user":(await w("Apply to (u)ser globally or this (p)roject?","u")).toLowerCase().startsWith("p")?"project":"user"}async function ce(e){if(e.endpoint)return e.endpoint;let o=process.env.HM_ENDPOINT||process.env.MEMORYAI_ENDPOINT;return o||(b()?Q:await w("Endpoint",Q))}async function le(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(b()||(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 W(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 ue(e){try{let o=new URL(e);return`${o.origin}${o.pathname}`}catch{return e}}async function pe(e){v();let o=await te(e),t=await ce(e),n=await le(e,t);if(process.stdout.write(" ... verifying key ..."),await q(t,n))console.log(" ok");else if(console.log(" failed"),!e.yes&&!b()&&!await j("Server did not accept the key (or is offline). Continue anyway?",!1))throw new Error("Aborted by user.");let s=$(o),c=R(o),g=P(s),p=I(s);p&&console.log(` back ${p}`),(0,a.mkdirSync)(_,{recursive:!0});let u,i;try{if((0,a.existsSync)(h)){let m=JSON.parse((0,a.readFileSync)(h,"utf-8"));m&&typeof m.first_met_at=="string"&&(u=m.first_met_at),m&&typeof m.total_sessions=="number"&&(i=m.total_sessions)}}catch{}u||(u=new Date().toISOString()),typeof i!="number"&&(i=0),(0,a.writeFileSync)(h,JSON.stringify({endpoint:t,apiKey:n,first_met_at:u,total_sessions:i},null,2)+`
27
+ `,"utf-8");try{require("node:fs").chmodSync(h,384)}catch{}let d=de();if(!d)throw new Error("session-start-runner.js not found alongside the CLI. If running from source, run `npm run build` first.");(0,a.copyFileSync)(d,C),console.log(` setup ${C}`);let S=process.execPath,T=t.replace(/\/+$/,""),re={SessionStart:N(g,"SessionStart",J(S,C,12)),UserPromptSubmit:N(g,"UserPromptSubmit",O(`${T}/v1/hooks/claude/user-prompt`,n,10)),Stop:N(g,"Stop",O(`${T}/v1/hooks/claude/stop`,n,15))};for(let[m,D]of Object.entries(re))console.log(` ${D?"add ":"skip "} hook ${m}${D?"":" (already present)"}`);x(s,g),console.log(` write ${s}`);let U=G(c);console.log(U==="created"?` create ${c}`:U==="appended"?` append ${c}`:` skip ${c} (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 de(){let e=[(0,f.join)((0,f.dirname)(process.argv[1]||""),"session-start-runner.js"),(0,f.join)(__dirname,"session-start-runner.js")];for(let o of e)if(o&&(0,a.existsSync)(o))return o;return null}async function fe(){v(),console.log("Diagnostics:");let e=$("user"),o=$("project"),t=!1;for(let[n,r]of[["user",e],["project",o]]){if(!(0,a.existsSync)(r)){console.log(` -- ${n}: ${r} (not present)`);continue}let s=P(r),c=H(s);if(Object.values(c).filter(i=>i.present).length===0){console.log(` -- ${n}: ${r} (no MemoryAI hooks)`);continue}t=!0,console.log(` ok ${n}: ${r}`);for(let[i,d]of Object.entries(c)){let S=d.url?` ${ue(d.url)}`:d.command?` command: ${d.command}`:"";console.log(` ${d.present?"present":"MISSING"} ${i}${S}`)}let p="",u="";if((0,a.existsSync)(h))try{let i=JSON.parse((0,a.readFileSync)(h,"utf-8"));i&&typeof i.endpoint=="string"&&(p=i.endpoint),i&&typeof i.apiKey=="string"&&(u=i.apiKey)}catch{}if(!p||!u){let i=me(s);i?.url&&(p=new URL(i.url).origin,u=(i.headers?.Authorization||"").replace(/^Bearer\s+/i,""))}if(p&&u){process.stdout.write(" ... ping endpoints ...");let i=await X(p,u);console.log("");for(let[d,S]of Object.entries(i))console.log(` ${S?"ok":"FAIL"} ${d}`)}}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.")),ne()}function me(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 ye(){let e=(0,f.join)(_,"runner.log");if(!(0,a.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,a.readFileSync)(e,"utf-8").trim().split(/\r?\n/).slice(-20).join(`
28
+ `);v(),console.log(`SessionStart runner log (${e}):`),console.log(""),console.log(n)}async function ge(){v();for(let e of["user","project"]){let o=$(e);if(!(0,a.existsSync)(o)){console.log(` ${e}: not present`);continue}let t=P(o),n=H(t),r=Object.values(n).filter(s=>s.present).length;console.log(` ${e}: ${r}/3 hooks wired (${o})`)}ne()}function ne(){if((0,a.existsSync)(h))try{let e=JSON.parse((0,a.readFileSync)(h,"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 c=Math.floor((Date.now()-n)/864e5);r=c===0?"today":c===1?"yesterday":`${c} days ago`}let s=t===1?"1 session":`${t} sessions`;console.log(r?` brain: met ${r} \xB7 ${s}`:` brain: ${s}`)}catch{}}async function he(e){v();let o=await te(e),t=$(o),n=R(o);if(!(0,a.existsSync)(t)){console.log(` Nothing to do \u2014 ${t} does not exist.`);return}if(!e.yes&&!b()&&!await j(`Remove MemoryAI hooks from ${t}?`,!0))return;let r=P(t),s=I(t);s&&console.log(` back ${s}`);let c=z(r);x(t,r),console.log(` removed ${c} hook${c===1?"":"s"} from ${t}`),V(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 ke(){let e=ae(process.argv.slice(2));try{switch(e.command){case"install":await pe(e);break;case"doctor":await fe();break;case"status":await ge();break;case"logs":await ye();break;case"uninstall":await he(e);break;case"-v":case"--version":console.log(oe);break;case"help":case"--help":case"-h":case void 0:case"":Z();break;default:console.error(`Unknown command: ${e.command}`),Z(),process.exit(2)}}catch(o){console.error(""),console.error(`Error: ${o.message}`),process.exit(1)}}ke();
@@ -1,112 +1,6 @@
1
1
  #!/usr/bin/env node
2
- "use strict";
2
+ "use strict";var r=require("node:fs"),f=require("node:path"),d=require("node:os"),p=process.env.MEMORYAI_CLAUDE_CONFIG||(0,f.join)((0,d.homedir)(),".memoryai","claude.json"),g=(0,f.join)((0,d.homedir)(),".memoryai","runner.log");function s(t){try{(0,r.mkdirSync)((0,f.dirname)(g),{recursive:!0}),(0,r.appendFileSync)(g,`${new Date().toISOString()} ${t}
3
+ `,"utf-8")}catch{}}function _(){if(!(0,r.existsSync)(p))return s("config-missing"),null;try{let t=JSON.parse((0,r.readFileSync)(p,"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)(p,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}),signal:i.signal});clearTimeout(c),m.ok||(s(`bootstrap-http-${m.status}`),process.exit(0));let o=await m.json(),l=o&&typeof o.context_block=="string"?o.context_block.trim():"";l||(s("bootstrap-empty"),process.exit(0));let y=O(o);$(y+l),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.3",
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
+ }