Enhance error handling and logging in early-settings and worker-utils

- Added silent debugging for settings file loading failures in early-settings.ts.
- Improved error logging in worker-utils.ts for health check and worker startup failures, including detailed error information and context.
This commit is contained in:
Alex Newman
2025-12-09 14:04:32 -05:00
parent a2f7a4dc5a
commit 7cab32151e
11 changed files with 193 additions and 162 deletions
+8 -6
View File
@@ -1,11 +1,13 @@
#!/usr/bin/env node #!/usr/bin/env node
import{stdin as p}from"process";import a from"path";import{existsSync as u}from"fs";import{homedir as A}from"os";import{spawnSync as g}from"child_process";import{readFileSync as U,existsSync as h}from"fs";import{join as R}from"path";import{homedir as y}from"os";var N=["bugfix","feature","refactor","discovery","decision","change"],D=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var d=N.join(","),f=D.join(",");var T=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_DATA_DIR:R(y(),".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:d,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:f,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 r=this.get(t);return parseInt(r,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){if(!h(t))return this.getAllDefaults();let r=U(t,"utf-8"),o=JSON.parse(r).env||{},i={...this.DEFAULTS};for(let _ of Object.keys(this.DEFAULTS))o[_]!==void 0&&(i[_]=o[_]);return i}};var n=a.join(A(),".claude","plugins","marketplaces","thedotmack"),I=500,w=1e3,P=15;function l(){let e=a.join(A(),".claude-mem","settings.json"),t=T.loadFromFile(e);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function C(){try{let e=l();return(await fetch(`http://127.0.0.1:${e}/health`,{signal:AbortSignal.timeout(I)})).ok}catch{return!1}}async function k(){try{let e=a.join(n,"plugin","scripts","worker-service.cjs");if(!u(e))throw new Error(`Worker script not found at ${e}`);if(process.platform==="win32"){let t=g("powershell.exe",["-NoProfile","-NonInteractive","-Command",`Start-Process -FilePath 'node' -ArgumentList '${e}' -WorkingDirectory '${n}' -WindowStyle Hidden`],{cwd:n,stdio:"pipe",encoding:"utf-8",windowsHide:!0});if(t.status!==0)throw new Error(t.stderr||"PowerShell Start-Process failed")}else{let t=a.join(n,"ecosystem.config.cjs");if(!u(t))throw new Error(`Ecosystem config not found at ${t}`);let r=a.join(n,"node_modules",".bin","pm2"),s=u(r)?r:"pm2",o=g(s,["start",t],{cwd:n,stdio:"pipe",encoding:"utf-8"});if(o.status!==0)throw new Error(o.stderr||"PM2 start failed")}for(let t=0;t<P;t++)if(await new Promise(r=>setTimeout(r,w)),await C())return!0;return!1}catch{return!1}}async function M(){if(await C())return;if(!await k()){let t=l();throw new Error(`Worker service failed to start on port ${t}. import{stdin as C}from"process";import l from"path";import{existsSync as m}from"fs";import{homedir as R}from"os";import{spawnSync as N}from"child_process";import{readFileSync as b,existsSync as x}from"fs";import{join as W}from"path";import{homedir as F}from"os";var P=["bugfix","feature","refactor","discovery","decision","change"],v=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var h=P.join(","),D=v.join(",");var g=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_DATA_DIR:W(F(),".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:h,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:D,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(!x(t))return this.getAllDefaults();let e=b(t,"utf-8"),r=JSON.parse(e).env||{},s={...this.DEFAULTS};for(let _ of Object.keys(this.DEFAULTS))r[_]!==void 0&&(s[_]=r[_]);return s}};import{join as B}from"path";import{homedir as G}from"os";import{existsSync as Y,readFileSync as K}from"fs";import{appendFileSync as H}from"fs";import{homedir as j}from"os";import{join as V}from"path";var X=V(j(),".claude-mem","silent.log");function E(o,t,e=""){let n=new Date().toISOString(),p=((new Error().stack||"").split(`
`)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),u=p?`${p[1].split("/").pop()}:${p[2]}`:"unknown",a=`[${n}] [${u}] ${o}`;if(t!==void 0)try{a+=` ${JSON.stringify(t)}`}catch(c){a+=` [stringify error: ${c}]`}a+=`
`;try{H(X,a)}catch(c){console.error("[silent-debug] Failed to write to log:",c)}return e}var S=B(G(),".claude-mem","settings.json");function y(o,t){try{if(Y(S)){let n=JSON.parse(K(S,"utf-8")).env?.[o];if(n!==void 0)return n}}catch(e){E("Failed to load settings file",{error:e,settingsPath:S,key:o})}return process.env[o]||t}var T=(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))(T||{}),O=class{level;useColor;constructor(){let t=y("CLAUDE_MEM_LOG_LEVEL","INFO").toUpperCase();this.level=T[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 n=typeof e=="string"?JSON.parse(e):e;if(t==="Bash"&&n.command){let r=n.command.length>50?n.command.substring(0,50)+"...":n.command;return`${t}(${r})`}if(t==="Read"&&n.file_path){let r=n.file_path.split("/").pop()||n.file_path;return`${t}(${r})`}if(t==="Edit"&&n.file_path){let r=n.file_path.split("/").pop()||n.file_path;return`${t}(${r})`}if(t==="Write"&&n.file_path){let r=n.file_path.split("/").pop()||n.file_path;return`${t}(${r})`}return t}catch{return t}}log(t,e,n,r,s){if(t<this.level)return;let _=new Date().toISOString().replace("T"," ").substring(0,23),p=T[t].padEnd(5),u=e.padEnd(6),a="";r?.correlationId?a=`[${r.correlationId}] `:r?.sessionId&&(a=`[session-${r.sessionId}] `);let c="";s!=null&&(this.level===0&&typeof s=="object"?c=`
`+JSON.stringify(s,null,2):c=" "+this.formatData(s));let A="";if(r){let{sessionId:Z,sdkSessionId:tt,correlationId:et,...M}=r;Object.keys(M).length>0&&(A=` {${Object.entries(M).map(([$,w])=>`${$}=${w}`).join(", ")}}`)}let L=`[${_}] [${p}] [${u}] ${a}${n}${A}${c}`;t===3?console.error(L):console.log(L)}debug(t,e,n,r){this.log(0,t,e,n,r)}info(t,e,n,r){this.log(1,t,e,n,r)}warn(t,e,n,r){this.log(2,t,e,n,r)}error(t,e,n,r){this.log(3,t,e,n,r)}dataIn(t,e,n,r){this.info(t,`\u2192 ${e}`,n,r)}dataOut(t,e,n,r){this.info(t,`\u2190 ${e}`,n,r)}success(t,e,n,r){this.info(t,`\u2713 ${e}`,n,r)}failure(t,e,n,r){this.error(t,`\u2717 ${e}`,n,r)}timing(t,e,n,r){this.info(t,`\u23F1 ${e}`,r,{duration:`${n}ms`})}},d=new O;var i=l.join(R(),".claude","plugins","marketplaces","thedotmack"),J=500,q=1e3,z=15;function f(){let o=l.join(R(),".claude-mem","settings.json"),t=g.loadFromFile(o);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function U(){try{let o=f();return(await fetch(`http://127.0.0.1:${o}/health`,{signal:AbortSignal.timeout(J)})).ok}catch(o){return d.debug("SYSTEM","Worker health check failed",{error:o instanceof Error?o.message:String(o),errorType:o?.constructor?.name}),!1}}async function Q(){try{let o=l.join(i,"plugin","scripts","worker-service.cjs");if(!m(o))throw new Error(`Worker script not found at ${o}`);if(process.platform==="win32"){let t=N("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(!m(t))throw new Error(`Ecosystem config not found at ${t}`);let e=l.join(i,"node_modules",".bin","pm2"),n=m(e)?e:"pm2",r=N(n,["start",t],{cwd:i,stdio:"pipe",encoding:"utf-8"});if(r.status!==0)throw new Error(r.stderr||"PM2 start failed")}for(let t=0;t<z;t++)if(await new Promise(e=>setTimeout(e,q)),await U())return!0;return!1}catch(o){return d.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 I(){if(await U())return;if(!await Q()){let t=f();throw new Error(`Worker service failed to start on port ${t}.
To start manually, run: To start manually, run:
cd ${n} cd ${i}
npx pm2 start ecosystem.config.cjs npx pm2 start ecosystem.config.cjs
If already running, try: npx pm2 restart claude-mem-worker`)}}import{appendFileSync as W}from"fs";import{homedir as x}from"os";import{join as F}from"path";var b=F(x(),".claude-mem","silent.log");function E(e,t,r=""){let s=new Date().toISOString(),O=((new Error().stack||"").split(` If already running, try: npx pm2 restart claude-mem-worker`)}}async function k(o){await I(),E("[cleanup-hook] Hook fired",{session_id:o?.session_id,cwd:o?.cwd,reason:o?.reason}),o||(console.log("No input provided - this script is designed to run as a Claude Code SessionEnd hook"),console.log(`
`)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),L=O?`${O[1].split("/").pop()}:${O[2]}`:"unknown",c=`[${s}] [${L}] ${e}`;if(t!==void 0)try{c+=` ${JSON.stringify(t)}`}catch(S){c+=` [stringify error: ${S}]`}c+=` Expected input format:`),console.log(JSON.stringify({session_id:"string",cwd:"string",transcript_path:"string",hook_event_name:"SessionEnd",reason:"exit"},null,2)),process.exit(0));let{session_id:t,reason:e}=o,n=f();try{let r=await fetch(`http://127.0.0.1:${n}/api/sessions/complete`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({claudeSessionId:t,reason:e}),signal:AbortSignal.timeout(2e3)});if(r.ok){let s=await r.json();E("[cleanup-hook] Session cleanup completed",s)}else E("[cleanup-hook] Session not found or already cleaned up")}catch(r){E("[cleanup-hook] Worker not reachable (non-critical)",{error:r.message})}console.log('{"continue": true, "suppressOutput": true}'),process.exit(0)}if(C.isTTY)k(void 0);else{let o="";C.on("data",t=>o+=t),C.on("end",async()=>{let t=o?JSON.parse(o):void 0;await k(t)})}
`;try{W(b,c)}catch(S){console.error("[silent-debug] Failed to write to log:",S)}return r}async function m(e){await M(),E("[cleanup-hook] Hook fired",{session_id:e?.session_id,cwd:e?.cwd,reason:e?.reason}),e||(console.log("No input provided - this script is designed to run as a Claude Code SessionEnd hook"),console.log(`
Expected input format:`),console.log(JSON.stringify({session_id:"string",cwd:"string",transcript_path:"string",hook_event_name:"SessionEnd",reason:"exit"},null,2)),process.exit(0));let{session_id:t,reason:r}=e,s=l();try{let o=await fetch(`http://127.0.0.1:${s}/api/sessions/complete`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({claudeSessionId:t,reason:r}),signal:AbortSignal.timeout(2e3)});if(o.ok){let i=await o.json();E("[cleanup-hook] Session cleanup completed",i)}else E("[cleanup-hook] Session not found or already cleaned up")}catch(o){E("[cleanup-hook] Worker not reachable (non-critical)",{error:o.message})}console.log('{"continue": true, "suppressOutput": true}'),process.exit(0)}if(p.isTTY)m(void 0);else{let e="";p.on("data",t=>e+=t),p.on("end",async()=>{let t=e?JSON.parse(e):void 0;await m(t)})}
File diff suppressed because one or more lines are too long
+7 -3
View File
@@ -1,8 +1,12 @@
#!/usr/bin/env node #!/usr/bin/env node
import h from"path";import{stdin as O}from"process";import{execSync as y}from"child_process";import n from"path";import{existsSync as T}from"fs";import{homedir as p}from"os";import{spawnSync as A}from"child_process";import{readFileSync as N,existsSync as m}from"fs";import{join as D}from"path";import{homedir as L}from"os";var l=["bugfix","feature","refactor","discovery","decision","change"],g=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var S=l.join(","),u=g.join(",");var i=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_DATA_DIR:D(L(),".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:S,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:u,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 r=this.get(t);return parseInt(r,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){if(!m(t))return this.getAllDefaults();let r=N(t,"utf-8"),o=JSON.parse(r).env||{},a={...this.DEFAULTS};for(let c of Object.keys(this.DEFAULTS))o[c]!==void 0&&(a[c]=o[c]);return a}};var s=n.join(p(),".claude","plugins","marketplaces","thedotmack"),d=500,U=1e3,R=15;function E(){let e=n.join(p(),".claude-mem","settings.json"),t=i.loadFromFile(e);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function C(){try{let e=E();return(await fetch(`http://127.0.0.1:${e}/health`,{signal:AbortSignal.timeout(d)})).ok}catch{return!1}}async function I(){try{let e=n.join(s,"plugin","scripts","worker-service.cjs");if(!T(e))throw new Error(`Worker script not found at ${e}`);if(process.platform==="win32"){let t=A("powershell.exe",["-NoProfile","-NonInteractive","-Command",`Start-Process -FilePath 'node' -ArgumentList '${e}' -WorkingDirectory '${s}' -WindowStyle Hidden`],{cwd:s,stdio:"pipe",encoding:"utf-8",windowsHide:!0});if(t.status!==0)throw new Error(t.stderr||"PowerShell Start-Process failed")}else{let t=n.join(s,"ecosystem.config.cjs");if(!T(t))throw new Error(`Ecosystem config not found at ${t}`);let r=n.join(s,"node_modules",".bin","pm2"),_=T(r)?r:"pm2",o=A(_,["start",t],{cwd:s,stdio:"pipe",encoding:"utf-8"});if(o.status!==0)throw new Error(o.stderr||"PM2 start failed")}for(let t=0;t<R;t++)if(await new Promise(r=>setTimeout(r,U)),await C())return!0;return!1}catch{return!1}}async function f(){if(await C())return;if(!await I()){let t=E();throw new Error(`Worker service failed to start on port ${t}. import Z from"path";import{stdin as C}from"process";import{execSync as tt}from"child_process";import _ from"path";import{existsSync as m}from"fs";import{homedir as R}from"os";import{spawnSync as N}from"child_process";import{readFileSync as x,existsSync as b}from"fs";import{join as W}from"path";import{homedir as F}from"os";var k=["bugfix","feature","refactor","discovery","decision","change"],v=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var M=k.join(","),D=v.join(",");var p=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_DATA_DIR:W(F(),".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:D,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(!b(t))return this.getAllDefaults();let e=x(t,"utf-8"),n=JSON.parse(e).env||{},s={...this.DEFAULTS};for(let E of Object.keys(this.DEFAULTS))n[E]!==void 0&&(s[E]=n[E]);return s}};import{join as B}from"path";import{homedir as G}from"os";import{existsSync as Y,readFileSync as K}from"fs";import{appendFileSync as j}from"fs";import{homedir as H}from"os";import{join as V}from"path";var X=V(H(),".claude-mem","silent.log");function h(o,t,e=""){let r=new Date().toISOString(),l=((new Error().stack||"").split(`
`)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),g=l?`${l[1].split("/").pop()}:${l[2]}`:"unknown",c=`[${r}] [${g}] ${o}`;if(t!==void 0)try{c+=` ${JSON.stringify(t)}`}catch(a){c+=` [stringify error: ${a}]`}c+=`
`;try{j(X,c)}catch(a){console.error("[silent-debug] Failed to write to log:",a)}return e}var u=B(G(),".claude-mem","settings.json");function y(o,t){try{if(Y(u)){let r=JSON.parse(K(u,"utf-8")).env?.[o];if(r!==void 0)return r}}catch(e){h("Failed to load settings file",{error:e,settingsPath:u,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||{}),T=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 E=new Date().toISOString().replace("T"," ").substring(0,23),l=S[t].padEnd(5),g=e.padEnd(6),c="";n?.correlationId?c=`[${n.correlationId}] `:n?.sessionId&&(c=`[session-${n.sessionId}] `);let a="";s!=null&&(this.level===0&&typeof s=="object"?a=`
`+JSON.stringify(s,null,2):a=" "+this.formatData(s));let d="";if(n){let{sessionId:rt,sdkSessionId:nt,correlationId:ot,...L}=n;Object.keys(L).length>0&&(d=` {${Object.entries(L).map(([w,P])=>`${w}=${P}`).join(", ")}}`)}let A=`[${E}] [${l}] [${g}] ${c}${r}${d}${a}`;t===3?console.error(A):console.log(A)}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`})}},O=new T;var i=_.join(R(),".claude","plugins","marketplaces","thedotmack"),J=500,q=1e3,z=15;function f(){let o=_.join(R(),".claude-mem","settings.json"),t=p.loadFromFile(o);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function U(){try{let o=f();return(await fetch(`http://127.0.0.1:${o}/health`,{signal:AbortSignal.timeout(J)})).ok}catch(o){return O.debug("SYSTEM","Worker health check failed",{error:o instanceof Error?o.message:String(o),errorType:o?.constructor?.name}),!1}}async function Q(){try{let o=_.join(i,"plugin","scripts","worker-service.cjs");if(!m(o))throw new Error(`Worker script not found at ${o}`);if(process.platform==="win32"){let t=N("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=_.join(i,"ecosystem.config.cjs");if(!m(t))throw new Error(`Ecosystem config not found at ${t}`);let e=_.join(i,"node_modules",".bin","pm2"),r=m(e)?e:"pm2",n=N(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 U())return!0;return!1}catch(o){return O.error("SYSTEM","Failed to start worker",{platform:process.platform,workerScript:_.join(i,"plugin","scripts","worker-service.cjs"),error:o instanceof Error?o.message:String(o),marketplaceRoot:i}),!1}}async function I(){if(await U())return;if(!await Q()){let t=f();throw new Error(`Worker service failed to start on port ${t}.
To start manually, run: To start manually, run:
cd ${s} cd ${i}
npx pm2 start ecosystem.config.cjs npx pm2 start ecosystem.config.cjs
If already running, try: npx pm2 restart claude-mem-worker`)}}async function M(e){await f();let t=e?.cwd??process.cwd(),r=t?h.basename(t):"unknown-project",o=`http://127.0.0.1:${E()}/api/context/inject?project=${encodeURIComponent(r)}`;return y(`curl -s "${o}"`,{encoding:"utf-8",timeout:5e3}).trim()}var P=process.argv.includes("--colors");if(O.isTTY||P)M(void 0).then(e=>{console.log(e),process.exit(0)});else{let e="";O.on("data",t=>e+=t),O.on("end",async()=>{let t=e.trim()?JSON.parse(e):void 0,r=await M(t);console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:r}})),process.exit(0)})} If already running, try: npx pm2 restart claude-mem-worker`)}}async function $(o){await I();let t=o?.cwd??process.cwd(),e=t?Z.basename(t):"unknown-project",n=`http://127.0.0.1:${f()}/api/context/inject?project=${encodeURIComponent(e)}`;return tt(`curl -s "${n}"`,{encoding:"utf-8",timeout:5e3}).trim()}var et=process.argv.includes("--colors");if(C.isTTY||et)$(void 0).then(o=>{console.log(o),process.exit(0)});else{let o="";C.on("data",t=>o+=t),C.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)})}
File diff suppressed because one or more lines are too long
+7 -5
View File
@@ -1,10 +1,12 @@
#!/usr/bin/env node #!/usr/bin/env node
import X from"path";import{stdin as h}from"process";function D(e,t,r){return e==="PreCompact"?t?{continue:!0,suppressOutput:!0}:{continue:!1,stopReason:r.reason||"Pre-compact operation failed",suppressOutput:!0}:e==="SessionStart"?t&&r.context?{continue:!0,suppressOutput:!0,hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:r.context}}:{continue:!0,suppressOutput:!0}:e==="UserPromptSubmit"||e==="PostToolUse"?{continue:!0,suppressOutput:!0}:e==="Stop"?{continue:!0,suppressOutput:!0}:{continue:t,suppressOutput:!0,...r.reason&&!t?{stopReason:r.reason}:{}}}function T(e,t,r={}){let s=D(e,t,r);return JSON.stringify(s)}import u from"path";import{existsSync as O}from"fs";import{homedir as C}from"os";import{spawnSync as A}from"child_process";import{readFileSync as U,existsSync as R}from"fs";import{join as P}from"path";import{homedir as k}from"os";var w=["bugfix","feature","refactor","discovery","decision","change"],y=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var d=w.join(","),g=y.join(",");var _=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_DATA_DIR:P(k(),".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:d,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:g,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 r=this.get(t);return parseInt(r,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){if(!R(t))return this.getAllDefaults();let r=U(t,"utf-8"),n=JSON.parse(r).env||{},p={...this.DEFAULTS};for(let a of Object.keys(this.DEFAULTS))n[a]!==void 0&&(p[a]=n[a]);return p}};var E=u.join(C(),".claude","plugins","marketplaces","thedotmack"),I=500,b=1e3,x=15;function l(){let e=u.join(C(),".claude-mem","settings.json"),t=_.loadFromFile(e);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function M(){try{let e=l();return(await fetch(`http://127.0.0.1:${e}/health`,{signal:AbortSignal.timeout(I)})).ok}catch{return!1}}async function H(){try{let e=u.join(E,"plugin","scripts","worker-service.cjs");if(!O(e))throw new Error(`Worker script not found at ${e}`);if(process.platform==="win32"){let t=A("powershell.exe",["-NoProfile","-NonInteractive","-Command",`Start-Process -FilePath 'node' -ArgumentList '${e}' -WorkingDirectory '${E}' -WindowStyle Hidden`],{cwd:E,stdio:"pipe",encoding:"utf-8",windowsHide:!0});if(t.status!==0)throw new Error(t.stderr||"PowerShell Start-Process failed")}else{let t=u.join(E,"ecosystem.config.cjs");if(!O(t))throw new Error(`Ecosystem config not found at ${t}`);let r=u.join(E,"node_modules",".bin","pm2"),s=O(r)?r:"pm2",n=A(s,["start",t],{cwd:E,stdio:"pipe",encoding:"utf-8"});if(n.status!==0)throw new Error(n.stderr||"PM2 start failed")}for(let t=0;t<x;t++)if(await new Promise(r=>setTimeout(r,b)),await M())return!0;return!1}catch{return!1}}async function N(){if(await M())return;if(!await H()){let t=l();throw new Error(`Worker service failed to start on port ${t}. import et from"path";import{stdin as k}from"process";function $(n,t,e){return n==="PreCompact"?t?{continue:!0,suppressOutput:!0}:{continue:!1,stopReason:e.reason||"Pre-compact operation failed",suppressOutput:!0}:n==="SessionStart"?t&&e.context?{continue:!0,suppressOutput:!0,hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:e.context}}:{continue:!0,suppressOutput:!0}:n==="UserPromptSubmit"||n==="PostToolUse"?{continue:!0,suppressOutput:!0}:n==="Stop"?{continue:!0,suppressOutput:!0}:{continue:t,suppressOutput:!0,...e.reason&&!t?{stopReason:e.reason}:{}}}function S(n,t,e={}){let r=$(n,t,e);return JSON.stringify(r)}import E from"path";import{existsSync as h}from"fs";import{homedir as w}from"os";import{spawnSync as R}from"child_process";import{readFileSync as H,existsSync as W}from"fs";import{join as F}from"path";import{homedir as j}from"os";var v=["bugfix","feature","refactor","discovery","decision","change"],x=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var M=v.join(","),D=x.join(",");var g=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:D,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(!W(t))return this.getAllDefaults();let e=H(t,"utf-8"),o=JSON.parse(e).env||{},i={...this.DEFAULTS};for(let c of Object.keys(this.DEFAULTS))o[c]!==void 0&&(i[c]=o[c]);return i}};import{join as K}from"path";import{homedir as Y}from"os";import{existsSync as J,readFileSync as q}from"fs";import{appendFileSync as V}from"fs";import{homedir as X}from"os";import{join as B}from"path";var G=B(X(),".claude-mem","silent.log");function _(n,t,e=""){let r=new Date().toISOString(),p=((new Error().stack||"").split(`
`)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),l=p?`${p[1].split("/").pop()}:${p[2]}`:"unknown",s=`[${r}] [${l}] ${n}`;if(t!==void 0)try{s+=` ${JSON.stringify(t)}`}catch(a){s+=` [stringify error: ${a}]`}s+=`
`;try{V(G,s)}catch(a){console.error("[silent-debug] Failed to write to log:",a)}return e}var O=K(Y(),".claude-mem","settings.json");function N(n,t){try{if(J(O)){let r=JSON.parse(q(O,"utf-8")).env?.[n];if(r!==void 0)return r}}catch(e){_("Failed to load settings file",{error:e,settingsPath:O,key:n})}return process.env[n]||t}var T=(i=>(i[i.DEBUG=0]="DEBUG",i[i.INFO=1]="INFO",i[i.WARN=2]="WARN",i[i.ERROR=3]="ERROR",i[i.SILENT=4]="SILENT",i))(T||{}),d=class{level;useColor;constructor(){let t=N("CLAUDE_MEM_LOG_LEVEL","INFO").toUpperCase();this.level=T[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 o=r.command.length>50?r.command.substring(0,50)+"...":r.command;return`${t}(${o})`}if(t==="Read"&&r.file_path){let o=r.file_path.split("/").pop()||r.file_path;return`${t}(${o})`}if(t==="Edit"&&r.file_path){let o=r.file_path.split("/").pop()||r.file_path;return`${t}(${o})`}if(t==="Write"&&r.file_path){let o=r.file_path.split("/").pop()||r.file_path;return`${t}(${o})`}return t}catch{return t}}log(t,e,r,o,i){if(t<this.level)return;let c=new Date().toISOString().replace("T"," ").substring(0,23),p=T[t].padEnd(5),l=e.padEnd(6),s="";o?.correlationId?s=`[${o.correlationId}] `:o?.sessionId&&(s=`[session-${o.sessionId}] `);let a="";i!=null&&(this.level===0&&typeof i=="object"?a=`
`+JSON.stringify(i,null,2):a=" "+this.formatData(i));let f="";if(o){let{sessionId:ot,sdkSessionId:nt,correlationId:st,...L}=o;Object.keys(L).length>0&&(f=` {${Object.entries(L).map(([b,P])=>`${b}=${P}`).join(", ")}}`)}let y=`[${c}] [${p}] [${l}] ${s}${r}${f}${a}`;t===3?console.error(y):console.log(y)}debug(t,e,r,o){this.log(0,t,e,r,o)}info(t,e,r,o){this.log(1,t,e,r,o)}warn(t,e,r,o){this.log(2,t,e,r,o)}error(t,e,r,o){this.log(3,t,e,r,o)}dataIn(t,e,r,o){this.info(t,`\u2192 ${e}`,r,o)}dataOut(t,e,r,o){this.info(t,`\u2190 ${e}`,r,o)}success(t,e,r,o){this.info(t,`\u2713 ${e}`,r,o)}failure(t,e,r,o){this.error(t,`\u2717 ${e}`,r,o)}timing(t,e,r,o){this.info(t,`\u23F1 ${e}`,o,{duration:`${r}ms`})}},C=new d;var u=E.join(w(),".claude","plugins","marketplaces","thedotmack"),z=500,Q=1e3,Z=15;function m(){let n=E.join(w(),".claude-mem","settings.json"),t=g.loadFromFile(n);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function U(){try{let n=m();return(await fetch(`http://127.0.0.1:${n}/health`,{signal:AbortSignal.timeout(z)})).ok}catch(n){return C.debug("SYSTEM","Worker health check failed",{error:n instanceof Error?n.message:String(n),errorType:n?.constructor?.name}),!1}}async function tt(){try{let n=E.join(u,"plugin","scripts","worker-service.cjs");if(!h(n))throw new Error(`Worker script not found at ${n}`);if(process.platform==="win32"){let t=R("powershell.exe",["-NoProfile","-NonInteractive","-Command",`Start-Process -FilePath 'node' -ArgumentList '${n}' -WorkingDirectory '${u}' -WindowStyle Hidden`],{cwd:u,stdio:"pipe",encoding:"utf-8",windowsHide:!0});if(t.status!==0)throw new Error(t.stderr||"PowerShell Start-Process failed")}else{let t=E.join(u,"ecosystem.config.cjs");if(!h(t))throw new Error(`Ecosystem config not found at ${t}`);let e=E.join(u,"node_modules",".bin","pm2"),r=h(e)?e:"pm2",o=R(r,["start",t],{cwd:u,stdio:"pipe",encoding:"utf-8"});if(o.status!==0)throw new Error(o.stderr||"PM2 start failed")}for(let t=0;t<Z;t++)if(await new Promise(e=>setTimeout(e,Q)),await U())return!0;return!1}catch(n){return C.error("SYSTEM","Failed to start worker",{platform:process.platform,workerScript:E.join(u,"plugin","scripts","worker-service.cjs"),error:n instanceof Error?n.message:String(n),marketplaceRoot:u}),!1}}async function I(){if(await U())return;if(!await tt()){let t=m();throw new Error(`Worker service failed to start on port ${t}.
To start manually, run: To start manually, run:
cd ${E} cd ${u}
npx pm2 start ecosystem.config.cjs npx pm2 start ecosystem.config.cjs
If already running, try: npx pm2 restart claude-mem-worker`)}}import{appendFileSync as W}from"fs";import{homedir as F}from"os";import{join as $}from"path";var v=$(F(),".claude-mem","silent.log");function m(e,t,r=""){let s=new Date().toISOString(),c=((new Error().stack||"").split(` If already running, try: npx pm2 restart claude-mem-worker`)}}async function rt(n){if(await I(),!n)throw new Error("newHook requires input");let{session_id:t,cwd:e,prompt:r}=n;_("[new-hook] Input received",{session_id:t,cwd:e,cwd_type:typeof e,cwd_length:e?.length,has_cwd:!!e,prompt_length:r?.length});let o=et.basename(e);_("[new-hook] Project extracted",{project:o,project_type:typeof o,project_length:o?.length,is_empty:o==="",cwd_was:e});let i=m(),c,p;try{let s=await fetch(`http://127.0.0.1:${i}/api/sessions/init`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({claudeSessionId:t,project:o,prompt:r}),signal:AbortSignal.timeout(5e3)});if(!s.ok){let f=await s.text();throw new Error(`Failed to initialize session: ${s.status} ${f}`)}let a=await s.json();if(c=a.sessionDbId,p=a.promptNumber,a.skipped&&a.reason==="private"){console.error(`[new-hook] Session ${c}, prompt #${p} (fully private - skipped)`),console.log(S("UserPromptSubmit",!0));return}console.error(`[new-hook] Session ${c}, prompt #${p}`)}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}let l=r.startsWith("/")?r.substring(1):r;try{let s=await fetch(`http://127.0.0.1:${i}/sessions/${c}/init`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({project:o,userPrompt:l,promptNumber:p}),signal:AbortSignal.timeout(5e3)});if(!s.ok){let a=await s.text();throw new Error(`Failed to initialize session: ${s.status} ${a}`)}}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}console.log(S("UserPromptSubmit",!0))}var A="";k.on("data",n=>A+=n);k.on("end",async()=>{let n=A?JSON.parse(A):void 0;await rt(n)});
`)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),S=c?`${c[1].split("/").pop()}:${c[2]}`:"unknown",o=`[${s}] [${S}] ${e}`;if(t!==void 0)try{o+=` ${JSON.stringify(t)}`}catch(i){o+=` [stringify error: ${i}]`}o+=`
`;try{W(v,o)}catch(i){console.error("[silent-debug] Failed to write to log:",i)}return r}async function j(e){if(await N(),!e)throw new Error("newHook requires input");let{session_id:t,cwd:r,prompt:s}=e;m("[new-hook] Input received",{session_id:t,cwd:r,cwd_type:typeof r,cwd_length:r?.length,has_cwd:!!r,prompt_length:s?.length});let n=X.basename(r);m("[new-hook] Project extracted",{project:n,project_type:typeof n,project_length:n?.length,is_empty:n==="",cwd_was:r});let p=l(),a,c;try{let o=await fetch(`http://127.0.0.1:${p}/api/sessions/init`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({claudeSessionId:t,project:n,prompt:s}),signal:AbortSignal.timeout(5e3)});if(!o.ok){let L=await o.text();throw new Error(`Failed to initialize session: ${o.status} ${L}`)}let i=await o.json();if(a=i.sessionDbId,c=i.promptNumber,i.skipped&&i.reason==="private"){console.error(`[new-hook] Session ${a}, prompt #${c} (fully private - skipped)`),console.log(T("UserPromptSubmit",!0));return}console.error(`[new-hook] Session ${a}, prompt #${c}`)}catch(o){throw o.cause?.code==="ECONNREFUSED"||o.name==="TimeoutError"||o.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"):o}let S=s.startsWith("/")?s.substring(1):s;try{let o=await fetch(`http://127.0.0.1:${p}/sessions/${a}/init`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({project:n,userPrompt:S,promptNumber:c}),signal:AbortSignal.timeout(5e3)});if(!o.ok){let i=await o.text();throw new Error(`Failed to initialize session: ${o.status} ${i}`)}}catch(o){throw o.cause?.code==="ECONNREFUSED"||o.name==="TimeoutError"||o.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"):o}console.log(T("UserPromptSubmit",!0))}var f="";h.on("data",e=>f+=e);h.on("end",async()=>{let e=f?JSON.parse(f):void 0;await j(e)});
+7 -5
View File
@@ -1,10 +1,12 @@
#!/usr/bin/env node #!/usr/bin/env node
import{stdin as P}from"process";function v(n,t,e){return n==="PreCompact"?t?{continue:!0,suppressOutput:!0}:{continue:!1,stopReason:e.reason||"Pre-compact operation failed",suppressOutput:!0}:n==="SessionStart"?t&&e.context?{continue:!0,suppressOutput:!0,hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:e.context}}:{continue:!0,suppressOutput:!0}:n==="UserPromptSubmit"||n==="PostToolUse"?{continue:!0,suppressOutput:!0}:n==="Stop"?{continue:!0,suppressOutput:!0}:{continue:t,suppressOutput:!0,...e.reason&&!t?{stopReason:e.reason}:{}}}function S(n,t,e={}){let o=v(n,t,e);return JSON.stringify(o)}import{join as b}from"path";import{homedir as x}from"os";import{existsSync as $,readFileSync as H}from"fs";var M=b(x(),".claude-mem","settings.json");function y(n,t){try{if($(M)){let o=JSON.parse(H(M,"utf-8")).env?.[n];if(o!==void 0)return o}}catch{}return process.env[n]||t}var T=(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))(T||{}),g=class{level;useColor;constructor(){let t=y("CLAUDE_MEM_LOG_LEVEL","INFO").toUpperCase();this.level=T[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} import{stdin as k}from"process";function b(n,t,e){return n==="PreCompact"?t?{continue:!0,suppressOutput:!0}:{continue:!1,stopReason:e.reason||"Pre-compact operation failed",suppressOutput:!0}:n==="SessionStart"?t&&e.context?{continue:!0,suppressOutput:!0,hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:e.context}}:{continue:!0,suppressOutput:!0}:n==="UserPromptSubmit"||n==="PostToolUse"?{continue:!0,suppressOutput:!0}:n==="Stop"?{continue:!0,suppressOutput:!0}:{continue:t,suppressOutput:!0,...e.reason&&!t?{stopReason:e.reason}:{}}}function S(n,t,e={}){let r=b(n,t,e);return JSON.stringify(r)}import{join as F}from"path";import{homedir as j}from"os";import{existsSync as V,readFileSync as X}from"fs";import{appendFileSync as v}from"fs";import{homedir as x}from"os";import{join as H}from"path";var W=H(x(),".claude-mem","silent.log");function h(n,t,e=""){let r=new Date().toISOString(),p=((new Error().stack||"").split(`
${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 o=typeof e=="string"?JSON.parse(e):e;if(t==="Bash"&&o.command){let r=o.command.length>50?o.command.substring(0,50)+"...":o.command;return`${t}(${r})`}if(t==="Read"&&o.file_path){let r=o.file_path.split("/").pop()||o.file_path;return`${t}(${r})`}if(t==="Edit"&&o.file_path){let r=o.file_path.split("/").pop()||o.file_path;return`${t}(${r})`}if(t==="Write"&&o.file_path){let r=o.file_path.split("/").pop()||o.file_path;return`${t}(${r})`}return t}catch{return t}}log(t,e,o,r,s){if(t<this.level)return;let a=new Date().toISOString().replace("T"," ").substring(0,23),f=T[t].padEnd(5),i=e.padEnd(6),u="";r?.correlationId?u=`[${r.correlationId}] `:r?.sessionId&&(u=`[session-${r.sessionId}] `);let O="";s!=null&&(this.level===0&&typeof s=="object"?O=` `)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),i=p?`${p[1].split("/").pop()}:${p[2]}`:"unknown",a=`[${r}] [${i}] ${n}`;if(t!==void 0)try{a+=` ${JSON.stringify(t)}`}catch(l){a+=` [stringify error: ${l}]`}a+=`
`+JSON.stringify(s,null,2):O=" "+this.formatData(s));let d="";if(r){let{sessionId:z,sdkSessionId:Z,correlationId:tt,...L}=r;Object.keys(L).length>0&&(d=` {${Object.entries(L).map(([w,k])=>`${w}=${k}`).join(", ")}}`)}let A=`[${a}] [${f}] [${i}] ${u}${o}${d}${O}`;t===3?console.error(A):console.log(A)}debug(t,e,o,r){this.log(0,t,e,o,r)}info(t,e,o,r){this.log(1,t,e,o,r)}warn(t,e,o,r){this.log(2,t,e,o,r)}error(t,e,o,r){this.log(3,t,e,o,r)}dataIn(t,e,o,r){this.info(t,`\u2192 ${e}`,o,r)}dataOut(t,e,o,r){this.info(t,`\u2190 ${e}`,o,r)}success(t,e,o,r){this.info(t,`\u2713 ${e}`,o,r)}failure(t,e,o,r){this.error(t,`\u2717 ${e}`,o,r)}timing(t,e,o,r){this.info(t,`\u23F1 ${e}`,r,{duration:`${o}ms`})}},c=new g;import p from"path";import{existsSync as m}from"fs";import{homedir as R}from"os";import{spawnSync as N}from"child_process";import{readFileSync as V,existsSync as j}from"fs";import{join as X}from"path";import{homedir as B}from"os";var W=["bugfix","feature","refactor","discovery","decision","change"],F=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var D=W.join(","),h=F.join(",");var _=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_DATA_DIR:X(B(),".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:D,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:h,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(!j(t))return this.getAllDefaults();let e=V(t,"utf-8"),r=JSON.parse(e).env||{},s={...this.DEFAULTS};for(let a of Object.keys(this.DEFAULTS))r[a]!==void 0&&(s[a]=r[a]);return s}};var E=p.join(R(),".claude","plugins","marketplaces","thedotmack"),K=500,G=1e3,Y=15;function l(){let n=p.join(R(),".claude-mem","settings.json"),t=_.loadFromFile(n);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function U(){try{let n=l();return(await fetch(`http://127.0.0.1:${n}/health`,{signal:AbortSignal.timeout(K)})).ok}catch{return!1}}async function J(){try{let n=p.join(E,"plugin","scripts","worker-service.cjs");if(!m(n))throw new Error(`Worker script not found at ${n}`);if(process.platform==="win32"){let t=N("powershell.exe",["-NoProfile","-NonInteractive","-Command",`Start-Process -FilePath 'node' -ArgumentList '${n}' -WorkingDirectory '${E}' -WindowStyle Hidden`],{cwd:E,stdio:"pipe",encoding:"utf-8",windowsHide:!0});if(t.status!==0)throw new Error(t.stderr||"PowerShell Start-Process failed")}else{let t=p.join(E,"ecosystem.config.cjs");if(!m(t))throw new Error(`Ecosystem config not found at ${t}`);let e=p.join(E,"node_modules",".bin","pm2"),o=m(e)?e:"pm2",r=N(o,["start",t],{cwd:E,stdio:"pipe",encoding:"utf-8"});if(r.status!==0)throw new Error(r.stderr||"PM2 start failed")}for(let t=0;t<Y;t++)if(await new Promise(e=>setTimeout(e,G)),await U())return!0;return!1}catch{return!1}}async function I(){if(await U())return;if(!await J()){let t=l();throw new Error(`Worker service failed to start on port ${t}. `;try{v(W,a)}catch(l){console.error("[silent-debug] Failed to write to log:",l)}return e}var O=F(j(),".claude-mem","settings.json");function y(n,t){try{if(V(O)){let r=JSON.parse(X(O,"utf-8")).env?.[n];if(r!==void 0)return r}}catch(e){h("Failed to load settings file",{error:e,settingsPath:O,key:n})}return process.env[n]||t}var T=(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))(T||{}),m=class{level;useColor;constructor(){let t=y("CLAUDE_MEM_LOG_LEVEL","INFO").toUpperCase();this.level=T[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 o=r.command.length>50?r.command.substring(0,50)+"...":r.command;return`${t}(${o})`}if(t==="Read"&&r.file_path){let o=r.file_path.split("/").pop()||r.file_path;return`${t}(${o})`}if(t==="Edit"&&r.file_path){let o=r.file_path.split("/").pop()||r.file_path;return`${t}(${o})`}if(t==="Write"&&r.file_path){let o=r.file_path.split("/").pop()||r.file_path;return`${t}(${o})`}return t}catch{return t}}log(t,e,r,o,s){if(t<this.level)return;let c=new Date().toISOString().replace("T"," ").substring(0,23),p=T[t].padEnd(5),i=e.padEnd(6),a="";o?.correlationId?a=`[${o.correlationId}] `:o?.sessionId&&(a=`[session-${o.sessionId}] `);let l="";s!=null&&(this.level===0&&typeof s=="object"?l=`
`+JSON.stringify(s,null,2):l=" "+this.formatData(s));let A="";if(o){let{sessionId:ot,sdkSessionId:nt,correlationId:st,...M}=o;Object.keys(M).length>0&&(A=` {${Object.entries(M).map(([w,$])=>`${w}=${$}`).join(", ")}}`)}let L=`[${c}] [${p}] [${i}] ${a}${r}${A}${l}`;t===3?console.error(L):console.log(L)}debug(t,e,r,o){this.log(0,t,e,r,o)}info(t,e,r,o){this.log(1,t,e,r,o)}warn(t,e,r,o){this.log(2,t,e,r,o)}error(t,e,r,o){this.log(3,t,e,r,o)}dataIn(t,e,r,o){this.info(t,`\u2192 ${e}`,r,o)}dataOut(t,e,r,o){this.info(t,`\u2190 ${e}`,r,o)}success(t,e,r,o){this.info(t,`\u2713 ${e}`,r,o)}failure(t,e,r,o){this.error(t,`\u2717 ${e}`,r,o)}timing(t,e,r,o){this.info(t,`\u23F1 ${e}`,o,{duration:`${r}ms`})}},E=new m;import _ from"path";import{existsSync as d}from"fs";import{homedir as U}from"os";import{spawnSync as R}from"child_process";import{readFileSync as G,existsSync as Y}from"fs";import{join as J}from"path";import{homedir as q}from"os";var B=["bugfix","feature","refactor","discovery","decision","change"],K=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var D=B.join(","),N=K.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:J(q(),".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:D,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:N,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(!Y(t))return this.getAllDefaults();let e=G(t,"utf-8"),o=JSON.parse(e).env||{},s={...this.DEFAULTS};for(let c of Object.keys(this.DEFAULTS))o[c]!==void 0&&(s[c]=o[c]);return s}};var u=_.join(U(),".claude","plugins","marketplaces","thedotmack"),Q=500,z=1e3,Z=15;function g(){let n=_.join(U(),".claude-mem","settings.json"),t=f.loadFromFile(n);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function I(){try{let n=g();return(await fetch(`http://127.0.0.1:${n}/health`,{signal:AbortSignal.timeout(Q)})).ok}catch(n){return E.debug("SYSTEM","Worker health check failed",{error:n instanceof Error?n.message:String(n),errorType:n?.constructor?.name}),!1}}async function tt(){try{let n=_.join(u,"plugin","scripts","worker-service.cjs");if(!d(n))throw new Error(`Worker script not found at ${n}`);if(process.platform==="win32"){let t=R("powershell.exe",["-NoProfile","-NonInteractive","-Command",`Start-Process -FilePath 'node' -ArgumentList '${n}' -WorkingDirectory '${u}' -WindowStyle Hidden`],{cwd:u,stdio:"pipe",encoding:"utf-8",windowsHide:!0});if(t.status!==0)throw new Error(t.stderr||"PowerShell Start-Process failed")}else{let t=_.join(u,"ecosystem.config.cjs");if(!d(t))throw new Error(`Ecosystem config not found at ${t}`);let e=_.join(u,"node_modules",".bin","pm2"),r=d(e)?e:"pm2",o=R(r,["start",t],{cwd:u,stdio:"pipe",encoding:"utf-8"});if(o.status!==0)throw new Error(o.stderr||"PM2 start failed")}for(let t=0;t<Z;t++)if(await new Promise(e=>setTimeout(e,z)),await I())return!0;return!1}catch(n){return E.error("SYSTEM","Failed to start worker",{platform:process.platform,workerScript:_.join(u,"plugin","scripts","worker-service.cjs"),error:n instanceof Error?n.message:String(n),marketplaceRoot:u}),!1}}async function P(){if(await I())return;if(!await tt()){let t=g();throw new Error(`Worker service failed to start on port ${t}.
To start manually, run: To start manually, run:
cd ${E} cd ${u}
npx pm2 start ecosystem.config.cjs npx pm2 start ecosystem.config.cjs
If already running, try: npx pm2 restart claude-mem-worker`)}}var q=new Set(["ListMcpResourcesTool","SlashCommand","Skill","TodoWrite","AskUserQuestion"]);async function Q(n){if(await I(),!n)throw new Error("saveHook requires input");let{session_id:t,cwd:e,tool_name:o,tool_input:r,tool_response:s}=n;if(q.has(o)){console.log(S("PostToolUse",!0));return}let a=l(),f=c.formatTool(o,r);c.dataIn("HOOK",`PostToolUse: ${f}`,{workerPort:a});try{let i=await fetch(`http://127.0.0.1:${a}/api/sessions/observations`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({claudeSessionId:t,tool_name:o,tool_input:r,tool_response:s,cwd:e||""}),signal:AbortSignal.timeout(2e3)});if(!i.ok){let u=await i.text();throw c.failure("HOOK","Failed to send observation",{status:i.status},u),new Error(`Failed to send observation to worker: ${i.status} ${u}`)}c.debug("HOOK","Observation sent successfully",{toolName:o})}catch(i){throw i.cause?.code==="ECONNREFUSED"||i.name==="TimeoutError"||i.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"):i}console.log(S("PostToolUse",!0))}var C="";P.on("data",n=>C+=n);P.on("end",async()=>{let n=C?JSON.parse(C):void 0;await Q(n)}); If already running, try: npx pm2 restart claude-mem-worker`)}}var et=new Set(["ListMcpResourcesTool","SlashCommand","Skill","TodoWrite","AskUserQuestion"]);async function rt(n){if(await P(),!n)throw new Error("saveHook requires input");let{session_id:t,cwd:e,tool_name:r,tool_input:o,tool_response:s}=n;if(et.has(r)){console.log(S("PostToolUse",!0));return}let c=g(),p=E.formatTool(r,o);E.dataIn("HOOK",`PostToolUse: ${p}`,{workerPort:c});try{let i=await fetch(`http://127.0.0.1:${c}/api/sessions/observations`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({claudeSessionId:t,tool_name:r,tool_input:o,tool_response:s,cwd:e||""}),signal:AbortSignal.timeout(2e3)});if(!i.ok){let a=await i.text();throw E.failure("HOOK","Failed to send observation",{status:i.status},a),new Error(`Failed to send observation to worker: ${i.status} ${a}`)}E.debug("HOOK","Observation sent successfully",{toolName:r})}catch(i){throw i.cause?.code==="ECONNREFUSED"||i.name==="TimeoutError"||i.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"):i}console.log(S("PostToolUse",!0))}var C="";k.on("data",n=>C+=n);k.on("end",async()=>{let n=C?JSON.parse(C):void 0;await rt(n)});
+12 -10
View File
@@ -1,16 +1,18 @@
#!/usr/bin/env node #!/usr/bin/env node
import{stdin as x}from"process";import{readFileSync as P,existsSync as w}from"fs";function H(o,t,e){return o==="PreCompact"?t?{continue:!0,suppressOutput:!0}:{continue:!1,stopReason:e.reason||"Pre-compact operation failed",suppressOutput:!0}:o==="SessionStart"?t&&e.context?{continue:!0,suppressOutput:!0,hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:e.context}}:{continue:!0,suppressOutput:!0}:o==="UserPromptSubmit"||o==="PostToolUse"?{continue:!0,suppressOutput:!0}:o==="Stop"?{continue:!0,suppressOutput:!0}:{continue:t,suppressOutput:!0,...e.reason&&!t?{stopReason:e.reason}:{}}}function A(o,t,e={}){let r=H(o,t,e);return JSON.stringify(r)}import{join as v}from"path";import{homedir as $}from"os";import{existsSync as W,readFileSync as F}from"fs";var M=v($(),".claude-mem","settings.json");function L(o,t){try{if(W(M)){let r=JSON.parse(F(M,"utf-8")).env?.[o];if(r!==void 0)return r}}catch{}return process.env[o]||t}var O=(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))(O||{}),S=class{level;useColor;constructor(){let t=L("CLAUDE_MEM_LOG_LEVEL","INFO").toUpperCase();this.level=O[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} import{stdin as w}from"process";import{readFileSync as x,existsSync as P}from"fs";function v(n,t,e){return n==="PreCompact"?t?{continue:!0,suppressOutput:!0}:{continue:!1,stopReason:e.reason||"Pre-compact operation failed",suppressOutput:!0}:n==="SessionStart"?t&&e.context?{continue:!0,suppressOutput:!0,hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:e.context}}:{continue:!0,suppressOutput:!0}:n==="UserPromptSubmit"||n==="PostToolUse"?{continue:!0,suppressOutput:!0}:n==="Stop"?{continue:!0,suppressOutput:!0}:{continue:t,suppressOutput:!0,...e.reason&&!t?{stopReason:e.reason}:{}}}function L(n,t,e={}){let r=v(n,t,e);return JSON.stringify(r)}import{join as V}from"path";import{homedir as X}from"os";import{existsSync as B,readFileSync as K}from"fs";import{appendFileSync as H}from"fs";import{homedir as F}from"os";import{join as W}from"path";var j=W(F(),".claude-mem","silent.log");function M(n,t,e=""){let r=new Date().toISOString(),a=((new Error().stack||"").split(`
${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 i=new Date().toISOString().replace("T"," ").substring(0,23),c=O[t].padEnd(5),E=e.padEnd(6),l="";n?.correlationId?l=`[${n.correlationId}] `:n?.sessionId&&(l=`[session-${n.sessionId}] `);let g="";s!=null&&(this.level===0&&typeof s=="object"?g=` `)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),E=a?`${a[1].split("/").pop()}:${a[2]}`:"unknown",u=`[${r}] [${E}] ${n}`;if(t!==void 0)try{u+=` ${JSON.stringify(t)}`}catch(l){u+=` [stringify error: ${l}]`}u+=`
`+JSON.stringify(s,null,2):g=" "+this.formatData(s));let C="";if(n){let{sessionId:et,sdkSessionId:rt,correlationId:nt,...y}=n;Object.keys(y).length>0&&(C=` {${Object.entries(y).map(([k,b])=>`${k}=${b}`).join(", ")}}`)}let d=`[${i}] [${c}] [${E}] ${l}${r}${C}${g}`;t===3?console.error(d):console.log(d)}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`})}},u=new S;import p from"path";import{existsSync as m}from"fs";import{homedir as R}from"os";import{spawnSync as D}from"child_process";import{readFileSync as X,existsSync as B}from"fs";import{join as K}from"path";import{homedir as G}from"os";var j=["bugfix","feature","refactor","discovery","decision","change"],V=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var h=j.join(","),N=V.join(",");var _=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_DATA_DIR:K(G(),".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:h,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:N,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(!B(t))return this.getAllDefaults();let e=X(t,"utf-8"),n=JSON.parse(e).env||{},s={...this.DEFAULTS};for(let i of Object.keys(this.DEFAULTS))n[i]!==void 0&&(s[i]=n[i]);return s}};var a=p.join(R(),".claude","plugins","marketplaces","thedotmack"),Y=500,J=1e3,q=15;function f(){let o=p.join(R(),".claude-mem","settings.json"),t=_.loadFromFile(o);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function U(){try{let o=f();return(await fetch(`http://127.0.0.1:${o}/health`,{signal:AbortSignal.timeout(Y)})).ok}catch{return!1}}async function z(){try{let o=p.join(a,"plugin","scripts","worker-service.cjs");if(!m(o))throw new Error(`Worker script not found at ${o}`);if(process.platform==="win32"){let t=D("powershell.exe",["-NoProfile","-NonInteractive","-Command",`Start-Process -FilePath 'node' -ArgumentList '${o}' -WorkingDirectory '${a}' -WindowStyle Hidden`],{cwd:a,stdio:"pipe",encoding:"utf-8",windowsHide:!0});if(t.status!==0)throw new Error(t.stderr||"PowerShell Start-Process failed")}else{let t=p.join(a,"ecosystem.config.cjs");if(!m(t))throw new Error(`Ecosystem config not found at ${t}`);let e=p.join(a,"node_modules",".bin","pm2"),r=m(e)?e:"pm2",n=D(r,["start",t],{cwd:a,stdio:"pipe",encoding:"utf-8"});if(n.status!==0)throw new Error(n.stderr||"PM2 start failed")}for(let t=0;t<q;t++)if(await new Promise(e=>setTimeout(e,J)),await U())return!0;return!1}catch{return!1}}async function I(){if(await U())return;if(!await z()){let t=f();throw new Error(`Worker service failed to start on port ${t}. `;try{H(j,u)}catch(l){console.error("[silent-debug] Failed to write to log:",l)}return e}var S=V(X(),".claude-mem","settings.json");function h(n,t){try{if(B(S)){let r=JSON.parse(K(S,"utf-8")).env?.[n];if(r!==void 0)return r}}catch(e){M("Failed to load settings file",{error:e,settingsPath:S,key:n})}return process.env[n]||t}var m=(o=>(o[o.DEBUG=0]="DEBUG",o[o.INFO=1]="INFO",o[o.WARN=2]="WARN",o[o.ERROR=3]="ERROR",o[o.SILENT=4]="SILENT",o))(m||{}),O=class{level;useColor;constructor(){let t=h("CLAUDE_MEM_LOG_LEVEL","INFO").toUpperCase();this.level=m[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 s=r.command.length>50?r.command.substring(0,50)+"...":r.command;return`${t}(${s})`}if(t==="Read"&&r.file_path){let s=r.file_path.split("/").pop()||r.file_path;return`${t}(${s})`}if(t==="Edit"&&r.file_path){let s=r.file_path.split("/").pop()||r.file_path;return`${t}(${s})`}if(t==="Write"&&r.file_path){let s=r.file_path.split("/").pop()||r.file_path;return`${t}(${s})`}return t}catch{return t}}log(t,e,r,s,o){if(t<this.level)return;let i=new Date().toISOString().replace("T"," ").substring(0,23),a=m[t].padEnd(5),E=e.padEnd(6),u="";s?.correlationId?u=`[${s.correlationId}] `:s?.sessionId&&(u=`[session-${s.sessionId}] `);let l="";o!=null&&(this.level===0&&typeof o=="object"?l=`
`+JSON.stringify(o,null,2):l=" "+this.formatData(o));let y="";if(s){let{sessionId:it,sdkSessionId:at,correlationId:ct,...A}=s;Object.keys(A).length>0&&(y=` {${Object.entries(A).map(([$,b])=>`${$}=${b}`).join(", ")}}`)}let C=`[${i}] [${a}] [${E}] ${u}${r}${y}${l}`;t===3?console.error(C):console.log(C)}debug(t,e,r,s){this.log(0,t,e,r,s)}info(t,e,r,s){this.log(1,t,e,r,s)}warn(t,e,r,s){this.log(2,t,e,r,s)}error(t,e,r,s){this.log(3,t,e,r,s)}dataIn(t,e,r,s){this.info(t,`\u2192 ${e}`,r,s)}dataOut(t,e,r,s){this.info(t,`\u2190 ${e}`,r,s)}success(t,e,r,s){this.info(t,`\u2713 ${e}`,r,s)}failure(t,e,r,s){this.error(t,`\u2717 ${e}`,r,s)}timing(t,e,r,s){this.info(t,`\u23F1 ${e}`,s,{duration:`${r}ms`})}},c=new O;import f from"path";import{existsSync as T}from"fs";import{homedir as U}from"os";import{spawnSync as R}from"child_process";import{readFileSync as J,existsSync as q}from"fs";import{join as z}from"path";import{homedir as Q}from"os";var G=["bugfix","feature","refactor","discovery","decision","change"],Y=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var D=G.join(","),N=Y.join(",");var g=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_DATA_DIR:z(Q(),".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:D,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:N,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(!q(t))return this.getAllDefaults();let e=J(t,"utf-8"),s=JSON.parse(e).env||{},o={...this.DEFAULTS};for(let i of Object.keys(this.DEFAULTS))s[i]!==void 0&&(o[i]=s[i]);return o}};var p=f.join(U(),".claude","plugins","marketplaces","thedotmack"),Z=500,tt=1e3,et=15;function _(){let n=f.join(U(),".claude-mem","settings.json"),t=g.loadFromFile(n);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function I(){try{let n=_();return(await fetch(`http://127.0.0.1:${n}/health`,{signal:AbortSignal.timeout(Z)})).ok}catch(n){return c.debug("SYSTEM","Worker health check failed",{error:n instanceof Error?n.message:String(n),errorType:n?.constructor?.name}),!1}}async function rt(){try{let n=f.join(p,"plugin","scripts","worker-service.cjs");if(!T(n))throw new Error(`Worker script not found at ${n}`);if(process.platform==="win32"){let t=R("powershell.exe",["-NoProfile","-NonInteractive","-Command",`Start-Process -FilePath 'node' -ArgumentList '${n}' -WorkingDirectory '${p}' -WindowStyle Hidden`],{cwd:p,stdio:"pipe",encoding:"utf-8",windowsHide:!0});if(t.status!==0)throw new Error(t.stderr||"PowerShell Start-Process failed")}else{let t=f.join(p,"ecosystem.config.cjs");if(!T(t))throw new Error(`Ecosystem config not found at ${t}`);let e=f.join(p,"node_modules",".bin","pm2"),r=T(e)?e:"pm2",s=R(r,["start",t],{cwd:p,stdio:"pipe",encoding:"utf-8"});if(s.status!==0)throw new Error(s.stderr||"PM2 start failed")}for(let t=0;t<et;t++)if(await new Promise(e=>setTimeout(e,tt)),await I())return!0;return!1}catch(n){return c.error("SYSTEM","Failed to start worker",{platform:process.platform,workerScript:f.join(p,"plugin","scripts","worker-service.cjs"),error:n instanceof Error?n.message:String(n),marketplaceRoot:p}),!1}}async function k(){if(await I())return;if(!await rt()){let t=_();throw new Error(`Worker service failed to start on port ${t}.
To start manually, run: To start manually, run:
cd ${a} cd ${p}
npx pm2 start ecosystem.config.cjs npx pm2 start ecosystem.config.cjs
If already running, try: npx pm2 restart claude-mem-worker`)}}function Q(o){if(!o||!w(o))return"";try{let t=P(o,"utf-8").trim();if(!t)return"";let e=t.split(` If already running, try: npx pm2 restart claude-mem-worker`)}}function nt(n){if(!n||!P(n))return"";try{let t=x(n,"utf-8").trim();if(!t)return"";let e=t.split(`
`);for(let r=e.length-1;r>=0;r--)try{let n=JSON.parse(e[r]);if(n.type==="user"&&n.message?.content){let s=n.message.content;if(typeof s=="string")return s;if(Array.isArray(s))return s.filter(c=>c.type==="text").map(c=>c.text).join(` `);for(let r=e.length-1;r>=0;r--)try{let s=JSON.parse(e[r]);if(s.type==="user"&&s.message?.content){let o=s.message.content;if(typeof o=="string")return o;if(Array.isArray(o))return o.filter(a=>a.type==="text").map(a=>a.text).join(`
`)}}catch{continue}}catch(t){u.error("HOOK","Failed to read transcript",{transcriptPath:o},t)}return""}function Z(o){if(!o||!w(o))return"";try{let t=P(o,"utf-8").trim();if(!t)return"";let e=t.split(` `)}}catch{continue}}catch(t){c.error("HOOK","Failed to read transcript",{transcriptPath:n},t)}return""}function st(n){if(!n||!P(n))return"";try{let t=x(n,"utf-8").trim();if(!t)return"";let e=t.split(`
`);for(let r=e.length-1;r>=0;r--)try{let n=JSON.parse(e[r]);if(n.type==="assistant"&&n.message?.content){let s="",i=n.message.content;return typeof i=="string"?s=i:Array.isArray(i)&&(s=i.filter(E=>E.type==="text").map(E=>E.text).join(` `);for(let r=e.length-1;r>=0;r--)try{let s=JSON.parse(e[r]);if(s.type==="assistant"&&s.message?.content){let o="",i=s.message.content;return typeof i=="string"?o=i:Array.isArray(i)&&(o=i.filter(E=>E.type==="text").map(E=>E.text).join(`
`)),s=s.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g,""),s=s.replace(/\n{3,}/g,` `)),o=o.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g,""),o=o.replace(/\n{3,}/g,`
`).trim(),s}}catch{continue}}catch(t){u.error("HOOK","Failed to read transcript",{transcriptPath:o},t)}return""}async function tt(o){if(await I(),!o)throw new Error("summaryHook requires input");let{session_id:t}=o,e=f(),r=Q(o.transcript_path||""),n=Z(o.transcript_path||"");u.dataIn("HOOK","Stop: Requesting summary",{workerPort:e,hasLastUserMessage:!!r,hasLastAssistantMessage:!!n});try{let s=await fetch(`http://127.0.0.1:${e}/api/sessions/summarize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({claudeSessionId:t,last_user_message:r,last_assistant_message:n}),signal:AbortSignal.timeout(2e3)});if(!s.ok){let i=await s.text();throw u.failure("HOOK","Failed to generate summary",{status:s.status},i),new Error(`Failed to request summary from worker: ${s.status} ${i}`)}u.debug("HOOK","Summary request sent successfully")}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}finally{fetch(`http://127.0.0.1:${e}/api/processing`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({isProcessing:!1})}).catch(()=>{})}console.log(A("Stop",!0))}var T="";x.on("data",o=>T+=o);x.on("end",async()=>{let o=T?JSON.parse(T):void 0;await tt(o)}); `).trim(),o}}catch{continue}}catch(t){c.error("HOOK","Failed to read transcript",{transcriptPath:n},t)}return""}async function ot(n){if(await k(),!n)throw new Error("summaryHook requires input");let{session_id:t}=n,e=_(),r=nt(n.transcript_path||""),s=st(n.transcript_path||"");c.dataIn("HOOK","Stop: Requesting summary",{workerPort:e,hasLastUserMessage:!!r,hasLastAssistantMessage:!!s});try{let o=await fetch(`http://127.0.0.1:${e}/api/sessions/summarize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({claudeSessionId:t,last_user_message:r,last_assistant_message:s}),signal:AbortSignal.timeout(2e3)});if(!o.ok){let i=await o.text();throw c.failure("HOOK","Failed to generate summary",{status:o.status},i),new Error(`Failed to request summary from worker: ${o.status} ${i}`)}c.debug("HOOK","Summary request sent successfully")}catch(o){throw o.cause?.code==="ECONNREFUSED"||o.name==="TimeoutError"||o.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"):o}finally{fetch(`http://127.0.0.1:${e}/api/processing`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({isProcessing:!1})}).catch(()=>{})}console.log(L("Stop",!0))}var d="";w.on("data",n=>d+=n);w.on("end",async()=>{let n=d?JSON.parse(d):void 0;await ot(n)});
+14 -10
View File
@@ -1,11 +1,15 @@
#!/usr/bin/env node #!/usr/bin/env node
import{join as f,basename as x}from"path";import{homedir as b}from"os";import{existsSync as H}from"fs";import i from"path";import{existsSync as u}from"fs";import{homedir as C}from"os";import{spawnSync as A}from"child_process";import{readFileSync as y,existsSync as w}from"fs";import{join as R}from"path";import{homedir as I}from"os";var h=["bugfix","feature","refactor","discovery","decision","change"],U=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var S=h.join(","),m=U.join(",");var _=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_DATA_DIR:R(I(),".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:S,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:m,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 o=this.get(t);return parseInt(o,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){if(!w(t))return this.getAllDefaults();let o=y(t,"utf-8"),r=JSON.parse(o).env||{},E={...this.DEFAULTS};for(let s of Object.keys(this.DEFAULTS))r[s]!==void 0&&(E[s]=r[s]);return E}};var n=i.join(C(),".claude","plugins","marketplaces","thedotmack"),P=500,k=1e3,W=15;function c(){let e=i.join(C(),".claude-mem","settings.json"),t=_.loadFromFile(e);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function M(){try{let e=c();return(await fetch(`http://127.0.0.1:${e}/health`,{signal:AbortSignal.timeout(P)})).ok}catch{return!1}}async function v(){try{let e=i.join(n,"plugin","scripts","worker-service.cjs");if(!u(e))throw new Error(`Worker script not found at ${e}`);if(process.platform==="win32"){let t=A("powershell.exe",["-NoProfile","-NonInteractive","-Command",`Start-Process -FilePath 'node' -ArgumentList '${e}' -WorkingDirectory '${n}' -WindowStyle Hidden`],{cwd:n,stdio:"pipe",encoding:"utf-8",windowsHide:!0});if(t.status!==0)throw new Error(t.stderr||"PowerShell Start-Process failed")}else{let t=i.join(n,"ecosystem.config.cjs");if(!u(t))throw new Error(`Ecosystem config not found at ${t}`);let o=i.join(n,"node_modules",".bin","pm2"),a=u(o)?o:"pm2",r=A(a,["start",t],{cwd:n,stdio:"pipe",encoding:"utf-8"});if(r.status!==0)throw new Error(r.stderr||"PM2 start failed")}for(let t=0;t<W;t++)if(await new Promise(o=>setTimeout(o,k)),await M())return!0;return!1}catch{return!1}}async function d(){if(await M())return;if(!await v()){let t=c();throw new Error(`Worker service failed to start on port ${t}. import{join as v,basename as et}from"path";import{homedir as rt}from"os";import{existsSync as nt}from"fs";import _ from"path";import{existsSync as h}from"fs";import{homedir as R}from"os";import{spawnSync as U}from"child_process";import{readFileSync as W,existsSync as F}from"fs";import{join as H}from"path";import{homedir as j}from"os";var b=["bugfix","feature","refactor","discovery","decision","change"],x=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var M=b.join(","),D=x.join(",");var m=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_DATA_DIR:H(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:D,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(!F(t))return this.getAllDefaults();let e=W(t,"utf-8"),r=JSON.parse(e).env||{},s={...this.DEFAULTS};for(let E of Object.keys(this.DEFAULTS))r[E]!==void 0&&(s[E]=r[E]);return s}};import{join as Y}from"path";import{homedir as K}from"os";import{existsSync as J,readFileSync as q}from"fs";import{appendFileSync as V}from"fs";import{homedir as X}from"os";import{join as B}from"path";var G=B(X(),".claude-mem","silent.log");function y(o,t,e=""){let n=new Date().toISOString(),l=((new Error().stack||"").split(`
`)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),p=l?`${l[1].split("/").pop()}:${l[2]}`:"unknown",i=`[${n}] [${p}] ${o}`;if(t!==void 0)try{i+=` ${JSON.stringify(t)}`}catch(a){i+=` [stringify error: ${a}]`}i+=`
`;try{V(G,i)}catch(a){console.error("[silent-debug] Failed to write to log:",a)}return e}var S=Y(K(),".claude-mem","settings.json");function N(o,t){try{if(J(S)){let n=JSON.parse(q(S,"utf-8")).env?.[o];if(n!==void 0)return n}}catch(e){y("Failed to load settings file",{error:e,settingsPath:S,key:o})}return process.env[o]||t}var O=(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))(O||{}),d=class{level;useColor;constructor(){let t=N("CLAUDE_MEM_LOG_LEVEL","INFO").toUpperCase();this.level=O[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 n=typeof e=="string"?JSON.parse(e):e;if(t==="Bash"&&n.command){let r=n.command.length>50?n.command.substring(0,50)+"...":n.command;return`${t}(${r})`}if(t==="Read"&&n.file_path){let r=n.file_path.split("/").pop()||n.file_path;return`${t}(${r})`}if(t==="Edit"&&n.file_path){let r=n.file_path.split("/").pop()||n.file_path;return`${t}(${r})`}if(t==="Write"&&n.file_path){let r=n.file_path.split("/").pop()||n.file_path;return`${t}(${r})`}return t}catch{return t}}log(t,e,n,r,s){if(t<this.level)return;let E=new Date().toISOString().replace("T"," ").substring(0,23),l=O[t].padEnd(5),p=e.padEnd(6),i="";r?.correlationId?i=`[${r.correlationId}] `:r?.sessionId&&(i=`[session-${r.sessionId}] `);let a="";s!=null&&(this.level===0&&typeof s=="object"?a=`
`+JSON.stringify(s,null,2):a=" "+this.formatData(s));let u="";if(r){let{sessionId:A,sdkSessionId:k,correlationId:L,...f}=r;Object.keys(f).length>0&&(u=` {${Object.entries(f).map(([$,P])=>`${$}=${P}`).join(", ")}}`)}let g=`[${E}] [${l}] [${p}] ${i}${n}${u}${a}`;t===3?console.error(g):console.log(g)}debug(t,e,n,r){this.log(0,t,e,n,r)}info(t,e,n,r){this.log(1,t,e,n,r)}warn(t,e,n,r){this.log(2,t,e,n,r)}error(t,e,n,r){this.log(3,t,e,n,r)}dataIn(t,e,n,r){this.info(t,`\u2192 ${e}`,n,r)}dataOut(t,e,n,r){this.info(t,`\u2190 ${e}`,n,r)}success(t,e,n,r){this.info(t,`\u2713 ${e}`,n,r)}failure(t,e,n,r){this.error(t,`\u2717 ${e}`,n,r)}timing(t,e,n,r){this.info(t,`\u23F1 ${e}`,r,{duration:`${n}ms`})}},C=new d;var c=_.join(R(),".claude","plugins","marketplaces","thedotmack"),Z=500,z=1e3,Q=15;function T(){let o=_.join(R(),".claude-mem","settings.json"),t=m.loadFromFile(o);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function I(){try{let o=T();return(await fetch(`http://127.0.0.1:${o}/health`,{signal:AbortSignal.timeout(Z)})).ok}catch(o){return C.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=_.join(c,"plugin","scripts","worker-service.cjs");if(!h(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 '${c}' -WindowStyle Hidden`],{cwd:c,stdio:"pipe",encoding:"utf-8",windowsHide:!0});if(t.status!==0)throw new Error(t.stderr||"PowerShell Start-Process failed")}else{let t=_.join(c,"ecosystem.config.cjs");if(!h(t))throw new Error(`Ecosystem config not found at ${t}`);let e=_.join(c,"node_modules",".bin","pm2"),n=h(e)?e:"pm2",r=U(n,["start",t],{cwd:c,stdio:"pipe",encoding:"utf-8"});if(r.status!==0)throw new Error(r.stderr||"PM2 start failed")}for(let t=0;t<Q;t++)if(await new Promise(e=>setTimeout(e,z)),await I())return!0;return!1}catch(o){return C.error("SYSTEM","Failed to start worker",{platform:process.platform,workerScript:_.join(c,"plugin","scripts","worker-service.cjs"),error:o instanceof Error?o.message:String(o),marketplaceRoot:c}),!1}}async function w(){if(await I())return;if(!await tt()){let t=T();throw new Error(`Worker service failed to start on port ${t}.
To start manually, run: To start manually, run:
cd ${n} cd ${c}
npx pm2 start ecosystem.config.cjs npx pm2 start ecosystem.config.cjs
If already running, try: npx pm2 restart claude-mem-worker`)}}var F=f(b(),".claude","plugins","marketplaces","thedotmack"),V=f(F,"node_modules");H(V)||(console.error(` If already running, try: npx pm2 restart claude-mem-worker`)}}var ot=v(rt(),".claude","plugins","marketplaces","thedotmack"),st=v(ot,"node_modules");nt(st)||(console.error(`
--- ---
\u{1F389} Note: This appears under Plugin Hook Error, but it's not an error. That's the only option for \u{1F389} Note: This appears under Plugin Hook Error, but it's not an error. That's the only option for
user messages in Claude Code UI until a better method is provided. user messages in Claude Code UI until a better method is provided.
@@ -23,7 +27,7 @@ Dependencies have been installed in the background. This only happens once.
Thank you for installing Claude-Mem! Thank you for installing Claude-Mem!
This message was not added to your startup context, so you can continue working as normal. This message was not added to your startup context, so you can continue working as normal.
`),process.exit(3));try{await d();let e=c(),t=x(process.cwd()),o=await fetch(`http://127.0.0.1:${e}/api/context/inject?project=${encodeURIComponent(t)}&colors=true`,{method:"GET",signal:AbortSignal.timeout(5e3)});if(!o.ok)throw new Error(`Worker error ${o.status}`);let a=await o.text(),r=new Date,E=new Date("2025-12-06T00:00:00Z"),s=new Date("2025-12-05T05:00:00Z"),l="";r<s&&(l=` `),process.exit(3));try{await w();let o=T(),t=et(process.cwd()),e=await fetch(`http://127.0.0.1:${o}/api/context/inject?project=${encodeURIComponent(t)}&colors=true`,{method:"GET",signal:AbortSignal.timeout(5e3)});if(!e.ok)throw new Error(`Worker error ${e.status}`);let n=await e.text(),r=new Date,s=new Date("2025-12-06T00:00:00Z"),E=new Date("2025-12-05T05:00:00Z"),l="";r<E&&(l=`
\u{1F680} \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 \u{1F680} \u{1F680} \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 \u{1F680}
@@ -33,19 +37,19 @@ This message was not added to your startup context, so you can continue working
\u2B50 Your upvote means the world - thank you! \u2B50 Your upvote means the world - thank you!
\u{1F680} \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 \u{1F680} \u{1F680} \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 \u{1F680}
`);let T="";if(r<E){let g=r.getUTCHours()*60+r.getUTCMinutes(),O=Math.floor((g-300+1440)%1440/60),p=r.getUTCDate(),D=r.getUTCMonth(),N=r.getUTCFullYear()===2025&&D===11&&p>=1&&p<=5,L=O>=17&&O<19;N&&L?T=` `);let p="";if(r<s){let a=r.getUTCHours()*60+r.getUTCMinutes(),u=Math.floor((a-300+1440)%1440/60),g=r.getUTCDate(),A=r.getUTCMonth(),L=r.getUTCFullYear()===2025&&A===11&&g>=1&&g<=5,f=u>=17&&u<19;L&&f?p=`
\u{1F534} LIVE NOW: AMA w/ Dev (@thedotmack) until 7pm EST \u{1F534} LIVE NOW: AMA w/ Dev (@thedotmack) until 7pm EST
`:T=` `:p=`
\u2013 LIVE AMA w/ Dev (@thedotmack) Dec 1st\u20135th, 5pm to 7pm EST \u2013 LIVE AMA w/ Dev (@thedotmack) Dec 1st\u20135th, 5pm to 7pm EST
`}console.error(` `}console.error(`
\u{1F4DD} Claude-Mem Context Loaded \u{1F4DD} Claude-Mem Context Loaded
\u2139\uFE0F Note: This appears as stderr but is informational only \u2139\uFE0F Note: This appears as stderr but is informational only
`+a+` `+n+`
\u{1F4A1} New! Wrap all or part of any message with <private> ... </private> to prevent storing sensitive information in your observation history. \u{1F4A1} New! Wrap all or part of any message with <private> ... </private> to prevent storing sensitive information in your observation history.
\u{1F4AC} Community https://discord.gg/J4wttp9vDu`+l+T+` \u{1F4AC} Community https://discord.gg/J4wttp9vDu`+l+p+`
\u{1F4FA} Watch live in browser http://localhost:${e}/ \u{1F4FA} Watch live in browser http://localhost:${o}/
`)}catch(e){console.error(`\u274C Failed to load context display: ${e}`)}process.exit(3); `)}catch(o){console.error(`\u274C Failed to load context display: ${o}`)}process.exit(3);
File diff suppressed because one or more lines are too long
+3 -2
View File
@@ -1,6 +1,7 @@
import { join } from 'path'; import { join } from 'path';
import { homedir } from 'os'; import { homedir } from 'os';
import { existsSync, readFileSync } from 'fs'; import { existsSync, readFileSync } from 'fs';
import { silentDebug } from '../utils/silent-debug.js';
const SETTINGS_PATH = join(homedir(), '.claude-mem', 'settings.json'); const SETTINGS_PATH = join(homedir(), '.claude-mem', 'settings.json');
@@ -23,8 +24,8 @@ export function loadEarlySetting(key: keyof EarlySettings, defaultValue: string)
const fileValue = data.env?.[key]; const fileValue = data.env?.[key];
if (fileValue !== undefined) return fileValue; if (fileValue !== undefined) return fileValue;
} }
} catch { } catch (error) {
// Fail silently - fall through to env var silentDebug('Failed to load settings file', { error, settingsPath: SETTINGS_PATH, key });
} }
return process.env[key] || defaultValue; return process.env[key] || defaultValue;
+12 -2
View File
@@ -3,6 +3,7 @@ import { existsSync } from "fs";
import { homedir } from "os"; import { homedir } from "os";
import { spawnSync } from "child_process"; import { spawnSync } from "child_process";
import { SettingsDefaultsManager } from "../services/worker/settings/SettingsDefaultsManager.js"; import { SettingsDefaultsManager } from "../services/worker/settings/SettingsDefaultsManager.js";
import { logger } from "../utils/logger.js";
// CRITICAL: Always use marketplace directory for PM2/ecosystem // CRITICAL: Always use marketplace directory for PM2/ecosystem
// This ensures cross-platform compatibility and avoids cache directory confusion // This ensures cross-platform compatibility and avoids cache directory confusion
@@ -34,7 +35,11 @@ async function isWorkerHealthy(): Promise<boolean> {
signal: AbortSignal.timeout(HEALTH_CHECK_TIMEOUT_MS) signal: AbortSignal.timeout(HEALTH_CHECK_TIMEOUT_MS)
}); });
return response.ok; return response.ok;
} catch { } catch (error) {
logger.debug('SYSTEM', 'Worker health check failed', {
error: error instanceof Error ? error.message : String(error),
errorType: error?.constructor?.name
});
return false; return false;
} }
} }
@@ -102,7 +107,12 @@ async function startWorker(): Promise<boolean> {
return false; return false;
} catch (error) { } catch (error) {
// Failed to start worker logger.error('SYSTEM', 'Failed to start worker', {
platform: process.platform,
workerScript: path.join(MARKETPLACE_ROOT, 'plugin', 'scripts', 'worker-service.cjs'),
error: error instanceof Error ? error.message : String(error),
marketplaceRoot: MARKETPLACE_ROOT
});
return false; return false;
} }
} }