Refactor hook timeout settings to use centralized constants

- Introduced a new module `hook-constants.ts` to define timeout constants for various hooks.
- Updated `cleanup-hook.ts`, `context-hook.ts`, `save-hook.ts`, and `summary-hook.ts` to utilize the new `HOOK_TIMEOUTS.DEFAULT` for fetch timeouts instead of hardcoded values.
- Adjusted worker utility timeouts in `worker-utils.ts` to use constants from `hook-constants.ts`, improving maintainability and consistency across the codebase.
This commit is contained in:
Alex Newman
2025-12-09 14:25:53 -05:00
parent e09e64ade5
commit 1fb8df42b6
14 changed files with 128 additions and 110 deletions
+7 -7
View File
@@ -1,13 +1,13 @@
#!/usr/bin/env node #!/usr/bin/env node
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(` import{stdin as A}from"process";import p from"path";import{existsSync as C}from"fs";import{homedir as I}from"os";import{spawnSync as U}from"child_process";import{readFileSync as x,existsSync as H}from"fs";import{join as F}from"path";import{homedir as j}from"os";var W=["bugfix","feature","refactor","discovery","decision","change"],b=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var D=W.join(","),R=b.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:D,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=x(t,"utf-8"),r=JSON.parse(e).env||{},s={...this.DEFAULTS};for(let l of Object.keys(this.DEFAULTS))r[l]!==void 0&&(s[l]=r[l]);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 E(o,t,e=""){let n=new Date().toISOString(),g=((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+=` `)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),u=g?`${g[1].split("/").pop()}:${g[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} `;try{K(B,a)}catch(c){console.error("[silent-debug] Failed to write to log:",c)}return e}var S=G(Y(),".claude-mem","settings.json");function y(o,t){try{if(J(S)){let n=JSON.parse(q(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 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||{}),m=class{level;useColor;constructor(){let t=y("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 _=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=` ${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 l=new Date().toISOString().replace("T"," ").substring(0,23),g=O[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}. `+JSON.stringify(s,null,2):c=" "+this.formatData(s));let L="";if(r){let{sessionId:et,sdkSessionId:rt,correlationId:nt,...h}=r;Object.keys(h).length>0&&(L=` {${Object.entries(h).map(([$,v])=>`${$}=${v}`).join(", ")}}`)}let M=`[${l}] [${g}] [${u}] ${a}${n}${L}${c}`;t===3?console.error(M):console.log(M)}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 m;var _={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*_.WINDOWS_MULTIPLIER):o}var i=p.join(I(),".claude","plugins","marketplaces","thedotmack"),z=N(_.HEALTH_CHECK),Q=_.WORKER_STARTUP_WAIT,Z=_.WORKER_STARTUP_RETRIES;function T(){let o=p.join(I(),".claude-mem","settings.json"),t=f.loadFromFile(o);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function P(){try{let o=T();return(await fetch(`http://127.0.0.1:${o}/health`,{signal:AbortSignal.timeout(z)})).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 tt(){try{let o=p.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=p.join(i,"ecosystem.config.cjs");if(!C(t))throw new Error(`Ecosystem config not found at ${t}`);let e=p.join(i,"node_modules",".bin","pm2"),n=C(e)?e:"pm2",r=U(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 P())return!0;return!1}catch(o){return d.error("SYSTEM","Failed to start worker",{platform:process.platform,workerScript:p.join(i,"plugin","scripts","worker-service.cjs"),error:o instanceof Error?o.message:String(o),marketplaceRoot:i}),!1}}async function k(){if(await P())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 ${i} 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 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(` If already running, try: npx pm2 restart claude-mem-worker`)}}async function w(o){await k(),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(`
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)})} 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=T();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(_.DEFAULT)});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(A.isTTY)w(void 0);else{let o="";A.on("data",t=>o+=t),A.on("end",async()=>{let t=o?JSON.parse(o):void 0;await w(t)})}
+6 -6
View File
@@ -1,12 +1,12 @@
#!/usr/bin/env node #!/usr/bin/env node
import Z from"path";import{stdin as d}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 x,existsSync as b}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 M=P.join(","),h=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: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(!b(t))return this.getAllDefaults();let e=x(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 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 D(o,t,e=""){let r=new Date().toISOString(),E=((new Error().stack||"").split(` 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 h=v.join(","),D=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: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(!H(t))return this.getAllDefaults();let e=b(t,"utf-8"),n=JSON.parse(e).env||{},s={...this.DEFAULTS};for(let a of Object.keys(this.DEFAULTS))n[a]!==void 0&&(s[a]=n[a]);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 R(o,t,e=""){let r=new Date().toISOString(),E=((new Error().stack||"").split(`
`)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),g=E?`${E[1].split("/").pop()}:${E[2]}`:"unknown",a=`[${r}] [${g}] ${o}`;if(t!==void 0)try{a+=` ${JSON.stringify(t)}`}catch(_){a+=` [stringify error: ${_}]`}a+=` `)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),u=E?`${E[1].split("/").pop()}:${E[2]}`:"unknown",c=`[${r}] [${u}] ${o}`;if(t!==void 0)try{c+=` ${JSON.stringify(t)}`}catch(_){c+=` [stringify error: ${_}]`}c+=`
`;try{j(X,a)}catch(_){console.error("[silent-debug] Failed to write to log:",_)}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){D("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} `;try{K(B,c)}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){R("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),g=e.padEnd(6),a="";n?.correlationId?a=`[${n.correlationId}] `:n?.sessionId&&(a=`[session-${n.sessionId}] `);let _="";s!=null&&(this.level===0&&typeof s=="object"?_=` ${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 a=new Date().toISOString().replace("T"," ").substring(0,23),E=S[t].padEnd(5),u=e.padEnd(6),c="";n?.correlationId?c=`[${n.correlationId}] `:n?.sessionId&&(c=`[session-${n.sessionId}] `);let _="";s!=null&&(this.level===0&&typeof s=="object"?_=`
`+JSON.stringify(s,null,2):_=" "+this.formatData(s));let C="";if(n){let{sessionId:et,sdkSessionId:rt,correlationId:nt,...L}=n;Object.keys(L).length>0&&(C=` {${Object.entries(L).map(([$,k])=>`${$}=${k}`).join(", ")}}`)}let A=`[${c}] [${E}] [${g}] ${a}${r}${C}${_}`;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=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=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=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"),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: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}. `+JSON.stringify(s,null,2):_=" "+this.formatData(s));let A="";if(n){let{sessionId:nt,sdkSessionId:ot,correlationId:st,...M}=n;Object.keys(M).length>0&&(A=` {${Object.entries(M).map(([k,x])=>`${k}=${x}`).join(", ")}}`)}let L=`[${a}] [${E}] [${u}] ${c}${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: To start manually, run:
cd ${i} 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 w(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)}`;try{let s=await fetch(n,{signal:AbortSignal.timeout(5e3)});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 tt=process.argv.includes("--colors");if(d.isTTY||tt)w(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 w(t);console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:e}})),process.exit(0)})} 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)})}
File diff suppressed because one or more lines are too long
+7 -7
View File
@@ -1,12 +1,12 @@
#!/usr/bin/env node #!/usr/bin/env node
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 l from"path";import{existsSync as C}from"fs";import{homedir as U}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 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: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||{},s={...this.DEFAULTS};for(let c of Object.keys(this.DEFAULTS))o[c]!==void 0&&(s[c]=o[c]);return s}};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(` import ot from"path";import{stdin as b}from"process";function x(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 m(n,t,e={}){let r=x(n,t,e);return JSON.stringify(r)}import _ from"path";import{existsSync as A}from"fs";import{homedir as w}from"os";import{spawnSync as I}from"child_process";import{readFileSync as F,existsSync as j}from"fs";import{join as V}from"path";import{homedir as K}from"os";var H=["bugfix","feature","refactor","discovery","decision","change"],W=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var y=H.join(","),D=W.join(",");var S=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_DATA_DIR:V(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:y,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(!j(t))return this.getAllDefaults();let e=F(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}};import{join as J}from"path";import{homedir as q}from"os";import{existsSync as z,readFileSync as Q}from"fs";import{appendFileSync as X}from"fs";import{homedir as B}from"os";import{join as G}from"path";var Y=G(B(),".claude-mem","silent.log");function l(n,t,e=""){let r=new Date().toISOString(),p=((new Error().stack||"").split(`
`)[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(u){a+=` [stringify error: ${u}]`}a+=` `)[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(E){a+=` [stringify error: ${E}]`}a+=`
`;try{V(G,a)}catch(u){console.error("[silent-debug] Failed to write to log:",u)}return e}var m=K(Y(),".claude-mem","settings.json");function N(n,t){try{if(J(m)){let r=JSON.parse(q(m,"utf-8")).env?.[n];if(r!==void 0)return r}}catch(e){_("Failed to load settings file",{error:e,settingsPath:m,key:n})}return process.env[n]||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||{}),T=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} `;try{X(Y,a)}catch(E){console.error("[silent-debug] Failed to write to log:",E)}return e}var O=J(q(),".claude-mem","settings.json");function N(n,t){try{if(z(O)){let r=JSON.parse(Q(O,"utf-8")).env?.[n];if(r!==void 0)return r}}catch(e){l("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||{}),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,s){if(t<this.level)return;let c=new Date().toISOString().replace("T"," ").substring(0,23),p=O[t].padEnd(5),i=e.padEnd(6),a="";o?.correlationId?a=`[${o.correlationId}] `:o?.sessionId&&(a=`[session-${o.sessionId}] `);let u="";s!=null&&(this.level===0&&typeof s=="object"?u=` ${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 E="";s!=null&&(this.level===0&&typeof s=="object"?E=`
`+JSON.stringify(s,null,2):u=" "+this.formatData(s));let h="";if(o){let{sessionId:ot,sdkSessionId:nt,correlationId:st,...y}=o;Object.keys(y).length>0&&(h=` {${Object.entries(y).map(([P,b])=>`${P}=${b}`).join(", ")}}`)}let L=`[${c}] [${p}] [${i}] ${a}${r}${h}${u}`;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`})}},d=new T;var E=l.join(U(),".claude","plugins","marketplaces","thedotmack"),z=500,Q=1e3,Z=15;function g(){let n=l.join(U(),".claude-mem","settings.json"),t=f.loadFromFile(n);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function w(){try{let n=g();return(await fetch(`http://127.0.0.1:${n}/health`,{signal:AbortSignal.timeout(z)})).ok}catch(n){return d.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=l.join(E,"plugin","scripts","worker-service.cjs");if(!C(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 '${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=l.join(E,"ecosystem.config.cjs");if(!C(t))throw new Error(`Ecosystem config not found at ${t}`);let e=l.join(E,"node_modules",".bin","pm2"),r=C(e)?e:"pm2",o=R(r,["start",t],{cwd:E,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 w())return!0;return!1}catch(n){return d.error("SYSTEM","Failed to start worker",{platform:process.platform,workerScript:l.join(E,"plugin","scripts","worker-service.cjs"),error:n instanceof Error?n.message:String(n),marketplaceRoot:E}),!1}}async function I(){if(await w())return;if(!await tt()){let t=g();throw new Error(`Worker service failed to start on port ${t}. `+JSON.stringify(s,null,2):E=" "+this.formatData(s));let h="";if(o){let{sessionId:st,sdkSessionId:it,correlationId:at,...R}=o;Object.keys(R).length>0&&(h=` {${Object.entries(R).map(([$,v])=>`${$}=${v}`).join(", ")}}`)}let M=`[${c}] [${p}] [${i}] ${a}${r}${h}${E}`;t===3?console.error(M):console.log(M)}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 f={DEFAULT:5e3,HEALTH_CHECK:1e3,WORKER_STARTUP_WAIT:1e3,WORKER_STARTUP_RETRIES:15,WINDOWS_MULTIPLIER:1.5};function U(n){return process.platform==="win32"?Math.round(n*f.WINDOWS_MULTIPLIER):n}var u=_.join(w(),".claude","plugins","marketplaces","thedotmack"),Z=U(f.HEALTH_CHECK),tt=f.WORKER_STARTUP_WAIT,et=f.WORKER_STARTUP_RETRIES;function g(){let n=_.join(w(),".claude-mem","settings.json"),t=S.loadFromFile(n);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function P(){try{let n=g();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=_.join(u,"plugin","scripts","worker-service.cjs");if(!A(n))throw new Error(`Worker script not found at ${n}`);if(process.platform==="win32"){let t=I("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(!A(t))throw new Error(`Ecosystem config not found at ${t}`);let e=_.join(u,"node_modules",".bin","pm2"),r=A(e)?e:"pm2",o=I(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<et;t++)if(await new Promise(e=>setTimeout(e,tt)),await P())return!0;return!1}catch(n){return C.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 k(){if(await P())return;if(!await rt()){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`)}}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 s=g(),c,p;try{let i=await fetch(`http://127.0.0.1:${s}/api/sessions/init`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({claudeSessionId:t,project:o,prompt:r}),signal:AbortSignal.timeout(5e3)});if(!i.ok){let u=await i.text();throw new Error(`Failed to initialize session: ${i.status} ${u}`)}let a=await i.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(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("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)}); If already running, try: npx pm2 restart claude-mem-worker`)}}async function nt(n){if(await k(),!n)throw new Error("newHook requires input");let{session_id:t,cwd:e,prompt:r}=n;l("[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=ot.basename(e);l("[new-hook] Project extracted",{project:o,project_type:typeof o,project_length:o?.length,is_empty:o==="",cwd_was:e});let s=g(),c,p;try{let i=await fetch(`http://127.0.0.1:${s}/api/sessions/init`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({claudeSessionId:t,project:o,prompt:r}),signal:AbortSignal.timeout(5e3)});if(!i.ok){let E=await i.text();throw new Error(`Failed to initialize session: ${i.status} ${E}`)}let a=await i.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(m("UserPromptSubmit",!0));return}console.error(`[new-hook] Session ${c}, prompt #${p}`)}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(m("UserPromptSubmit",!0))}var L="";b.on("data",n=>L+=n);b.on("end",async()=>{let n=L?JSON.parse(L):void 0;await nt(n)});
+7 -7
View File
@@ -1,12 +1,12 @@
#!/usr/bin/env node #!/usr/bin/env node
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(` import{stdin as b}from"process";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 T(o,t,e={}){let r=H(o,t,e);return JSON.stringify(r)}import{join as j}from"path";import{homedir as V}from"os";import{existsSync as X,readFileSync as B}from"fs";import{appendFileSync as x}from"fs";import{homedir as W}from"os";import{join as F}from"path";var K=F(W(),".claude-mem","silent.log");function h(o,t,e=""){let r=new Date().toISOString(),p=((new Error().stack||"").split(`
`)[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+=` `)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),i=p?`${p[1].split("/").pop()}:${p[2]}`:"unknown",a=`[${r}] [${i}] ${o}`;if(t!==void 0)try{a+=` ${JSON.stringify(t)}`}catch(_){a+=` [stringify error: ${_}]`}a+=`
`;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} `;try{x(K,a)}catch(_){console.error("[silent-debug] Failed to write to log:",_)}return e}var g=j(V(),".claude-mem","settings.json");function y(o,t){try{if(X(g)){let r=JSON.parse(B(g,"utf-8")).env?.[o];if(r!==void 0)return r}}catch(e){h("Failed to load settings file",{error:e,settingsPath:g,key:o})}return process.env[o]||t}var m=(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))(m||{}),d=class{level;useColor;constructor(){let t=y("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 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=` ${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),p=m[t].padEnd(5),i=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):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}. `+JSON.stringify(s,null,2):_=" "+this.formatData(s));let L="";if(n){let{sessionId:st,sdkSessionId:it,correlationId:at,...R}=n;Object.keys(R).length>0&&(L=` {${Object.entries(R).map(([$,v])=>`${$}=${v}`).join(", ")}}`)}let M=`[${c}] [${p}] [${i}] ${a}${r}${L}${_}`;t===3?console.error(M):console.log(M)}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 d;import f from"path";import{existsSync as C}from"fs";import{homedir as P}from"os";import{spawnSync as I}from"child_process";import{readFileSync as J,existsSync as q}from"fs";import{join as Q}from"path";import{homedir as z}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(","),U=Y.join(",");var S=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_DATA_DIR:Q(z(),".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: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 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"),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}};var l={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*l.WINDOWS_MULTIPLIER):o}var E=f.join(P(),".claude","plugins","marketplaces","thedotmack"),Z=N(l.HEALTH_CHECK),tt=l.WORKER_STARTUP_WAIT,et=l.WORKER_STARTUP_RETRIES;function O(){let o=f.join(P(),".claude-mem","settings.json"),t=S.loadFromFile(o);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function w(){try{let o=O();return(await fetch(`http://127.0.0.1:${o}/health`,{signal:AbortSignal.timeout(Z)})).ok}catch(o){return u.debug("SYSTEM","Worker health check failed",{error:o instanceof Error?o.message:String(o),errorType:o?.constructor?.name}),!1}}async function rt(){try{let o=f.join(E,"plugin","scripts","worker-service.cjs");if(!C(o))throw new Error(`Worker script not found at ${o}`);if(process.platform==="win32"){let t=I("powershell.exe",["-NoProfile","-NonInteractive","-Command",`Start-Process -FilePath 'node' -ArgumentList '${o}' -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=f.join(E,"ecosystem.config.cjs");if(!C(t))throw new Error(`Ecosystem config not found at ${t}`);let e=f.join(E,"node_modules",".bin","pm2"),r=C(e)?e:"pm2",n=I(r,["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<et;t++)if(await new Promise(e=>setTimeout(e,tt)),await w())return!0;return!1}catch(o){return u.error("SYSTEM","Failed to start worker",{platform:process.platform,workerScript:f.join(E,"plugin","scripts","worker-service.cjs"),error:o instanceof Error?o.message:String(o),marketplaceRoot:E}),!1}}async function k(){if(await w())return;if(!await rt()){let t=O();throw new Error(`Worker service failed to start on port ${t}.
To start manually, run: To start manually, run:
cd ${u} cd ${E}
npx pm2 start ecosystem.config.cjs npx pm2 start ecosystem.config.cjs
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)}); If already running, try: npx pm2 restart claude-mem-worker`)}}var ot=new Set(["ListMcpResourcesTool","SlashCommand","Skill","TodoWrite","AskUserQuestion"]);async function nt(o){if(await k(),!o)throw new Error("saveHook requires input");let{session_id:t,cwd:e,tool_name:r,tool_input:n,tool_response:s}=o;if(ot.has(r)){console.log(T("PostToolUse",!0));return}let c=O(),p=u.formatTool(r,n);u.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:n,tool_response:s,cwd:e||""}),signal:AbortSignal.timeout(l.DEFAULT)});if(!i.ok){let a=await i.text();throw u.failure("HOOK","Failed to send observation",{status:i.status},a),new Error(`Failed to send observation to worker: ${i.status} ${a}`)}u.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(T("PostToolUse",!0))}var A="";b.on("data",o=>A+=o);b.on("end",async()=>{let o=A?JSON.parse(A):void 0;await nt(o)});
+11 -11
View File
@@ -1,18 +1,18 @@
#!/usr/bin/env node #!/usr/bin/env node
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(` import{stdin as x}from"process";import{readFileSync as H,existsSync as $}from"fs";function W(r,t,e){return r==="PreCompact"?t?{continue:!0,suppressOutput:!0}:{continue:!1,stopReason:e.reason||"Pre-compact operation failed",suppressOutput:!0}:r==="SessionStart"?t&&e.context?{continue:!0,suppressOutput:!0,hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:e.context}}:{continue:!0,suppressOutput:!0}:r==="UserPromptSubmit"||r==="PostToolUse"?{continue:!0,suppressOutput:!0}:r==="Stop"?{continue:!0,suppressOutput:!0}:{continue:t,suppressOutput:!0,...e.reason&&!t?{stopReason:e.reason}:{}}}function M(r,t,e={}){let n=W(r,t,e);return JSON.stringify(n)}import{join as X}from"path";import{homedir as B}from"os";import{existsSync as G,readFileSync as Y}from"fs";import{appendFileSync as F}from"fs";import{homedir as K}from"os";import{join as j}from"path";var V=j(K(),".claude-mem","silent.log");function h(r,t,e=""){let n=new Date().toISOString(),a=((new Error().stack||"").split(`
`)[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+=` `)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),E=a?`${a[1].split("/").pop()}:${a[2]}`:"unknown",u=`[${n}] [${E}] ${r}`;if(t!==void 0)try{u+=` ${JSON.stringify(t)}`}catch(l){u+=` [stringify error: ${l}]`}u+=`
`;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} `;try{F(V,u)}catch(l){console.error("[silent-debug] Failed to write to log:",l)}return e}var O=X(B(),".claude-mem","settings.json");function R(r,t){try{if(G(O)){let n=JSON.parse(Y(O,"utf-8")).env?.[r];if(n!==void 0)return n}}catch(e){h("Failed to load settings file",{error:e,settingsPath:O,key:r})}return process.env[r]||t}var m=(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))(m||{}),T=class{level;useColor;constructor(){let t=R("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=` ${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 o=n.command.length>50?n.command.substring(0,50)+"...":n.command;return`${t}(${o})`}if(t==="Read"&&n.file_path){let o=n.file_path.split("/").pop()||n.file_path;return`${t}(${o})`}if(t==="Edit"&&n.file_path){let o=n.file_path.split("/").pop()||n.file_path;return`${t}(${o})`}if(t==="Write"&&n.file_path){let o=n.file_path.split("/").pop()||n.file_path;return`${t}(${o})`}return t}catch{return t}}log(t,e,n,o,s){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="";o?.correlationId?u=`[${o.correlationId}] `:o?.sessionId&&(u=`[session-${o.sessionId}] `);let l="";s!=null&&(this.level===0&&typeof s=="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}. `+JSON.stringify(s,null,2):l=" "+this.formatData(s));let y="";if(o){let{sessionId:ct,sdkSessionId:pt,correlationId:ut,...L}=o;Object.keys(L).length>0&&(y=` {${Object.entries(L).map(([b,v])=>`${b}=${v}`).join(", ")}}`)}let A=`[${i}] [${a}] [${E}] ${u}${n}${y}${l}`;t===3?console.error(A):console.log(A)}debug(t,e,n,o){this.log(0,t,e,n,o)}info(t,e,n,o){this.log(1,t,e,n,o)}warn(t,e,n,o){this.log(2,t,e,n,o)}error(t,e,n,o){this.log(3,t,e,n,o)}dataIn(t,e,n,o){this.info(t,`\u2192 ${e}`,n,o)}dataOut(t,e,n,o){this.info(t,`\u2190 ${e}`,n,o)}success(t,e,n,o){this.info(t,`\u2713 ${e}`,n,o)}failure(t,e,n,o){this.error(t,`\u2717 ${e}`,n,o)}timing(t,e,n,o){this.info(t,`\u23F1 ${e}`,o,{duration:`${n}ms`})}},c=new T;import _ from"path";import{existsSync as d}from"fs";import{homedir as P}from"os";import{spawnSync as I}from"child_process";import{readFileSync as z,existsSync as Q}from"fs";import{join as Z}from"path";import{homedir as tt}from"os";var J=["bugfix","feature","refactor","discovery","decision","change"],q=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var D=J.join(","),N=q.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(tt(),".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=z(t,"utf-8"),o=JSON.parse(e).env||{},s={...this.DEFAULTS};for(let i of Object.keys(this.DEFAULTS))o[i]!==void 0&&(s[i]=o[i]);return s}};var f={DEFAULT:5e3,HEALTH_CHECK:1e3,WORKER_STARTUP_WAIT:1e3,WORKER_STARTUP_RETRIES:15,WINDOWS_MULTIPLIER:1.5};function U(r){return process.platform==="win32"?Math.round(r*f.WINDOWS_MULTIPLIER):r}var p=_.join(P(),".claude","plugins","marketplaces","thedotmack"),et=U(f.HEALTH_CHECK),rt=f.WORKER_STARTUP_WAIT,nt=f.WORKER_STARTUP_RETRIES;function S(){let r=_.join(P(),".claude-mem","settings.json"),t=g.loadFromFile(r);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function k(){try{let r=S();return(await fetch(`http://127.0.0.1:${r}/health`,{signal:AbortSignal.timeout(et)})).ok}catch(r){return c.debug("SYSTEM","Worker health check failed",{error:r instanceof Error?r.message:String(r),errorType:r?.constructor?.name}),!1}}async function ot(){try{let r=_.join(p,"plugin","scripts","worker-service.cjs");if(!d(r))throw new Error(`Worker script not found at ${r}`);if(process.platform==="win32"){let t=I("powershell.exe",["-NoProfile","-NonInteractive","-Command",`Start-Process -FilePath 'node' -ArgumentList '${r}' -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=_.join(p,"ecosystem.config.cjs");if(!d(t))throw new Error(`Ecosystem config not found at ${t}`);let e=_.join(p,"node_modules",".bin","pm2"),n=d(e)?e:"pm2",o=I(n,["start",t],{cwd:p,stdio:"pipe",encoding:"utf-8"});if(o.status!==0)throw new Error(o.stderr||"PM2 start failed")}for(let t=0;t<nt;t++)if(await new Promise(e=>setTimeout(e,rt)),await k())return!0;return!1}catch(r){return c.error("SYSTEM","Failed to start worker",{platform:process.platform,workerScript:_.join(p,"plugin","scripts","worker-service.cjs"),error:r instanceof Error?r.message:String(r),marketplaceRoot:p}),!1}}async function w(){if(await k())return;if(!await ot()){let t=S();throw new Error(`Worker service failed to start on port ${t}.
To start manually, run: To start manually, run:
cd ${p} 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 nt(n){if(!n||!P(n))return"";try{let t=x(n,"utf-8").trim();if(!t)return"";let e=t.split(` If already running, try: npx pm2 restart claude-mem-worker`)}}function st(r){if(!r||!$(r))return"";try{let t=H(r,"utf-8").trim();if(!t)return"";let e=t.split(`
`);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(` `);for(let n=e.length-1;n>=0;n--)try{let o=JSON.parse(e[n]);if(o.type==="user"&&o.message?.content){let s=o.message.content;if(typeof s=="string")return s;if(Array.isArray(s))return s.filter(a=>a.type==="text").map(a=>a.text).join(`
`)}}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(` `)}}catch{continue}}catch(t){c.error("HOOK","Failed to read transcript",{transcriptPath:r},t)}return""}function it(r){if(!r||!$(r))return"";try{let t=H(r,"utf-8").trim();if(!t)return"";let e=t.split(`
`);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(` `);for(let n=e.length-1;n>=0;n--)try{let o=JSON.parse(e[n]);if(o.type==="assistant"&&o.message?.content){let s="",i=o.message.content;return typeof i=="string"?s=i:Array.isArray(i)&&(s=i.filter(E=>E.type==="text").map(E=>E.text).join(`
`)),o=o.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g,""),o=o.replace(/\n{3,}/g,` `)),s=s.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g,""),s=s.replace(/\n{3,}/g,`
`).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)}); `).trim(),s}}catch{continue}}catch(t){c.error("HOOK","Failed to read transcript",{transcriptPath:r},t)}return""}async function at(r){if(await w(),!r)throw new Error("summaryHook requires input");let{session_id:t}=r,e=S(),n=st(r.transcript_path||""),o=it(r.transcript_path||"");c.dataIn("HOOK","Stop: Requesting summary",{workerPort:e,hasLastUserMessage:!!n,hasLastAssistantMessage:!!o});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:n,last_assistant_message:o}),signal:AbortSignal.timeout(f.DEFAULT)});if(!s.ok){let i=await s.text();throw c.failure("HOOK","Failed to generate summary",{status:s.status},i),new Error(`Failed to request summary from worker: ${s.status} ${i}`)}c.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(M("Stop",!0))}var C="";x.on("data",r=>C+=r);x.on("end",async()=>{let r=C?JSON.parse(C):void 0;await at(r)});
+10 -10
View File
@@ -1,15 +1,15 @@
#!/usr/bin/env node #!/usr/bin/env node
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(` import{join as k,basename as nt}from"path";import{homedir as ot}from"os";import{existsSync as st}from"fs";import p from"path";import{existsSync as h}from"fs";import{homedir as w}from"os";import{spawnSync as I}from"child_process";import{readFileSync as F,existsSync as j}from"fs";import{join as V}from"path";import{homedir as K}from"os";var x=["bugfix","feature","refactor","discovery","decision","change"],H=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var D=x.join(","),y=H.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:V(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:y,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=F(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 J}from"path";import{homedir as q}from"os";import{existsSync as Z,readFileSync as z}from"fs";import{appendFileSync as X}from"fs";import{homedir as B}from"os";import{join as G}from"path";var Y=G(B(),".claude-mem","silent.log");function R(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+=` `)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),_=l?`${l[1].split("/").pop()}:${l[2]}`:"unknown",i=`[${n}] [${_}] ${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} `;try{X(Y,i)}catch(a){console.error("[silent-debug] Failed to write to log:",a)}return e}var O=J(q(),".claude-mem","settings.json");function U(o,t){try{if(Z(O)){let n=JSON.parse(z(O,"utf-8")).env?.[o];if(n!==void 0)return n}}catch(e){R("Failed to load settings file",{error:e,settingsPath:O,key:o})}return process.env[o]||t}var d=(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))(d||{}),C=class{level;useColor;constructor(){let t=U("CLAUDE_MEM_LOG_LEVEL","INFO").toUpperCase();this.level=d[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=` ${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=d[t].padEnd(5),_=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}. `+JSON.stringify(s,null,2):a=" "+this.formatData(s));let u="";if(r){let{sessionId:L,sdkSessionId:$,correlationId:M,...g}=r;Object.keys(g).length>0&&(u=` {${Object.entries(g).map(([W,b])=>`${W}=${b}`).join(", ")}}`)}let T=`[${E}] [${l}] [${_}] ${i}${n}${u}${a}`;t===3?console.error(T):console.log(T)}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`})}},A=new C;var f={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*f.WINDOWS_MULTIPLIER):o}var c=p.join(w(),".claude","plugins","marketplaces","thedotmack"),Q=N(f.HEALTH_CHECK),tt=f.WORKER_STARTUP_WAIT,et=f.WORKER_STARTUP_RETRIES;function S(){let o=p.join(w(),".claude-mem","settings.json"),t=m.loadFromFile(o);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function P(){try{let o=S();return(await fetch(`http://127.0.0.1:${o}/health`,{signal:AbortSignal.timeout(Q)})).ok}catch(o){return A.debug("SYSTEM","Worker health check failed",{error:o instanceof Error?o.message:String(o),errorType:o?.constructor?.name}),!1}}async function rt(){try{let o=p.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=I("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=p.join(c,"ecosystem.config.cjs");if(!h(t))throw new Error(`Ecosystem config not found at ${t}`);let e=p.join(c,"node_modules",".bin","pm2"),n=h(e)?e:"pm2",r=I(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<et;t++)if(await new Promise(e=>setTimeout(e,tt)),await P())return!0;return!1}catch(o){return A.error("SYSTEM","Failed to start worker",{platform:process.platform,workerScript:p.join(c,"plugin","scripts","worker-service.cjs"),error:o instanceof Error?o.message:String(o),marketplaceRoot:c}),!1}}async function v(){if(await P())return;if(!await rt()){let t=S();throw new Error(`Worker service failed to start on port ${t}.
To start manually, run: To start manually, run:
cd ${c} 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 ot=v(rt(),".claude","plugins","marketplaces","thedotmack"),st=v(ot,"node_modules");nt(st)||(console.error(` If already running, try: npx pm2 restart claude-mem-worker`)}}var it=k(ot(),".claude","plugins","marketplaces","thedotmack"),at=k(it,"node_modules");st(at)||(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.
@@ -27,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 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=` `),process.exit(3));try{await v();let o=S(),t=nt(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}
@@ -37,9 +37,9 @@ 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 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=` `);let _="";if(r<s){let a=r.getUTCHours()*60+r.getUTCMinutes(),u=Math.floor((a-300+1440)%1440/60),T=r.getUTCDate(),L=r.getUTCMonth(),M=r.getUTCFullYear()===2025&&L===11&&T>=1&&T<=5,g=u>=17&&u<19;M&&g?_=`
\u{1F534} LIVE NOW: AMA w/ Dev (@thedotmack) until 7pm EST \u{1F534} LIVE NOW: AMA w/ Dev (@thedotmack) until 7pm EST
`: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(`
@@ -50,6 +50,6 @@ This message was not added to your startup context, so you can continue working
\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+p+` \u{1F4AC} Community https://discord.gg/J4wttp9vDu`+l+_+`
\u{1F4FA} Watch live in browser http://localhost:${o}/ \u{1F4FA} Watch live in browser http://localhost:${o}/
`)}catch(o){console.error(`\u274C Failed to load context display: ${o}`)}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
+2 -1
View File
@@ -9,6 +9,7 @@
import { stdin } from 'process'; import { stdin } from 'process';
import { ensureWorkerRunning, getWorkerPort } from '../shared/worker-utils.js'; import { ensureWorkerRunning, getWorkerPort } from '../shared/worker-utils.js';
import { silentDebug } from '../utils/silent-debug.js'; import { silentDebug } from '../utils/silent-debug.js';
import { HOOK_TIMEOUTS } from '../shared/hook-constants.js';
export interface SessionEndInput { export interface SessionEndInput {
session_id: string; session_id: string;
@@ -58,7 +59,7 @@ async function cleanupHook(input?: SessionEndInput): Promise<void> {
claudeSessionId: session_id, claudeSessionId: session_id,
reason reason
}), }),
signal: AbortSignal.timeout(2000) signal: AbortSignal.timeout(HOOK_TIMEOUTS.DEFAULT)
}); });
if (response.ok) { if (response.ok) {
+2 -1
View File
@@ -9,6 +9,7 @@
import path from "path"; import path from "path";
import { stdin } from "process"; import { stdin } from "process";
import { ensureWorkerRunning, getWorkerPort } from "../shared/worker-utils.js"; import { ensureWorkerRunning, getWorkerPort } from "../shared/worker-utils.js";
import { HOOK_TIMEOUTS } from "../shared/hook-constants.js";
export interface SessionStartInput { export interface SessionStartInput {
session_id?: string; session_id?: string;
@@ -30,7 +31,7 @@ async function contextHook(input?: SessionStartInput): Promise<string> {
const url = `http://127.0.0.1:${port}/api/context/inject?project=${encodeURIComponent(project)}`; const url = `http://127.0.0.1:${port}/api/context/inject?project=${encodeURIComponent(project)}`;
try { try {
const response = await fetch(url, { signal: AbortSignal.timeout(5000) }); const response = await fetch(url, { signal: AbortSignal.timeout(HOOK_TIMEOUTS.DEFAULT) });
if (!response.ok) { if (!response.ok) {
const errorText = await response.text(); const errorText = await response.text();
+2 -1
View File
@@ -10,6 +10,7 @@ import { stdin } from 'process';
import { createHookResponse } from './hook-response.js'; import { createHookResponse } from './hook-response.js';
import { logger } from '../utils/logger.js'; import { logger } from '../utils/logger.js';
import { ensureWorkerRunning, getWorkerPort } from '../shared/worker-utils.js'; import { ensureWorkerRunning, getWorkerPort } from '../shared/worker-utils.js';
import { HOOK_TIMEOUTS } from '../shared/hook-constants.js';
export interface PostToolUseInput { export interface PostToolUseInput {
session_id: string; session_id: string;
@@ -67,7 +68,7 @@ async function saveHook(input?: PostToolUseInput): Promise<void> {
tool_response, tool_response,
cwd: cwd || '' cwd: cwd || ''
}), }),
signal: AbortSignal.timeout(2000) signal: AbortSignal.timeout(HOOK_TIMEOUTS.DEFAULT)
}); });
if (!response.ok) { if (!response.ok) {
+2 -1
View File
@@ -14,6 +14,7 @@ import { readFileSync, existsSync } from 'fs';
import { createHookResponse } from './hook-response.js'; import { createHookResponse } from './hook-response.js';
import { logger } from '../utils/logger.js'; import { logger } from '../utils/logger.js';
import { ensureWorkerRunning, getWorkerPort } from '../shared/worker-utils.js'; import { ensureWorkerRunning, getWorkerPort } from '../shared/worker-utils.js';
import { HOOK_TIMEOUTS } from '../shared/hook-constants.js';
export interface StopInput { export interface StopInput {
session_id: string; session_id: string;
@@ -161,7 +162,7 @@ async function summaryHook(input?: StopInput): Promise<void> {
last_user_message: lastUserMessage, last_user_message: lastUserMessage,
last_assistant_message: lastAssistantMessage last_assistant_message: lastAssistantMessage
}), }),
signal: AbortSignal.timeout(2000) signal: AbortSignal.timeout(HOOK_TIMEOUTS.DEFAULT)
}); });
if (!response.ok) { if (!response.ok) {
+13
View File
@@ -0,0 +1,13 @@
export const HOOK_TIMEOUTS = {
DEFAULT: 5000, // Standard HTTP timeout (up from 2000ms)
HEALTH_CHECK: 1000, // Worker health check (up from 500ms)
WORKER_STARTUP_WAIT: 1000,
WORKER_STARTUP_RETRIES: 15,
WINDOWS_MULTIPLIER: 1.5 // Platform-specific adjustment
} as const;
export function getTimeout(baseTimeout: number): number {
return process.platform === 'win32'
? Math.round(baseTimeout * HOOK_TIMEOUTS.WINDOWS_MULTIPLIER)
: baseTimeout;
}
+4 -3
View File
@@ -4,6 +4,7 @@ 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"; import { logger } from "../utils/logger.js";
import { HOOK_TIMEOUTS, getTimeout } from "./hook-constants.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
@@ -11,9 +12,9 @@ const MARKETPLACE_ROOT = path.join(homedir(), '.claude', 'plugins', 'marketplace
// Named constants for health checks // Named constants for health checks
// Windows needs longer timeouts due to startup overhead // Windows needs longer timeouts due to startup overhead
const HEALTH_CHECK_TIMEOUT_MS = 500; const HEALTH_CHECK_TIMEOUT_MS = getTimeout(HOOK_TIMEOUTS.HEALTH_CHECK);
const WORKER_STARTUP_WAIT_MS = 1000; const WORKER_STARTUP_WAIT_MS = HOOK_TIMEOUTS.WORKER_STARTUP_WAIT;
const WORKER_STARTUP_RETRIES = 15; const WORKER_STARTUP_RETRIES = HOOK_TIMEOUTS.WORKER_STARTUP_RETRIES;
/** /**
* Get the worker port number * Get the worker port number