mirror of
https://github.com/thedotmack/claude-mem
synced 2026-04-25 17:15:04 +02:00
- Replaced instances of silentDebug with happy_path_error__with_fallback across multiple files to improve error logging and handling. - Updated the utility function to provide clearer semantics for error handling when expected values are missing. - Introduced a script to find potential silent failures in the codebase that may need to be addressed with the new error handling approach.
13 lines
7.6 KiB
JavaScript
Executable File
13 lines
7.6 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
import et from"path";import{stdin as d}from"process";import l from"path";import{existsSync as C}from"fs";import{homedir as I}from"os";import{spawnSync as U}from"child_process";import{readFileSync as b,existsSync as H}from"fs";import{join as F}from"path";import{homedir as j}from"os";var v=["bugfix","feature","refactor","discovery","decision","change"],W=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var M=v.join(","),R=W.join(",");var f=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_DATA_DIR:F(j(),".claude-mem"),CLAUDE_MEM_LOG_LEVEL:"INFO",CLAUDE_MEM_PYTHON_VERSION:"3.13",CLAUDE_CODE_PATH:"",CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT:"true",CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES:M,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:R,CLAUDE_MEM_CONTEXT_FULL_COUNT:"5",CLAUDE_MEM_CONTEXT_FULL_FIELD:"narrative",CLAUDE_MEM_CONTEXT_SESSION_COUNT:"10",CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY:"true",CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE:"false"};static getAllDefaults(){return{...this.DEFAULTS}}static get(t){return process.env[t]||this.DEFAULTS[t]}static getInt(t){let e=this.get(t);return parseInt(e,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){if(!H(t))return this.getAllDefaults();let e=b(t,"utf-8"),n=JSON.parse(e).env||{},s={...this.DEFAULTS};for(let c of Object.keys(this.DEFAULTS))n[c]!==void 0&&(s[c]=n[c]);return s}};import{join as G}from"path";import{homedir as Y}from"os";import{existsSync as J,readFileSync as q}from"fs";import{appendFileSync as K}from"fs";import{homedir as V}from"os";import{join as X}from"path";var B=X(V(),".claude-mem","silent.log");function D(o,t,e=""){let r=new Date().toISOString(),E=((new Error().stack||"").split(`
|
|
`)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),u=E?`${E[1].split("/").pop()}:${E[2]}`:"unknown",a=`[${r}] [HAPPY-PATH-ERROR] [${u}] ${o}`;if(t!==void 0)try{a+=` ${JSON.stringify(t)}`}catch(_){a+=` [stringify error: ${_}]`}a+=`
|
|
`;try{K(B,a)}catch(_){console.error("[silent-debug] Failed to write to log:",_)}return e}var g=G(Y(),".claude-mem","settings.json");function y(o,t){try{if(J(g)){let r=JSON.parse(q(g,"utf-8")).env?.[o];if(r!==void 0)return r}}catch(e){D("Failed to load settings file",{error:e,settingsPath:g,key:o})}return process.env[o]||t}var S=(s=>(s[s.DEBUG=0]="DEBUG",s[s.INFO=1]="INFO",s[s.WARN=2]="WARN",s[s.ERROR=3]="ERROR",s[s.SILENT=4]="SILENT",s))(S||{}),O=class{level;useColor;constructor(){let t=y("CLAUDE_MEM_LOG_LEVEL","INFO").toUpperCase();this.level=S[t]??1,this.useColor=process.stdout.isTTY??!1}correlationId(t,e){return`obs-${t}-${e}`}sessionId(t){return`session-${t}`}formatData(t){if(t==null)return"";if(typeof t=="string")return t;if(typeof t=="number"||typeof t=="boolean")return t.toString();if(typeof t=="object"){if(t instanceof Error)return this.level===0?`${t.message}
|
|
${t.stack}`:t.message;if(Array.isArray(t))return`[${t.length} items]`;let e=Object.keys(t);return e.length===0?"{}":e.length<=3?JSON.stringify(t):`{${e.length} keys: ${e.slice(0,3).join(", ")}...}`}return String(t)}formatTool(t,e){if(!e)return t;try{let r=typeof e=="string"?JSON.parse(e):e;if(t==="Bash"&&r.command){let n=r.command.length>50?r.command.substring(0,50)+"...":r.command;return`${t}(${n})`}if(t==="Read"&&r.file_path){let n=r.file_path.split("/").pop()||r.file_path;return`${t}(${n})`}if(t==="Edit"&&r.file_path){let n=r.file_path.split("/").pop()||r.file_path;return`${t}(${n})`}if(t==="Write"&&r.file_path){let n=r.file_path.split("/").pop()||r.file_path;return`${t}(${n})`}return t}catch{return t}}log(t,e,r,n,s){if(t<this.level)return;let c=new Date().toISOString().replace("T"," ").substring(0,23),E=S[t].padEnd(5),u=e.padEnd(6),a="";n?.correlationId?a=`[${n.correlationId}] `:n?.sessionId&&(a=`[session-${n.sessionId}] `);let _="";s!=null&&(this.level===0&&typeof s=="object"?_=`
|
|
`+JSON.stringify(s,null,2):_=" "+this.formatData(s));let A="";if(n){let{sessionId:nt,sdkSessionId:ot,correlationId:st,...h}=n;Object.keys(h).length>0&&(A=` {${Object.entries(h).map(([k,x])=>`${k}=${x}`).join(", ")}}`)}let L=`[${c}] [${E}] [${u}] ${a}${r}${A}${_}`;t===3?console.error(L):console.log(L)}debug(t,e,r,n){this.log(0,t,e,r,n)}info(t,e,r,n){this.log(1,t,e,r,n)}warn(t,e,r,n){this.log(2,t,e,r,n)}error(t,e,r,n){this.log(3,t,e,r,n)}dataIn(t,e,r,n){this.info(t,`\u2192 ${e}`,r,n)}dataOut(t,e,r,n){this.info(t,`\u2190 ${e}`,r,n)}success(t,e,r,n){this.info(t,`\u2713 ${e}`,r,n)}failure(t,e,r,n){this.error(t,`\u2717 ${e}`,r,n)}timing(t,e,r,n){this.info(t,`\u23F1 ${e}`,n,{duration:`${r}ms`})}},m=new O;var p={DEFAULT:5e3,HEALTH_CHECK:1e3,WORKER_STARTUP_WAIT:1e3,WORKER_STARTUP_RETRIES:15,WINDOWS_MULTIPLIER:1.5};function N(o){return process.platform==="win32"?Math.round(o*p.WINDOWS_MULTIPLIER):o}var i=l.join(I(),".claude","plugins","marketplaces","thedotmack"),z=N(p.HEALTH_CHECK),Q=p.WORKER_STARTUP_WAIT,Z=p.WORKER_STARTUP_RETRIES;function T(){let o=l.join(I(),".claude-mem","settings.json"),t=f.loadFromFile(o);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function w(){try{let o=T();return(await fetch(`http://127.0.0.1:${o}/health`,{signal:AbortSignal.timeout(z)})).ok}catch(o){return m.debug("SYSTEM","Worker health check failed",{error:o instanceof Error?o.message:String(o),errorType:o?.constructor?.name}),!1}}async function tt(){try{let o=l.join(i,"plugin","scripts","worker-service.cjs");if(!C(o))throw new Error(`Worker script not found at ${o}`);if(process.platform==="win32"){let t=U("powershell.exe",["-NoProfile","-NonInteractive","-Command",`Start-Process -FilePath 'node' -ArgumentList '${o}' -WorkingDirectory '${i}' -WindowStyle Hidden`],{cwd:i,stdio:"pipe",encoding:"utf-8",windowsHide:!0});if(t.status!==0)throw new Error(t.stderr||"PowerShell Start-Process failed")}else{let t=l.join(i,"ecosystem.config.cjs");if(!C(t))throw new Error(`Ecosystem config not found at ${t}`);let e=l.join(i,"node_modules",".bin","pm2"),r=C(e)?e:"pm2",n=U(r,["start",t],{cwd:i,stdio:"pipe",encoding:"utf-8"});if(n.status!==0)throw new Error(n.stderr||"PM2 start failed")}for(let t=0;t<Z;t++)if(await new Promise(e=>setTimeout(e,Q)),await w())return!0;return!1}catch(o){return m.error("SYSTEM","Failed to start worker",{platform:process.platform,workerScript:l.join(i,"plugin","scripts","worker-service.cjs"),error:o instanceof Error?o.message:String(o),marketplaceRoot:i}),!1}}async function P(){if(await w())return;if(!await tt()){let t=T();throw new Error(`Worker service failed to start on port ${t}.
|
|
|
|
To start manually, run:
|
|
cd ${i}
|
|
npx pm2 start ecosystem.config.cjs
|
|
|
|
If already running, try: npx pm2 restart claude-mem-worker`)}}async function $(o){await P();let t=o?.cwd??process.cwd(),e=t?et.basename(t):"unknown-project",n=`http://127.0.0.1:${T()}/api/context/inject?project=${encodeURIComponent(e)}`;try{let s=await fetch(n,{signal:AbortSignal.timeout(p.DEFAULT)});if(!s.ok){let E=await s.text();throw new Error(`Failed to fetch context: ${s.status} ${E}`)}return(await s.text()).trim()}catch(s){throw s.cause?.code==="ECONNREFUSED"||s.name==="TimeoutError"||s.message.includes("fetch failed")?new Error("There's a problem with the worker. If you just updated, type `pm2 restart claude-mem-worker` in your terminal to continue"):s}}var rt=process.argv.includes("--colors");if(d.isTTY||rt)$(void 0).then(o=>{console.log(o),process.exit(0)});else{let o="";d.on("data",t=>o+=t),d.on("end",async()=>{let t=o.trim()?JSON.parse(o):void 0,e=await $(t);console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:e}})),process.exit(0)})}
|