memoryai-claude 0.1.1 → 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/dist/cli.js +9 -9
- package/dist/session-start-runner.js +5 -2
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var a=require("node:fs"),
|
|
3
|
-
`,"utf-8")}function
|
|
4
|
-
${
|
|
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}
|
|
5
5
|
## MemoryAI
|
|
6
6
|
|
|
7
7
|
Memory works automatically here. Past decisions, preferences, and recent project
|
|
8
8
|
context are recalled before each prompt and saved when each turn ends. Nothing
|
|
9
9
|
to call by hand \u2014 just work normally.
|
|
10
|
-
`;function
|
|
11
|
-
${
|
|
12
|
-
`);return(0,
|
|
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:
|
|
13
13
|
memoryai-claude install [--user|--project] [--endpoint URL] [--key KEY] [--yes]
|
|
14
14
|
memoryai-claude doctor
|
|
15
15
|
memoryai-claude status
|
|
@@ -23,6 +23,6 @@ nothing to call by hand.
|
|
|
23
23
|
|
|
24
24
|
Get a free key at https://memoryai.dev (or leave it blank during install and a
|
|
25
25
|
key will be created for you).
|
|
26
|
-
`)}async function
|
|
27
|
-
`,"utf-8");try{require("node:fs").chmodSync(
|
|
28
|
-
`);
|
|
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,3 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var
|
|
3
|
-
`,"utf-8")}catch{}}function
|
|
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 ")}
|
|
5
|
+
|
|
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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memoryai-claude",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
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
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://memoryai.dev",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^22.0.0",
|
|
41
|
-
"esbuild": "^0.25.
|
|
41
|
+
"esbuild": "^0.25.12",
|
|
42
42
|
"typescript": "^5.5.0"
|
|
43
43
|
}
|
|
44
44
|
}
|