From f07eb17a33e807f6d958ce0423e15cd1df119237 Mon Sep 17 00:00:00 2001 From: Alex Newman Date: Sun, 14 Dec 2025 20:13:48 -0500 Subject: [PATCH] Refactor code structure for improved readability and maintainability --- plugin/scripts/mcp-server.cjs | 2 +- plugin/scripts/worker-service.cjs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/scripts/mcp-server.cjs b/plugin/scripts/mcp-server.cjs index 5166536b..f6987244 100755 --- a/plugin/scripts/mcp-server.cjs +++ b/plugin/scripts/mcp-server.cjs @@ -9,7 +9,7 @@ `)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),u=i?`${i[1].split("/").pop()}:${i[2]}`:"unknown",f=`[${s}] [HAPPY-PATH-ERROR] [${u}] ${a}`;if(e!==void 0)try{f+=` ${JSON.stringify(e)}`}catch(p){f+=` [stringify error: ${p}]`}f+=` `;try{(0,al.appendFileSync)(Mf,f)}catch(p){console.error("[silent-debug] Failed to write to log:",p)}return t}var wa=Ia(require("path"),1),As=require("os");var pt=require("fs"),ll=require("path"),cl=require("os");var Ff=["bugfix","feature","refactor","discovery","decision","change"],Uf=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var ol=Ff.join(","),il=Uf.join(",");var rr=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_WORKER_HOST:"127.0.0.1",CLAUDE_MEM_SKIP_TOOLS:"ListMcpResourcesTool,SlashCommand,Skill,TodoWrite,AskUserQuestion",CLAUDE_MEM_DATA_DIR:(0,ll.join)((0,cl.homedir)(),".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:ol,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:il,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(e){return this.DEFAULTS[e]}static getInt(e){let t=this.get(e);return parseInt(t,10)}static getBool(e){return this.get(e)==="true"}static loadFromFile(e){if(!(0,pt.existsSync)(e))return this.getAllDefaults();let t=(0,pt.readFileSync)(e,"utf-8"),s=JSON.parse(t),r=s;if(s.env&&typeof s.env=="object"){r=s.env;try{(0,pt.writeFileSync)(e,JSON.stringify(r,null,2),"utf-8"),$t.info("SETTINGS","Migrated settings file from nested to flat schema",{settingsPath:e})}catch(l){$t.warn("SETTINGS","Failed to auto-migrate settings file",{settingsPath:e},l)}}let n={...this.DEFAULTS};for(let l of Object.keys(this.DEFAULTS))r[l]!==void 0&&(n[l]=r[l]);return n}};var ws=(n=>(n[n.DEBUG=0]="DEBUG",n[n.INFO=1]="INFO",n[n.WARN=2]="WARN",n[n.ERROR=3]="ERROR",n[n.SILENT=4]="SILENT",n))(ws||{}),Rs=class{level=null;useColor;constructor(){this.useColor=process.stdout.isTTY??!1}getLevel(){if(this.level===null){let e=rr.get("CLAUDE_MEM_LOG_LEVEL").toUpperCase();this.level=ws[e]??1}return this.level}correlationId(e,t){return`obs-${e}-${t}`}sessionId(e){return`session-${e}`}formatData(e){if(e==null)return"";if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="boolean")return e.toString();if(typeof e=="object"){if(e instanceof Error)return this.getLevel()===0?`${e.message} ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let t=Object.keys(e);return t.length===0?"{}":t.length<=3?JSON.stringify(e):`{${t.length} keys: ${t.slice(0,3).join(", ")}...}`}return String(e)}formatTool(e,t){if(!t)return e;try{let s=typeof t=="string"?JSON.parse(t):t;if(e==="Bash"&&s.command){let r=s.command.length>50?s.command.substring(0,50)+"...":s.command;return`${e}(${r})`}if(e==="Read"&&s.file_path){let r=s.file_path.split("/").pop()||s.file_path;return`${e}(${r})`}if(e==="Edit"&&s.file_path){let r=s.file_path.split("/").pop()||s.file_path;return`${e}(${r})`}if(e==="Write"&&s.file_path){let r=s.file_path.split("/").pop()||s.file_path;return`${e}(${r})`}return e}catch{return e}}formatTimestamp(e){let t=e.getFullYear(),s=String(e.getMonth()+1).padStart(2,"0"),r=String(e.getDate()).padStart(2,"0"),n=String(e.getHours()).padStart(2,"0"),l=String(e.getMinutes()).padStart(2,"0"),i=String(e.getSeconds()).padStart(2,"0"),u=String(e.getMilliseconds()).padStart(3,"0");return`${t}-${s}-${r} ${n}:${l}:${i}.${u}`}log(e,t,s,r,n){if(e0&&(m=` {${Object.entries(v).map(([O,x])=>`${O}=${x}`).join(", ")}}`)}let E=`[${l}] [${i}] [${u}] ${f}${s}${m}${p}`;e===3?console.error(E):console.log(E)}debug(e,t,s,r){this.log(0,e,t,s,r)}info(e,t,s,r){this.log(1,e,t,s,r)}warn(e,t,s,r){this.log(2,e,t,s,r)}error(e,t,s,r){this.log(3,e,t,s,r)}dataIn(e,t,s,r){this.info(e,`\u2192 ${t}`,s,r)}dataOut(e,t,s,r){this.info(e,`\u2190 ${t}`,s,r)}success(e,t,s,r){this.info(e,`\u2713 ${t}`,s,r)}failure(e,t,s,r){this.error(e,`\u2717 ${t}`,s,r)}timing(e,t,s,r){this.info(e,`\u23F1 ${t}`,r,{duration:`${s}ms`})}},$t=new Rs;var Ts={DEFAULT:5e3,HEALTH_CHECK:1e3,WORKER_STARTUP_WAIT:1e3,WORKER_STARTUP_RETRIES:15,WINDOWS_MULTIPLIER:1.5};function ul(a){return process.platform==="win32"?Math.round(a*Ts.WINDOWS_MULTIPLIER):a}var xa=require("path");var pl=require("os");var ke=require("path"),dl=require("os");var fl=require("url");var Vf={};function qf(){return typeof __dirname<"u"?__dirname:(0,ke.dirname)((0,fl.fileURLToPath)(Vf.url))}var Fg=qf(),cr=rr.get("CLAUDE_MEM_DATA_DIR"),Os=process.env.CLAUDE_CONFIG_DIR||(0,ke.join)((0,dl.homedir)(),".claude"),Ug=(0,ke.join)(cr,"archives"),qg=(0,ke.join)(cr,"logs"),Vg=(0,ke.join)(cr,"trash"),zg=(0,ke.join)(cr,"backups"),Hg=(0,ke.join)(cr,"settings.json"),Zg=(0,ke.join)(cr,"claude-mem.db"),Bg=(0,ke.join)(cr,"vector-db"),Wg=(0,ke.join)(Os,"settings.json"),Kg=(0,ke.join)(Os,"commands"),Qg=(0,ke.join)(Os,"CLAUDE.md");var ty=(0,xa.join)(cr,"worker.pid"),ay=(0,xa.join)(cr,"logs"),sy=(0,xa.join)((0,pl.homedir)(),".claude","plugins","marketplaces","thedotmack");var hy=wa.default.join((0,As.homedir)(),".claude","plugins","marketplaces","thedotmack"),my=ul(Ts.HEALTH_CHECK),ht=null;function hl(){if(ht!==null)return ht;try{let a=wa.default.join(rr.get("CLAUDE_MEM_DATA_DIR"),"settings.json"),e=rr.loadFromFile(a);return ht=parseInt(e.CLAUDE_MEM_WORKER_PORT,10),ht}catch(a){return $t.debug("SYSTEM","Failed to load port from settings, using default",{error:a}),ht=parseInt(rr.get("CLAUDE_MEM_WORKER_PORT"),10),ht}}function ml(){let a=wa.default.join((0,As.homedir)(),".claude-mem","settings.json");return rr.loadFromFile(a).CLAUDE_MEM_WORKER_HOST}var zf=hl(),Hf=ml(),mt=`http://${Hf}:${zf}`,kt={search:"/api/search",timeline:"/api/timeline",get_recent_context:"/api/context/recent",get_context_timeline:"/api/context/timeline",progressive_ix:"/api/instructions"};async function Ct(a,e){we("[mcp-server] \u2192 Worker API",{endpoint:a,params:e});try{let t=new URLSearchParams;for(let[l,i]of Object.entries(e))i!=null&&t.append(l,String(i));let s=`${mt}${a}?${t}`,r=await fetch(s);if(!r.ok){let l=await r.text();throw new Error(`Worker API error (${r.status}): ${l}`)}let n=await r.json();return we("[mcp-server] \u2190 Worker API success",{endpoint:a}),n}catch(t){return we("[mcp-server] \u2190 Worker API error",{endpoint:a,error:t.message}),{content:[{type:"text",text:`Error calling Worker API: ${t.message}`}],isError:!0}}}async function Ds(a,e){we("[mcp-server] \u2192 Worker API (path)",{endpoint:a,id:e});try{let t=`${mt}${a}/${e}`,s=await fetch(t);if(!s.ok){let n=await s.text();throw new Error(`Worker API error (${s.status}): ${n}`)}let r=await s.json();return we("[mcp-server] \u2190 Worker API success (path)",{endpoint:a,id:e}),{content:[{type:"text",text:JSON.stringify(r,null,2)}]}}catch(t){return we("[mcp-server] \u2190 Worker API error (path)",{endpoint:a,id:e,error:t.message}),{content:[{type:"text",text:`Error calling Worker API: ${t.message}`}],isError:!0}}}async function Zf(a,e){we("[mcp-server] \u2192 Worker API (POST)",{endpoint:a,body:e});try{let t=`${mt}${a}`,s=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!s.ok){let n=await s.text();throw new Error(`Worker API error (${s.status}): ${n}`)}let r=await s.json();return we("[mcp-server] \u2190 Worker API success (POST)",{endpoint:a}),{content:[{type:"text",text:JSON.stringify(r,null,2)}]}}catch(t){return we("[mcp-server] \u2190 Worker API error (POST)",{endpoint:a,error:t.message}),{content:[{type:"text",text:`Error calling Worker API: ${t.message}`}],isError:!0}}}async function Bf(){try{return(await fetch(`${mt}/api/health`)).ok}catch{return!1}}var vl=[{name:"search",description:"Search observations, sessions, and prompts",inputSchema:o.object({query:o.string().optional(),format:o.enum(["index","full"]).default("index"),type:o.enum(["observations","sessions","prompts"]).optional(),obs_type:o.string().optional(),concepts:o.string().optional(),files:o.string().optional(),project:o.string().optional(),dateStart:o.union([o.string(),o.number()]).optional(),dateEnd:o.union([o.string(),o.number()]).optional(),limit:o.number().min(1).max(100).default(20),offset:o.number().min(0).default(0),orderBy:o.enum(["relevance","date_desc","date_asc"]).default("date_desc")}),handler:async a=>{let e=kt.search;return await Ct(e,a)}},{name:"timeline",description:"Get timeline around observation ID or query",inputSchema:o.object({query:o.string().optional(),anchor:o.number().optional(),depth_before:o.number().min(0).max(100).default(10),depth_after:o.number().min(0).max(100).default(10),format:o.enum(["index","full"]).default("index"),type:o.string().optional(),concepts:o.string().optional(),files:o.string().optional(),project:o.string().optional()}),handler:async a=>{let e=kt.timeline;return await Ct(e,a)}},{name:"get_recent_context",description:"Get recent timeline items",inputSchema:o.object({limit:o.number().min(1).max(100).default(30),format:o.enum(["index","full"]).default("index"),type:o.string().optional(),concepts:o.string().optional(),files:o.string().optional(),project:o.string().optional(),dateStart:o.union([o.string(),o.number()]).optional(),dateEnd:o.union([o.string(),o.number()]).optional()}),handler:async a=>{let e=kt.get_recent_context;return await Ct(e,a)}},{name:"get_context_timeline",description:"Get timeline around specific observation",inputSchema:o.object({anchor:o.number(),depth_before:o.number().min(0).max(100).default(10),depth_after:o.number().min(0).max(100).default(10),format:o.enum(["index","full"]).default("index"),type:o.string().optional(),concepts:o.string().optional(),files:o.string().optional(),project:o.string().optional()}),handler:async a=>{let e=kt.get_context_timeline;return await Ct(e,a)}},{name:"progressive_ix",description:"Load parameter docs and usage instructions",inputSchema:o.object({topic:o.enum(["workflow","search_params","examples","all"]).default("all")}),handler:async a=>{let e=kt.progressive_ix;return await Ct(e,a)}},{name:"get_observation",description:"Get observation by ID",inputSchema:o.object({id:o.number()}),handler:async a=>await Ds("/api/observation",a.id)},{name:"get_batch_observations",description:"Get multiple observations by IDs",inputSchema:o.object({ids:o.array(o.number()),orderBy:o.enum(["date_desc","date_asc"]).optional(),limit:o.number().optional(),project:o.string().optional()}),handler:async a=>await Zf("/api/observations/batch",a)},{name:"get_session",description:"Get session summary by ID",inputSchema:o.object({id:o.number()}),handler:async a=>await Ds("/api/session",a.id)},{name:"get_prompt",description:"Get user prompt by ID",inputSchema:o.object({id:o.number()}),handler:async a=>await Ds("/api/prompt",a.id)}],Is=new ga({name:"claude-mem-search-server",version:"1.0.0"},{capabilities:{tools:{}}});Is.setRequestHandler(za,async()=>({tools:vl.map(a=>({name:a.name,description:a.description,inputSchema:xs(a.inputSchema)}))}));Is.setRequestHandler(Ha,async a=>{let e=vl.find(t=>t.name===a.params.name);if(!e)throw new Error(`Unknown tool: ${a.params.name}`);try{return await e.handler(a.params.arguments||{})}catch(t){return{content:[{type:"text",text:`Tool execution failed: ${t.message}`}],isError:!0}}});async function gl(){we("[mcp-server] Shutting down..."),process.exit(0)}process.on("SIGTERM",gl);process.on("SIGINT",gl);async function Wf(){let a=new _a;await Is.connect(a),we("[mcp-server] Claude-mem search server started"),setTimeout(async()=>{await Bf()?we("[mcp-server] Worker available at",mt):(we("[mcp-server] WARNING: Worker not available at",mt),we("[mcp-server] Tools will fail until Worker is started"),we("[mcp-server] Start Worker with: npm run worker:restart"))},0)}Wf().catch(a=>{we("[mcp-server] Fatal error:",a),process.exit(1)}); +`+JSON.stringify(n,null,2):p=" "+this.formatData(n));let m="";if(r){let{sessionId:c,sdkSessionId:y,correlationId:_,...v}=r;Object.keys(v).length>0&&(m=` {${Object.entries(v).map(([O,x])=>`${O}=${x}`).join(", ")}}`)}let E=`[${l}] [${i}] [${u}] ${f}${s}${m}${p}`;e===3?console.error(E):console.log(E)}debug(e,t,s,r){this.log(0,e,t,s,r)}info(e,t,s,r){this.log(1,e,t,s,r)}warn(e,t,s,r){this.log(2,e,t,s,r)}error(e,t,s,r){this.log(3,e,t,s,r)}dataIn(e,t,s,r){this.info(e,`\u2192 ${t}`,s,r)}dataOut(e,t,s,r){this.info(e,`\u2190 ${t}`,s,r)}success(e,t,s,r){this.info(e,`\u2713 ${t}`,s,r)}failure(e,t,s,r){this.error(e,`\u2717 ${t}`,s,r)}timing(e,t,s,r){this.info(e,`\u23F1 ${t}`,r,{duration:`${s}ms`})}},$t=new Rs;var Ts={DEFAULT:5e3,HEALTH_CHECK:1e3,WORKER_STARTUP_WAIT:1e3,WORKER_STARTUP_RETRIES:15,WINDOWS_MULTIPLIER:1.5};function ul(a){return process.platform==="win32"?Math.round(a*Ts.WINDOWS_MULTIPLIER):a}var xa=require("path");var pl=require("os");var ke=require("path"),dl=require("os");var fl=require("url");var Vf={};function qf(){return typeof __dirname<"u"?__dirname:(0,ke.dirname)((0,fl.fileURLToPath)(Vf.url))}var Fg=qf(),cr=rr.get("CLAUDE_MEM_DATA_DIR"),Os=process.env.CLAUDE_CONFIG_DIR||(0,ke.join)((0,dl.homedir)(),".claude"),Ug=(0,ke.join)(cr,"archives"),qg=(0,ke.join)(cr,"logs"),Vg=(0,ke.join)(cr,"trash"),zg=(0,ke.join)(cr,"backups"),Hg=(0,ke.join)(cr,"settings.json"),Zg=(0,ke.join)(cr,"claude-mem.db"),Bg=(0,ke.join)(cr,"vector-db"),Wg=(0,ke.join)(Os,"settings.json"),Kg=(0,ke.join)(Os,"commands"),Qg=(0,ke.join)(Os,"CLAUDE.md");var ty=(0,xa.join)(cr,"worker.pid"),ay=(0,xa.join)(cr,"logs"),sy=(0,xa.join)((0,pl.homedir)(),".claude","plugins","marketplaces","thedotmack");var hy=wa.default.join((0,As.homedir)(),".claude","plugins","marketplaces","thedotmack"),my=ul(Ts.HEALTH_CHECK),ht=null;function hl(){if(ht!==null)return ht;try{let a=wa.default.join(rr.get("CLAUDE_MEM_DATA_DIR"),"settings.json"),e=rr.loadFromFile(a);return ht=parseInt(e.CLAUDE_MEM_WORKER_PORT,10),ht}catch(a){return $t.debug("SYSTEM","Failed to load port from settings, using default",{error:a}),ht=parseInt(rr.get("CLAUDE_MEM_WORKER_PORT"),10),ht}}function ml(){let a=wa.default.join((0,As.homedir)(),".claude-mem","settings.json");return rr.loadFromFile(a).CLAUDE_MEM_WORKER_HOST}var zf=hl(),Hf=ml(),mt=`http://${Hf}:${zf}`,kt={search:"/api/search",timeline:"/api/timeline",get_recent_context:"/api/context/recent",get_context_timeline:"/api/context/timeline",progressive_description:"/api/instructions"};async function Ct(a,e){we("[mcp-server] \u2192 Worker API",{endpoint:a,params:e});try{let t=new URLSearchParams;for(let[l,i]of Object.entries(e))i!=null&&t.append(l,String(i));let s=`${mt}${a}?${t}`,r=await fetch(s);if(!r.ok){let l=await r.text();throw new Error(`Worker API error (${r.status}): ${l}`)}let n=await r.json();return we("[mcp-server] \u2190 Worker API success",{endpoint:a}),n}catch(t){return we("[mcp-server] \u2190 Worker API error",{endpoint:a,error:t.message}),{content:[{type:"text",text:`Error calling Worker API: ${t.message}`}],isError:!0}}}async function Ds(a,e){we("[mcp-server] \u2192 Worker API (path)",{endpoint:a,id:e});try{let t=`${mt}${a}/${e}`,s=await fetch(t);if(!s.ok){let n=await s.text();throw new Error(`Worker API error (${s.status}): ${n}`)}let r=await s.json();return we("[mcp-server] \u2190 Worker API success (path)",{endpoint:a,id:e}),{content:[{type:"text",text:JSON.stringify(r,null,2)}]}}catch(t){return we("[mcp-server] \u2190 Worker API error (path)",{endpoint:a,id:e,error:t.message}),{content:[{type:"text",text:`Error calling Worker API: ${t.message}`}],isError:!0}}}async function Zf(a,e){we("[mcp-server] \u2192 Worker API (POST)",{endpoint:a,body:e});try{let t=`${mt}${a}`,s=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!s.ok){let n=await s.text();throw new Error(`Worker API error (${s.status}): ${n}`)}let r=await s.json();return we("[mcp-server] \u2190 Worker API success (POST)",{endpoint:a}),{content:[{type:"text",text:JSON.stringify(r,null,2)}]}}catch(t){return we("[mcp-server] \u2190 Worker API error (POST)",{endpoint:a,error:t.message}),{content:[{type:"text",text:`Error calling Worker API: ${t.message}`}],isError:!0}}}async function Bf(){try{return(await fetch(`${mt}/api/health`)).ok}catch{return!1}}var vl=[{name:"search",description:"Search memory",inputSchema:o.object({query:o.string().optional(),type:o.enum(["observations","sessions","prompts"]).optional(),obs_type:o.string().optional(),concepts:o.string().optional(),files:o.string().optional(),project:o.string().optional(),dateStart:o.union([o.string(),o.number()]).optional(),dateEnd:o.union([o.string(),o.number()]).optional(),limit:o.number().min(1).max(100).default(20),offset:o.number().min(0).default(0),orderBy:o.enum(["relevance","date_desc","date_asc"]).default("date_desc")}),handler:async a=>{let e=kt.search;return await Ct(e,a)}},{name:"timeline",description:"Timeline context",inputSchema:o.object({query:o.string().optional(),anchor:o.number().optional(),depth_before:o.number().min(0).max(100).default(10),depth_after:o.number().min(0).max(100).default(10),type:o.string().optional(),concepts:o.string().optional(),files:o.string().optional(),project:o.string().optional()}),handler:async a=>{let e=kt.timeline;return await Ct(e,a)}},{name:"get_recent_context",description:"Recent context",inputSchema:o.object({limit:o.number().min(1).max(100).default(30),type:o.string().optional(),concepts:o.string().optional(),files:o.string().optional(),project:o.string().optional(),dateStart:o.union([o.string(),o.number()]).optional(),dateEnd:o.union([o.string(),o.number()]).optional()}),handler:async a=>{let e=kt.get_recent_context;return await Ct(e,a)}},{name:"get_context_timeline",description:"Timeline around ID",inputSchema:o.object({anchor:o.number(),depth_before:o.number().min(0).max(100).default(10),depth_after:o.number().min(0).max(100).default(10),type:o.string().optional(),concepts:o.string().optional(),files:o.string().optional(),project:o.string().optional()}),handler:async a=>{let e=kt.get_context_timeline;return await Ct(e,a)}},{name:"progressive_description",description:"Usage help",inputSchema:o.object({topic:o.enum(["workflow","search_params","examples","all"]).default("all")}),handler:async a=>{let e=kt.progressive_description;return await Ct(e,a)}},{name:"get_observation",description:"Fetch by ID",inputSchema:o.object({id:o.number()}),handler:async a=>await Ds("/api/observation",a.id)},{name:"get_batch_observations",description:"Batch fetch",inputSchema:o.object({ids:o.array(o.number()),orderBy:o.enum(["date_desc","date_asc"]).optional(),limit:o.number().optional(),project:o.string().optional()}),handler:async a=>await Zf("/api/observations/batch",a)},{name:"get_session",description:"Session by ID",inputSchema:o.object({id:o.number()}),handler:async a=>await Ds("/api/session",a.id)},{name:"get_prompt",description:"Prompt by ID",inputSchema:o.object({id:o.number()}),handler:async a=>await Ds("/api/prompt",a.id)}],Is=new ga({name:"claude-mem-search-server",version:"1.0.0"},{capabilities:{tools:{}}});Is.setRequestHandler(za,async()=>({tools:vl.map(a=>({name:a.name,description:a.description,inputSchema:xs(a.inputSchema)}))}));Is.setRequestHandler(Ha,async a=>{let e=vl.find(t=>t.name===a.params.name);if(!e)throw new Error(`Unknown tool: ${a.params.name}`);try{return await e.handler(a.params.arguments||{})}catch(t){return{content:[{type:"text",text:`Tool execution failed: ${t.message}`}],isError:!0}}});async function gl(){we("[mcp-server] Shutting down..."),process.exit(0)}process.on("SIGTERM",gl);process.on("SIGINT",gl);async function Wf(){let a=new _a;await Is.connect(a),we("[mcp-server] Claude-mem search server started"),setTimeout(async()=>{await Bf()?we("[mcp-server] Worker available at",mt):(we("[mcp-server] WARNING: Worker not available at",mt),we("[mcp-server] Tools will fail until Worker is started"),we("[mcp-server] Start Worker with: npm run worker:restart"))},0)}Wf().catch(a=>{we("[mcp-server] Fatal error:",a),process.exit(1)}); /*! Bundled license information: uri-js/dist/es5/uri.all.js: diff --git a/plugin/scripts/worker-service.cjs b/plugin/scripts/worker-service.cjs index 4fc993b5..c8ee0a4e 100755 --- a/plugin/scripts/worker-service.cjs +++ b/plugin/scripts/worker-service.cjs @@ -1026,7 +1026,7 @@ Tips: WHERE project IS NOT NULL GROUP BY project ORDER BY MAX(created_at_epoch) DESC - `).all().map(o=>o.project);t.json({projects:n})});handleGetProcessingStatus=this.wrapHandler((r,t)=>{let s=this.sessionManager.isAnySessionProcessing(),i=this.sessionManager.getTotalActiveWork();t.json({isProcessing:s,queueDepth:i})});handleSetProcessing=this.wrapHandler((r,t)=>{this.workerService.broadcastProcessingStatus();let s=this.sessionManager.isAnySessionProcessing(),i=this.sessionManager.getTotalQueueDepth(),n=this.sessionManager.getActiveSessionCount();t.json({status:"ok",isProcessing:s})});parsePaginationParams(r){let t=parseInt(r.query.offset,10)||0,s=Math.min(parseInt(r.query.limit,10)||20,100),i=r.query.project;return{offset:t,limit:s,project:i}}};var Wl=class extends _r{constructor(r){super();this.searchManager=r}setupRoutes(r){r.get("/api/search",this.handleUnifiedSearch.bind(this)),r.get("/api/timeline",this.handleUnifiedTimeline.bind(this)),r.get("/api/decisions",this.handleDecisions.bind(this)),r.get("/api/changes",this.handleChanges.bind(this)),r.get("/api/how-it-works",this.handleHowItWorks.bind(this)),r.get("/api/search/observations",this.handleSearchObservations.bind(this)),r.get("/api/search/sessions",this.handleSearchSessions.bind(this)),r.get("/api/search/prompts",this.handleSearchPrompts.bind(this)),r.get("/api/search/by-concept",this.handleSearchByConcept.bind(this)),r.get("/api/search/by-file",this.handleSearchByFile.bind(this)),r.get("/api/search/by-type",this.handleSearchByType.bind(this)),r.get("/api/context/recent",this.handleGetRecentContext.bind(this)),r.get("/api/context/timeline",this.handleGetContextTimeline.bind(this)),r.get("/api/context/preview",this.handleContextPreview.bind(this)),r.get("/api/context/inject",this.handleContextInject.bind(this)),r.get("/api/timeline/by-query",this.handleGetTimelineByQuery.bind(this)),r.get("/api/search/help",this.handleSearchHelp.bind(this))}handleUnifiedSearch=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.search(r.query);t.json(s)});handleUnifiedTimeline=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.timeline(r.query);t.json(s)});handleDecisions=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.decisions(r.query);t.json(s)});handleChanges=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.changes(r.query);t.json(s)});handleHowItWorks=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.howItWorks(r.query);t.json(s)});handleSearchObservations=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.searchObservations(r.query);t.json(s)});handleSearchSessions=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.searchSessions(r.query);t.json(s)});handleSearchPrompts=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.searchUserPrompts(r.query);t.json(s)});handleSearchByConcept=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.findByConcept(r.query);t.json(s)});handleSearchByFile=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.findByFile(r.query);t.json(s)});handleSearchByType=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.findByType(r.query);t.json(s)});handleGetRecentContext=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.getRecentContext(r.query);t.json(s)});handleGetContextTimeline=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.getContextTimeline(r.query);t.json(s)});handleContextPreview=this.wrapHandler(async(r,t)=>{let s=r.query.project;if(!s){this.badRequest(t,"Project parameter is required");return}let{generateContext:i}=await Promise.resolve().then(()=>(Vl(),zl)),n=`/preview/${s}`,o=await i({session_id:"preview-"+Date.now(),cwd:n},!0);t.setHeader("Content-Type","text/plain; charset=utf-8"),t.send(o)});handleContextInject=this.wrapHandler(async(r,t)=>{let s=r.query.project,i=r.query.colors==="true";if(!s){this.badRequest(t,"Project parameter is required");return}let{generateContext:n}=await Promise.resolve().then(()=>(Vl(),zl)),o=`/context/${s}`,l=await n({session_id:"context-inject-"+Date.now(),cwd:o},i);t.setHeader("Content-Type","text/plain; charset=utf-8"),t.send(l)});handleGetTimelineByQuery=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.getTimelineByQuery(r.query);t.json(s)});handleSearchHelp=this.wrapHandler((r,t)=>{t.json({title:"Claude-Mem Search API",description:"HTTP API for searching persistent memory",endpoints:[{path:"/api/search/observations",method:"GET",description:"Search observations using full-text search",parameters:{query:"Search query (required)",format:'Response format: "index" or "full" (default: "full")',limit:"Number of results (default: 20)",project:"Filter by project name (optional)"}},{path:"/api/search/sessions",method:"GET",description:"Search session summaries using full-text search",parameters:{query:"Search query (required)",format:'Response format: "index" or "full" (default: "full")',limit:"Number of results (default: 20)"}},{path:"/api/search/prompts",method:"GET",description:"Search user prompts using full-text search",parameters:{query:"Search query (required)",format:'Response format: "index" or "full" (default: "full")',limit:"Number of results (default: 20)",project:"Filter by project name (optional)"}},{path:"/api/search/by-concept",method:"GET",description:"Find observations by concept tag",parameters:{concept:"Concept tag (required): discovery, decision, bugfix, feature, refactor",format:'Response format: "index" or "full" (default: "full")',limit:"Number of results (default: 10)",project:"Filter by project name (optional)"}},{path:"/api/search/by-file",method:"GET",description:"Find observations and sessions by file path",parameters:{filePath:"File path or partial path (required)",format:'Response format: "index" or "full" (default: "full")',limit:"Number of results per type (default: 10)",project:"Filter by project name (optional)"}},{path:"/api/search/by-type",method:"GET",description:"Find observations by type",parameters:{type:"Observation type (required): discovery, decision, bugfix, feature, refactor",format:'Response format: "index" or "full" (default: "full")',limit:"Number of results (default: 10)",project:"Filter by project name (optional)"}},{path:"/api/context/recent",method:"GET",description:"Get recent session context including summaries and observations",parameters:{project:"Project name (default: current directory)",limit:"Number of recent sessions (default: 3)"}},{path:"/api/context/timeline",method:"GET",description:"Get unified timeline around a specific point in time",parameters:{anchor:'Anchor point: observation ID, session ID (e.g., "S123"), or ISO timestamp (required)',depth_before:"Number of records before anchor (default: 10)",depth_after:"Number of records after anchor (default: 10)",project:"Filter by project name (optional)"}},{path:"/api/timeline/by-query",method:"GET",description:"Search for best match, then get timeline around it",parameters:{query:"Search query (required)",mode:'Search mode: "auto", "observations", or "sessions" (default: "auto")',depth_before:"Number of records before match (default: 10)",depth_after:"Number of records after match (default: 10)",project:"Filter by project name (optional)"}},{path:"/api/search/help",method:"GET",description:"Get this help documentation"}],examples:['curl "http://localhost:37777/api/search/observations?query=authentication&format=index&limit=5"','curl "http://localhost:37777/api/search/by-type?type=bugfix&limit=10"','curl "http://localhost:37777/api/context/recent?project=claude-mem&limit=3"','curl "http://localhost:37777/api/context/timeline?anchor=123&depth_before=5&depth_after=5"']})})};var Vs=xt(require("path"),1),Rt=require("fs"),$f=require("os");Ar();yt();var jf=require("child_process"),zs=require("fs"),u1=require("os"),no=require("path");yt();var io=(0,no.join)((0,u1.homedir)(),".claude","plugins","marketplaces","thedotmack"),b5=3e4,p1=12e4,x5=6e4;function Sr(a){return(0,jf.execSync)(`git ${a}`,{cwd:io,encoding:"utf-8",timeout:b5,windowsHide:!0}).trim()}function d1(a,e=x5){return(0,jf.execSync)(a,{cwd:io,encoding:"utf-8",timeout:e,windowsHide:!0}).trim()}function Gl(){let a=(0,no.join)(io,".git");if(!(0,zs.existsSync)(a))return{branch:null,isBeta:!1,isGitRepo:!1,isDirty:!1,canSwitch:!1,error:"Installed plugin is not a git repository"};try{let e=Sr("rev-parse --abbrev-ref HEAD"),t=Sr("status --porcelain").length>0,s=e.startsWith("beta");return{branch:e,isBeta:s,isGitRepo:!0,isDirty:t,canSwitch:!0}}catch(e){return F.error("BRANCH","Failed to get branch info",{},e),{branch:null,isBeta:!1,isGitRepo:!0,isDirty:!1,canSwitch:!1,error:e.message}}}async function f1(a){let e=Gl();if(!e.isGitRepo)return{success:!1,error:"Installed plugin is not a git repository. Please reinstall."};if(e.branch===a)return{success:!0,branch:a,message:`Already on branch ${a}`};try{F.info("BRANCH","Starting branch switch",{from:e.branch,to:a}),F.debug("BRANCH","Discarding local changes"),Sr("checkout -- ."),Sr("clean -fd"),F.debug("BRANCH","Fetching from origin"),Sr("fetch origin"),F.debug("BRANCH","Checking out branch",{branch:a});try{Sr(`checkout ${a}`)}catch{Sr(`checkout -b ${a} origin/${a}`)}F.debug("BRANCH","Pulling latest"),Sr(`pull origin ${a}`);let r=(0,no.join)(io,".install-version");return(0,zs.existsSync)(r)&&(0,zs.unlinkSync)(r),F.debug("BRANCH","Running npm install"),d1("npm install",p1),F.success("BRANCH","Branch switch complete",{branch:a}),{success:!0,branch:a,message:`Switched to ${a}. Worker will restart automatically.`}}catch(r){F.error("BRANCH","Branch switch failed",{targetBranch:a},r);try{e.branch&&Sr(`checkout ${e.branch}`)}catch{}return{success:!1,error:`Branch switch failed: ${r.message}`}}}async function m1(){let a=Gl();if(!a.isGitRepo||!a.branch)return{success:!1,error:"Cannot pull updates: not a git repository"};try{F.info("BRANCH","Pulling updates",{branch:a.branch}),Sr("checkout -- ."),Sr("fetch origin"),Sr(`pull origin ${a.branch}`);let e=(0,no.join)(io,".install-version");return(0,zs.existsSync)(e)&&(0,zs.unlinkSync)(e),d1("npm install",p1),F.success("BRANCH","Updates pulled",{branch:a.branch}),{success:!0,branch:a.branch,message:`Updated ${a.branch}. Worker will restart automatically.`}}catch(e){return F.error("BRANCH","Pull failed",{},e),{success:!1,error:`Pull failed: ${e.message}`}}}Ki();oa();var Zl=class extends _r{constructor(r){super();this.settingsManager=r}setupRoutes(r){r.get("/api/settings",this.handleGetSettings.bind(this)),r.post("/api/settings",this.handleUpdateSettings.bind(this)),r.get("/api/mcp/status",this.handleGetMcpStatus.bind(this)),r.post("/api/mcp/toggle",this.handleToggleMcp.bind(this)),r.get("/api/branch/status",this.handleGetBranchStatus.bind(this)),r.post("/api/branch/switch",this.handleSwitchBranch.bind(this)),r.post("/api/branch/update",this.handleUpdateBranch.bind(this))}handleGetSettings=this.wrapHandler((r,t)=>{let s=Vs.default.join((0,$f.homedir)(),".claude-mem","settings.json");this.ensureSettingsFile(s);let i=lt.loadFromFile(s);t.json(i)});handleUpdateSettings=this.wrapHandler((r,t)=>{if(r.body.CLAUDE_MEM_CONTEXT_OBSERVATIONS){let l=parseInt(r.body.CLAUDE_MEM_CONTEXT_OBSERVATIONS,10);if(isNaN(l)||l<1||l>200){t.status(400).json({success:!1,error:"CLAUDE_MEM_CONTEXT_OBSERVATIONS must be between 1 and 200"});return}}if(r.body.CLAUDE_MEM_WORKER_PORT){let l=parseInt(r.body.CLAUDE_MEM_WORKER_PORT,10);if(isNaN(l)||l<1024||l>65535){t.status(400).json({success:!1,error:"CLAUDE_MEM_WORKER_PORT must be between 1024 and 65535"});return}}if(r.body.CLAUDE_MEM_WORKER_HOST){let l=r.body.CLAUDE_MEM_WORKER_HOST;if(!/^(127\.0\.0\.1|0\.0\.0\.0|localhost|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/.test(l)){t.status(400).json({success:!1,error:"CLAUDE_MEM_WORKER_HOST must be a valid IP address (e.g., 127.0.0.1, 0.0.0.0)"});return}}if(r.body.CLAUDE_MEM_LOG_LEVEL&&!["DEBUG","INFO","WARN","ERROR","SILENT"].includes(r.body.CLAUDE_MEM_LOG_LEVEL.toUpperCase())){t.status(400).json({success:!1,error:"CLAUDE_MEM_LOG_LEVEL must be one of: DEBUG, INFO, WARN, ERROR, SILENT"});return}if(r.body.CLAUDE_MEM_PYTHON_VERSION&&!/^3\.\d{1,2}$/.test(r.body.CLAUDE_MEM_PYTHON_VERSION)){t.status(400).json({success:!1,error:'CLAUDE_MEM_PYTHON_VERSION must be in format "3.X" or "3.XX" (e.g., "3.13")'});return}let s=this.validateContextSettings(r.body);if(!s.valid){t.status(400).json({success:!1,error:s.error});return}let i=Vs.default.join((0,$f.homedir)(),".claude-mem","settings.json");this.ensureSettingsFile(i);let n={};if((0,Rt.existsSync)(i)){let l=(0,Rt.readFileSync)(i,"utf-8");n=JSON.parse(l)}let o=["CLAUDE_MEM_MODEL","CLAUDE_MEM_CONTEXT_OBSERVATIONS","CLAUDE_MEM_WORKER_PORT","CLAUDE_MEM_WORKER_HOST","CLAUDE_MEM_DATA_DIR","CLAUDE_MEM_LOG_LEVEL","CLAUDE_MEM_PYTHON_VERSION","CLAUDE_CODE_PATH","CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS","CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS","CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT","CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT","CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES","CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS","CLAUDE_MEM_CONTEXT_FULL_COUNT","CLAUDE_MEM_CONTEXT_FULL_FIELD","CLAUDE_MEM_CONTEXT_SESSION_COUNT","CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY","CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE"];for(let l of o)r.body[l]!==void 0&&(n[l]=r.body[l]);(0,Rt.writeFileSync)(i,JSON.stringify(n,null,2),"utf-8"),yE(),F.info("WORKER","Settings updated"),t.json({success:!0,message:"Settings updated successfully"})});handleGetMcpStatus=this.wrapHandler((r,t)=>{let s=this.isMcpEnabled();t.json({enabled:s})});handleToggleMcp=this.wrapHandler((r,t)=>{let{enabled:s}=r.body;if(typeof s!="boolean"){this.badRequest(t,"enabled must be a boolean");return}this.toggleMcp(s),t.json({success:!0,enabled:this.isMcpEnabled()})});handleGetBranchStatus=this.wrapHandler((r,t)=>{let s=Gl();t.json(s)});handleSwitchBranch=this.wrapHandler(async(r,t)=>{let{branch:s}=r.body;if(!s){t.status(400).json({success:!1,error:"Missing branch parameter"});return}let i=["main","beta/7.0","feature/bun-executable"];if(!i.includes(s)){t.status(400).json({success:!1,error:`Invalid branch. Allowed: ${i.join(", ")}`});return}F.info("WORKER","Branch switch requested",{branch:s});let n=await f1(s);n.success&&setTimeout(()=>{F.info("WORKER","Restarting worker after branch switch"),process.exit(0)},1e3),t.json(n)});handleUpdateBranch=this.wrapHandler(async(r,t)=>{F.info("WORKER","Branch update requested");let s=await m1();s.success&&setTimeout(()=>{F.info("WORKER","Restarting worker after branch update"),process.exit(0)},1e3),t.json(s)});validateContextSettings(r){let t=["CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS","CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS","CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT","CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT","CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY","CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE"];for(let s of t)if(r[s]&&!["true","false"].includes(r[s]))return{valid:!1,error:`${s} must be "true" or "false"`};if(r.CLAUDE_MEM_CONTEXT_FULL_COUNT){let s=parseInt(r.CLAUDE_MEM_CONTEXT_FULL_COUNT,10);if(isNaN(s)||s<0||s>20)return{valid:!1,error:"CLAUDE_MEM_CONTEXT_FULL_COUNT must be between 0 and 20"}}if(r.CLAUDE_MEM_CONTEXT_SESSION_COUNT){let s=parseInt(r.CLAUDE_MEM_CONTEXT_SESSION_COUNT,10);if(isNaN(s)||s<1||s>50)return{valid:!1,error:"CLAUDE_MEM_CONTEXT_SESSION_COUNT must be between 1 and 50"}}if(r.CLAUDE_MEM_CONTEXT_FULL_FIELD&&!["narrative","facts"].includes(r.CLAUDE_MEM_CONTEXT_FULL_FIELD))return{valid:!1,error:'CLAUDE_MEM_CONTEXT_FULL_FIELD must be "narrative" or "facts"'};if(r.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES){let s=r.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES.split(",").map(i=>i.trim());for(let i of s)if(i&&!jn.includes(i))return{valid:!1,error:`Invalid observation type: ${i}. Valid types: ${jn.join(", ")}`}}if(r.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS){let s=r.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS.split(",").map(i=>i.trim());for(let i of s)if(i&&!$n.includes(i))return{valid:!1,error:`Invalid observation concept: ${i}. Valid concepts: ${$n.join(", ")}`}}return{valid:!0}}isMcpEnabled(){let r=ca(),t=Vs.default.join(r,"plugin",".mcp.json");return(0,Rt.existsSync)(t)}toggleMcp(r){try{let t=ca(),s=Vs.default.join(t,"plugin",".mcp.json"),i=Vs.default.join(t,"plugin",".mcp.json.disabled");r&&(0,Rt.existsSync)(i)?((0,Rt.renameSync)(i,s),F.info("WORKER","MCP search server enabled")):!r&&(0,Rt.existsSync)(s)?((0,Rt.renameSync)(s,i),F.info("WORKER","MCP search server disabled")):F.debug("WORKER","MCP toggle no-op (already in desired state)",{enabled:r})}catch(t){throw F.failure("WORKER","Failed to toggle MCP",{enabled:r},t),t}}ensureSettingsFile(r){if(!(0,Rt.existsSync)(r)){let t=lt.getAllDefaults(),s=Vs.default.dirname(r);(0,Rt.existsSync)(s)||(0,Rt.mkdirSync)(s,{recursive:!0}),(0,Rt.writeFileSync)(r,JSON.stringify(t,null,2),"utf-8"),F.info("SETTINGS","Created settings file with defaults",{settingsPath:r})}}};var h1=(0,b1.promisify)(y1.exec),Kl=class{app;server=null;startTime=Date.now();mcpClient;dbManager;sessionManager;sseBroadcaster;sdkAgent;paginationHelper;settingsManager;sessionEventBroadcaster;viewerRoutes;sessionRoutes;dataRoutes;searchRoutes;settingsRoutes;initializationComplete;resolveInitialization;constructor(){this.app=(0,v1.default)(),this.initializationComplete=new Promise(e=>{this.resolveInitialization=e}),this.dbManager=new pl,this.sessionManager=new dl(this.dbManager),this.sseBroadcaster=new fl,this.sdkAgent=new kl(this.dbManager,this.sessionManager),this.paginationHelper=new Al(this.dbManager),this.settingsManager=new Il(this.dbManager),this.sessionEventBroadcaster=new $l(this.sseBroadcaster,this),this.sessionManager.setOnSessionDeleted(()=>{this.broadcastProcessingStatus()}),this.mcpClient=new An({name:"worker-search-proxy",version:"1.0.0"},{capabilities:{}}),this.viewerRoutes=new Ml(this.sseBroadcaster,this.dbManager,this.sessionManager),this.sessionRoutes=new Fl(this.sessionManager,this.dbManager,this.sdkAgent,this.sessionEventBroadcaster,this),this.dataRoutes=new Ul(this.paginationHelper,this.dbManager,this.sessionManager,this.sseBroadcaster,this,this.startTime),this.searchRoutes=null,this.settingsRoutes=new Zl(this.settingsManager),this.setupMiddleware(),this.setupRoutes()}setupMiddleware(){t1(this.summarizeRequestBody.bind(this)).forEach(r=>this.app.use(r))}setupRoutes(){this.app.get("/api/health",(e,r)=>{r.status(200).json({status:"ok"})}),this.app.get("/api/version",(e,r)=>{try{let{homedir:t}=require("os"),{readFileSync:s}=require("fs"),i=oo.default.join(t(),".claude","plugins","marketplaces","thedotmack"),n=oo.default.join(i,"package.json"),o=JSON.parse(s(n,"utf-8"));r.status(200).json({version:o.version})}catch(t){F.error("SYSTEM","Failed to read version",{packagePath:packageJsonPath},t),r.status(500).json({error:"Failed to read version",path:packageJsonPath})}}),this.app.get("/api/instructions",async(e,r)=>{let t=e.query.topic||"all";try{let s=oo.default.join(__dirname,"../skills/mem-search/SKILL.md"),i=await g1.promises.readFile(s,"utf-8"),n=this.extractInstructionSection(i,t);r.json({content:[{type:"text",text:n}]})}catch(s){F.error("WORKER","Failed to load instructions",{topic:t,skillPath},s),r.status(500).json({content:[{type:"text",text:`Error loading instructions: ${s instanceof Error?s.message:"Unknown error"}`}],isError:!0})}}),this.app.post("/api/admin/restart",async(e,r)=>{r.json({status:"restarting"}),setTimeout(async()=>{await this.shutdown(),process.exit(0)},100)}),this.app.post("/api/admin/shutdown",async(e,r)=>{r.json({status:"shutting_down"}),setTimeout(async()=>{await this.shutdown(),process.exit(0)},100)}),this.viewerRoutes.setupRoutes(this.app),this.sessionRoutes.setupRoutes(this.app),this.dataRoutes.setupRoutes(this.app),this.settingsRoutes.setupRoutes(this.app),this.app.get("/api/context/inject",async(e,r,t)=>{try{let i=new Promise((p,m)=>setTimeout(()=>m(new Error("Initialization timeout")),3e4));if(await Promise.race([this.initializationComplete,i]),!this.searchRoutes){r.status(503).json({error:"Search routes not initialized"});return}let n=e.query.project,o=e.query.colors==="true";if(!n){r.status(400).json({error:"Project parameter is required"});return}let{generateContext:l}=await Promise.resolve().then(()=>(Vl(),zl)),c=`/context/${n}`,u=await l({session_id:"context-inject-"+Date.now(),cwd:c},o);r.setHeader("Content-Type","text/plain; charset=utf-8"),r.send(u)}catch(s){F.error("WORKER","Context inject handler failed",{},s),r.status(500).json({error:s instanceof Error?s.message:"Internal server error"})}})}async cleanupOrphanedProcesses(){try{let{stdout:e}=await h1('ps aux | grep "chroma-mcp" | grep -v grep || true');if(!e.trim()){F.debug("SYSTEM","No orphaned chroma-mcp processes found");return}let r=e.trim().split(` + `).all().map(o=>o.project);t.json({projects:n})});handleGetProcessingStatus=this.wrapHandler((r,t)=>{let s=this.sessionManager.isAnySessionProcessing(),i=this.sessionManager.getTotalActiveWork();t.json({isProcessing:s,queueDepth:i})});handleSetProcessing=this.wrapHandler((r,t)=>{this.workerService.broadcastProcessingStatus();let s=this.sessionManager.isAnySessionProcessing(),i=this.sessionManager.getTotalQueueDepth(),n=this.sessionManager.getActiveSessionCount();t.json({status:"ok",isProcessing:s})});parsePaginationParams(r){let t=parseInt(r.query.offset,10)||0,s=Math.min(parseInt(r.query.limit,10)||20,100),i=r.query.project;return{offset:t,limit:s,project:i}}};var Wl=class extends _r{constructor(r){super();this.searchManager=r}setupRoutes(r){r.get("/api/search",this.handleUnifiedSearch.bind(this)),r.get("/api/timeline",this.handleUnifiedTimeline.bind(this)),r.get("/api/decisions",this.handleDecisions.bind(this)),r.get("/api/changes",this.handleChanges.bind(this)),r.get("/api/how-it-works",this.handleHowItWorks.bind(this)),r.get("/api/search/observations",this.handleSearchObservations.bind(this)),r.get("/api/search/sessions",this.handleSearchSessions.bind(this)),r.get("/api/search/prompts",this.handleSearchPrompts.bind(this)),r.get("/api/search/by-concept",this.handleSearchByConcept.bind(this)),r.get("/api/search/by-file",this.handleSearchByFile.bind(this)),r.get("/api/search/by-type",this.handleSearchByType.bind(this)),r.get("/api/context/recent",this.handleGetRecentContext.bind(this)),r.get("/api/context/timeline",this.handleGetContextTimeline.bind(this)),r.get("/api/context/preview",this.handleContextPreview.bind(this)),r.get("/api/context/inject",this.handleContextInject.bind(this)),r.get("/api/timeline/by-query",this.handleGetTimelineByQuery.bind(this)),r.get("/api/search/help",this.handleSearchHelp.bind(this))}handleUnifiedSearch=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.search(r.query);t.json(s)});handleUnifiedTimeline=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.timeline(r.query);t.json(s)});handleDecisions=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.decisions(r.query);t.json(s)});handleChanges=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.changes(r.query);t.json(s)});handleHowItWorks=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.howItWorks(r.query);t.json(s)});handleSearchObservations=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.searchObservations(r.query);t.json(s)});handleSearchSessions=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.searchSessions(r.query);t.json(s)});handleSearchPrompts=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.searchUserPrompts(r.query);t.json(s)});handleSearchByConcept=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.findByConcept(r.query);t.json(s)});handleSearchByFile=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.findByFile(r.query);t.json(s)});handleSearchByType=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.findByType(r.query);t.json(s)});handleGetRecentContext=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.getRecentContext(r.query);t.json(s)});handleGetContextTimeline=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.getContextTimeline(r.query);t.json(s)});handleContextPreview=this.wrapHandler(async(r,t)=>{let s=r.query.project;if(!s){this.badRequest(t,"Project parameter is required");return}let{generateContext:i}=await Promise.resolve().then(()=>(Vl(),zl)),n=`/preview/${s}`,o=await i({session_id:"preview-"+Date.now(),cwd:n},!0);t.setHeader("Content-Type","text/plain; charset=utf-8"),t.send(o)});handleContextInject=this.wrapHandler(async(r,t)=>{let s=r.query.project,i=r.query.colors==="true";if(!s){this.badRequest(t,"Project parameter is required");return}let{generateContext:n}=await Promise.resolve().then(()=>(Vl(),zl)),o=`/context/${s}`,l=await n({session_id:"context-inject-"+Date.now(),cwd:o},i);t.setHeader("Content-Type","text/plain; charset=utf-8"),t.send(l)});handleGetTimelineByQuery=this.wrapHandler(async(r,t)=>{let s=await this.searchManager.getTimelineByQuery(r.query);t.json(s)});handleSearchHelp=this.wrapHandler((r,t)=>{t.json({title:"Claude-Mem Search API",description:"HTTP API for searching persistent memory",endpoints:[{path:"/api/search/observations",method:"GET",description:"Search observations using full-text search",parameters:{query:"Search query (required)",limit:"Number of results (default: 20)",project:"Filter by project name (optional)"}},{path:"/api/search/sessions",method:"GET",description:"Search session summaries using full-text search",parameters:{query:"Search query (required)",limit:"Number of results (default: 20)"}},{path:"/api/search/prompts",method:"GET",description:"Search user prompts using full-text search",parameters:{query:"Search query (required)",limit:"Number of results (default: 20)",project:"Filter by project name (optional)"}},{path:"/api/search/by-concept",method:"GET",description:"Find observations by concept tag",parameters:{concept:"Concept tag (required): discovery, decision, bugfix, feature, refactor",limit:"Number of results (default: 10)",project:"Filter by project name (optional)"}},{path:"/api/search/by-file",method:"GET",description:"Find observations and sessions by file path",parameters:{filePath:"File path or partial path (required)",limit:"Number of results per type (default: 10)",project:"Filter by project name (optional)"}},{path:"/api/search/by-type",method:"GET",description:"Find observations by type",parameters:{type:"Observation type (required): discovery, decision, bugfix, feature, refactor",limit:"Number of results (default: 10)",project:"Filter by project name (optional)"}},{path:"/api/context/recent",method:"GET",description:"Get recent session context including summaries and observations",parameters:{project:"Project name (default: current directory)",limit:"Number of recent sessions (default: 3)"}},{path:"/api/context/timeline",method:"GET",description:"Get unified timeline around a specific point in time",parameters:{anchor:'Anchor point: observation ID, session ID (e.g., "S123"), or ISO timestamp (required)',depth_before:"Number of records before anchor (default: 10)",depth_after:"Number of records after anchor (default: 10)",project:"Filter by project name (optional)"}},{path:"/api/timeline/by-query",method:"GET",description:"Search for best match, then get timeline around it",parameters:{query:"Search query (required)",mode:'Search mode: "auto", "observations", or "sessions" (default: "auto")',depth_before:"Number of records before match (default: 10)",depth_after:"Number of records after match (default: 10)",project:"Filter by project name (optional)"}},{path:"/api/search/help",method:"GET",description:"Get this help documentation"}],examples:['curl "http://localhost:37777/api/search/observations?query=authentication&limit=5"','curl "http://localhost:37777/api/search/by-type?type=bugfix&limit=10"','curl "http://localhost:37777/api/context/recent?project=claude-mem&limit=3"','curl "http://localhost:37777/api/context/timeline?anchor=123&depth_before=5&depth_after=5"']})})};var Vs=xt(require("path"),1),Rt=require("fs"),$f=require("os");Ar();yt();var jf=require("child_process"),zs=require("fs"),u1=require("os"),no=require("path");yt();var io=(0,no.join)((0,u1.homedir)(),".claude","plugins","marketplaces","thedotmack"),b5=3e4,p1=12e4,x5=6e4;function Sr(a){return(0,jf.execSync)(`git ${a}`,{cwd:io,encoding:"utf-8",timeout:b5,windowsHide:!0}).trim()}function d1(a,e=x5){return(0,jf.execSync)(a,{cwd:io,encoding:"utf-8",timeout:e,windowsHide:!0}).trim()}function Gl(){let a=(0,no.join)(io,".git");if(!(0,zs.existsSync)(a))return{branch:null,isBeta:!1,isGitRepo:!1,isDirty:!1,canSwitch:!1,error:"Installed plugin is not a git repository"};try{let e=Sr("rev-parse --abbrev-ref HEAD"),t=Sr("status --porcelain").length>0,s=e.startsWith("beta");return{branch:e,isBeta:s,isGitRepo:!0,isDirty:t,canSwitch:!0}}catch(e){return F.error("BRANCH","Failed to get branch info",{},e),{branch:null,isBeta:!1,isGitRepo:!0,isDirty:!1,canSwitch:!1,error:e.message}}}async function f1(a){let e=Gl();if(!e.isGitRepo)return{success:!1,error:"Installed plugin is not a git repository. Please reinstall."};if(e.branch===a)return{success:!0,branch:a,message:`Already on branch ${a}`};try{F.info("BRANCH","Starting branch switch",{from:e.branch,to:a}),F.debug("BRANCH","Discarding local changes"),Sr("checkout -- ."),Sr("clean -fd"),F.debug("BRANCH","Fetching from origin"),Sr("fetch origin"),F.debug("BRANCH","Checking out branch",{branch:a});try{Sr(`checkout ${a}`)}catch{Sr(`checkout -b ${a} origin/${a}`)}F.debug("BRANCH","Pulling latest"),Sr(`pull origin ${a}`);let r=(0,no.join)(io,".install-version");return(0,zs.existsSync)(r)&&(0,zs.unlinkSync)(r),F.debug("BRANCH","Running npm install"),d1("npm install",p1),F.success("BRANCH","Branch switch complete",{branch:a}),{success:!0,branch:a,message:`Switched to ${a}. Worker will restart automatically.`}}catch(r){F.error("BRANCH","Branch switch failed",{targetBranch:a},r);try{e.branch&&Sr(`checkout ${e.branch}`)}catch{}return{success:!1,error:`Branch switch failed: ${r.message}`}}}async function m1(){let a=Gl();if(!a.isGitRepo||!a.branch)return{success:!1,error:"Cannot pull updates: not a git repository"};try{F.info("BRANCH","Pulling updates",{branch:a.branch}),Sr("checkout -- ."),Sr("fetch origin"),Sr(`pull origin ${a.branch}`);let e=(0,no.join)(io,".install-version");return(0,zs.existsSync)(e)&&(0,zs.unlinkSync)(e),d1("npm install",p1),F.success("BRANCH","Updates pulled",{branch:a.branch}),{success:!0,branch:a.branch,message:`Updated ${a.branch}. Worker will restart automatically.`}}catch(e){return F.error("BRANCH","Pull failed",{},e),{success:!1,error:`Pull failed: ${e.message}`}}}Ki();oa();var Zl=class extends _r{constructor(r){super();this.settingsManager=r}setupRoutes(r){r.get("/api/settings",this.handleGetSettings.bind(this)),r.post("/api/settings",this.handleUpdateSettings.bind(this)),r.get("/api/mcp/status",this.handleGetMcpStatus.bind(this)),r.post("/api/mcp/toggle",this.handleToggleMcp.bind(this)),r.get("/api/branch/status",this.handleGetBranchStatus.bind(this)),r.post("/api/branch/switch",this.handleSwitchBranch.bind(this)),r.post("/api/branch/update",this.handleUpdateBranch.bind(this))}handleGetSettings=this.wrapHandler((r,t)=>{let s=Vs.default.join((0,$f.homedir)(),".claude-mem","settings.json");this.ensureSettingsFile(s);let i=lt.loadFromFile(s);t.json(i)});handleUpdateSettings=this.wrapHandler((r,t)=>{if(r.body.CLAUDE_MEM_CONTEXT_OBSERVATIONS){let l=parseInt(r.body.CLAUDE_MEM_CONTEXT_OBSERVATIONS,10);if(isNaN(l)||l<1||l>200){t.status(400).json({success:!1,error:"CLAUDE_MEM_CONTEXT_OBSERVATIONS must be between 1 and 200"});return}}if(r.body.CLAUDE_MEM_WORKER_PORT){let l=parseInt(r.body.CLAUDE_MEM_WORKER_PORT,10);if(isNaN(l)||l<1024||l>65535){t.status(400).json({success:!1,error:"CLAUDE_MEM_WORKER_PORT must be between 1024 and 65535"});return}}if(r.body.CLAUDE_MEM_WORKER_HOST){let l=r.body.CLAUDE_MEM_WORKER_HOST;if(!/^(127\.0\.0\.1|0\.0\.0\.0|localhost|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/.test(l)){t.status(400).json({success:!1,error:"CLAUDE_MEM_WORKER_HOST must be a valid IP address (e.g., 127.0.0.1, 0.0.0.0)"});return}}if(r.body.CLAUDE_MEM_LOG_LEVEL&&!["DEBUG","INFO","WARN","ERROR","SILENT"].includes(r.body.CLAUDE_MEM_LOG_LEVEL.toUpperCase())){t.status(400).json({success:!1,error:"CLAUDE_MEM_LOG_LEVEL must be one of: DEBUG, INFO, WARN, ERROR, SILENT"});return}if(r.body.CLAUDE_MEM_PYTHON_VERSION&&!/^3\.\d{1,2}$/.test(r.body.CLAUDE_MEM_PYTHON_VERSION)){t.status(400).json({success:!1,error:'CLAUDE_MEM_PYTHON_VERSION must be in format "3.X" or "3.XX" (e.g., "3.13")'});return}let s=this.validateContextSettings(r.body);if(!s.valid){t.status(400).json({success:!1,error:s.error});return}let i=Vs.default.join((0,$f.homedir)(),".claude-mem","settings.json");this.ensureSettingsFile(i);let n={};if((0,Rt.existsSync)(i)){let l=(0,Rt.readFileSync)(i,"utf-8");n=JSON.parse(l)}let o=["CLAUDE_MEM_MODEL","CLAUDE_MEM_CONTEXT_OBSERVATIONS","CLAUDE_MEM_WORKER_PORT","CLAUDE_MEM_WORKER_HOST","CLAUDE_MEM_DATA_DIR","CLAUDE_MEM_LOG_LEVEL","CLAUDE_MEM_PYTHON_VERSION","CLAUDE_CODE_PATH","CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS","CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS","CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT","CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT","CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES","CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS","CLAUDE_MEM_CONTEXT_FULL_COUNT","CLAUDE_MEM_CONTEXT_FULL_FIELD","CLAUDE_MEM_CONTEXT_SESSION_COUNT","CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY","CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE"];for(let l of o)r.body[l]!==void 0&&(n[l]=r.body[l]);(0,Rt.writeFileSync)(i,JSON.stringify(n,null,2),"utf-8"),yE(),F.info("WORKER","Settings updated"),t.json({success:!0,message:"Settings updated successfully"})});handleGetMcpStatus=this.wrapHandler((r,t)=>{let s=this.isMcpEnabled();t.json({enabled:s})});handleToggleMcp=this.wrapHandler((r,t)=>{let{enabled:s}=r.body;if(typeof s!="boolean"){this.badRequest(t,"enabled must be a boolean");return}this.toggleMcp(s),t.json({success:!0,enabled:this.isMcpEnabled()})});handleGetBranchStatus=this.wrapHandler((r,t)=>{let s=Gl();t.json(s)});handleSwitchBranch=this.wrapHandler(async(r,t)=>{let{branch:s}=r.body;if(!s){t.status(400).json({success:!1,error:"Missing branch parameter"});return}let i=["main","beta/7.0","feature/bun-executable"];if(!i.includes(s)){t.status(400).json({success:!1,error:`Invalid branch. Allowed: ${i.join(", ")}`});return}F.info("WORKER","Branch switch requested",{branch:s});let n=await f1(s);n.success&&setTimeout(()=>{F.info("WORKER","Restarting worker after branch switch"),process.exit(0)},1e3),t.json(n)});handleUpdateBranch=this.wrapHandler(async(r,t)=>{F.info("WORKER","Branch update requested");let s=await m1();s.success&&setTimeout(()=>{F.info("WORKER","Restarting worker after branch update"),process.exit(0)},1e3),t.json(s)});validateContextSettings(r){let t=["CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS","CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS","CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT","CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT","CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY","CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE"];for(let s of t)if(r[s]&&!["true","false"].includes(r[s]))return{valid:!1,error:`${s} must be "true" or "false"`};if(r.CLAUDE_MEM_CONTEXT_FULL_COUNT){let s=parseInt(r.CLAUDE_MEM_CONTEXT_FULL_COUNT,10);if(isNaN(s)||s<0||s>20)return{valid:!1,error:"CLAUDE_MEM_CONTEXT_FULL_COUNT must be between 0 and 20"}}if(r.CLAUDE_MEM_CONTEXT_SESSION_COUNT){let s=parseInt(r.CLAUDE_MEM_CONTEXT_SESSION_COUNT,10);if(isNaN(s)||s<1||s>50)return{valid:!1,error:"CLAUDE_MEM_CONTEXT_SESSION_COUNT must be between 1 and 50"}}if(r.CLAUDE_MEM_CONTEXT_FULL_FIELD&&!["narrative","facts"].includes(r.CLAUDE_MEM_CONTEXT_FULL_FIELD))return{valid:!1,error:'CLAUDE_MEM_CONTEXT_FULL_FIELD must be "narrative" or "facts"'};if(r.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES){let s=r.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES.split(",").map(i=>i.trim());for(let i of s)if(i&&!jn.includes(i))return{valid:!1,error:`Invalid observation type: ${i}. Valid types: ${jn.join(", ")}`}}if(r.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS){let s=r.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS.split(",").map(i=>i.trim());for(let i of s)if(i&&!$n.includes(i))return{valid:!1,error:`Invalid observation concept: ${i}. Valid concepts: ${$n.join(", ")}`}}return{valid:!0}}isMcpEnabled(){let r=ca(),t=Vs.default.join(r,"plugin",".mcp.json");return(0,Rt.existsSync)(t)}toggleMcp(r){try{let t=ca(),s=Vs.default.join(t,"plugin",".mcp.json"),i=Vs.default.join(t,"plugin",".mcp.json.disabled");r&&(0,Rt.existsSync)(i)?((0,Rt.renameSync)(i,s),F.info("WORKER","MCP search server enabled")):!r&&(0,Rt.existsSync)(s)?((0,Rt.renameSync)(s,i),F.info("WORKER","MCP search server disabled")):F.debug("WORKER","MCP toggle no-op (already in desired state)",{enabled:r})}catch(t){throw F.failure("WORKER","Failed to toggle MCP",{enabled:r},t),t}}ensureSettingsFile(r){if(!(0,Rt.existsSync)(r)){let t=lt.getAllDefaults(),s=Vs.default.dirname(r);(0,Rt.existsSync)(s)||(0,Rt.mkdirSync)(s,{recursive:!0}),(0,Rt.writeFileSync)(r,JSON.stringify(t,null,2),"utf-8"),F.info("SETTINGS","Created settings file with defaults",{settingsPath:r})}}};var h1=(0,b1.promisify)(y1.exec),Kl=class{app;server=null;startTime=Date.now();mcpClient;dbManager;sessionManager;sseBroadcaster;sdkAgent;paginationHelper;settingsManager;sessionEventBroadcaster;viewerRoutes;sessionRoutes;dataRoutes;searchRoutes;settingsRoutes;initializationComplete;resolveInitialization;constructor(){this.app=(0,v1.default)(),this.initializationComplete=new Promise(e=>{this.resolveInitialization=e}),this.dbManager=new pl,this.sessionManager=new dl(this.dbManager),this.sseBroadcaster=new fl,this.sdkAgent=new kl(this.dbManager,this.sessionManager),this.paginationHelper=new Al(this.dbManager),this.settingsManager=new Il(this.dbManager),this.sessionEventBroadcaster=new $l(this.sseBroadcaster,this),this.sessionManager.setOnSessionDeleted(()=>{this.broadcastProcessingStatus()}),this.mcpClient=new An({name:"worker-search-proxy",version:"1.0.0"},{capabilities:{}}),this.viewerRoutes=new Ml(this.sseBroadcaster,this.dbManager,this.sessionManager),this.sessionRoutes=new Fl(this.sessionManager,this.dbManager,this.sdkAgent,this.sessionEventBroadcaster,this),this.dataRoutes=new Ul(this.paginationHelper,this.dbManager,this.sessionManager,this.sseBroadcaster,this,this.startTime),this.searchRoutes=null,this.settingsRoutes=new Zl(this.settingsManager),this.setupMiddleware(),this.setupRoutes()}setupMiddleware(){t1(this.summarizeRequestBody.bind(this)).forEach(r=>this.app.use(r))}setupRoutes(){this.app.get("/api/health",(e,r)=>{r.status(200).json({status:"ok"})}),this.app.get("/api/version",(e,r)=>{try{let{homedir:t}=require("os"),{readFileSync:s}=require("fs"),i=oo.default.join(t(),".claude","plugins","marketplaces","thedotmack"),n=oo.default.join(i,"package.json"),o=JSON.parse(s(n,"utf-8"));r.status(200).json({version:o.version})}catch(t){F.error("SYSTEM","Failed to read version",{packagePath:packageJsonPath},t),r.status(500).json({error:"Failed to read version",path:packageJsonPath})}}),this.app.get("/api/instructions",async(e,r)=>{let t=e.query.topic||"all";try{let s=oo.default.join(__dirname,"../skills/mem-search/SKILL.md"),i=await g1.promises.readFile(s,"utf-8"),n=this.extractInstructionSection(i,t);r.json({content:[{type:"text",text:n}]})}catch(s){F.error("WORKER","Failed to load instructions",{topic:t,skillPath},s),r.status(500).json({content:[{type:"text",text:`Error loading instructions: ${s instanceof Error?s.message:"Unknown error"}`}],isError:!0})}}),this.app.post("/api/admin/restart",async(e,r)=>{r.json({status:"restarting"}),setTimeout(async()=>{await this.shutdown(),process.exit(0)},100)}),this.app.post("/api/admin/shutdown",async(e,r)=>{r.json({status:"shutting_down"}),setTimeout(async()=>{await this.shutdown(),process.exit(0)},100)}),this.viewerRoutes.setupRoutes(this.app),this.sessionRoutes.setupRoutes(this.app),this.dataRoutes.setupRoutes(this.app),this.settingsRoutes.setupRoutes(this.app),this.app.get("/api/context/inject",async(e,r,t)=>{try{let i=new Promise((p,m)=>setTimeout(()=>m(new Error("Initialization timeout")),3e4));if(await Promise.race([this.initializationComplete,i]),!this.searchRoutes){r.status(503).json({error:"Search routes not initialized"});return}let n=e.query.project,o=e.query.colors==="true";if(!n){r.status(400).json({error:"Project parameter is required"});return}let{generateContext:l}=await Promise.resolve().then(()=>(Vl(),zl)),c=`/context/${n}`,u=await l({session_id:"context-inject-"+Date.now(),cwd:c},o);r.setHeader("Content-Type","text/plain; charset=utf-8"),r.send(u)}catch(s){F.error("WORKER","Context inject handler failed",{},s),r.status(500).json({error:s instanceof Error?s.message:"Internal server error"})}})}async cleanupOrphanedProcesses(){try{let{stdout:e}=await h1('ps aux | grep "chroma-mcp" | grep -v grep || true');if(!e.trim()){F.debug("SYSTEM","No orphaned chroma-mcp processes found");return}let r=e.trim().split(` `),t=[];for(let s of r){let i=s.trim().split(/\s+/);if(i.length>1){let n=parseInt(i[1],10);isNaN(n)||t.push(n)}}if(t.length===0)return;F.info("SYSTEM","Cleaning up orphaned chroma-mcp processes",{count:t.length,pids:t}),await h1(`kill ${t.join(" ")}`),F.info("SYSTEM","Orphaned processes cleaned up",{count:t.length})}catch(e){F.warn("SYSTEM","Failed to cleanup orphaned processes",{},e)}}async start(){let e=qn(),r=bE();this.server=await new Promise((t,s)=>{let i=this.app.listen(e,r,()=>t(i));i.on("error",s)}),F.info("SYSTEM","Worker started",{host:r,port:e,pid:process.pid}),this.initializeBackground().catch(t=>{F.error("SYSTEM","Background initialization failed",{},t)})}async initializeBackground(){try{await this.cleanupOrphanedProcesses(),await this.dbManager.initialize();let e=new Nl,r=new jl,t=new Dl(this.dbManager.getSessionSearch(),this.dbManager.getSessionStore(),this.dbManager.getChromaSync(),e,r);this.searchRoutes=new Wl(t),this.searchRoutes.setupRoutes(this.app),F.info("WORKER","SearchManager initialized and search routes registered");let s=oo.default.join(__dirname,"mcp-server.cjs"),i=new Nn({command:"node",args:[s],env:process.env});await this.mcpClient.connect(i),F.success("WORKER","Connected to MCP server"),this.resolveInitialization(),F.info("SYSTEM","Background initialization complete")}catch(e){throw F.error("SYSTEM","Background initialization failed",{},e),this.resolveInitialization(),e}}extractInstructionSection(e,r){let t={workflow:this.extractBetween(e,"## The Workflow","## Search Parameters"),search_params:this.extractBetween(e,"## Search Parameters","## Examples"),examples:this.extractBetween(e,"## Examples","## Why This Workflow"),all:e};return t[r]||t.all}extractBetween(e,r,t){let s=e.indexOf(r),i=e.indexOf(t);return s===-1?e:i===-1?e.substring(s):e.substring(s,i).trim()}async shutdown(){if(await this.sessionManager.shutdownAll(),this.mcpClient)try{await this.mcpClient.close(),F.info("SYSTEM","MCP client closed")}catch(e){F.error("SYSTEM","Failed to close MCP client",{},e)}this.server&&await new Promise((e,r)=>{this.server.close(t=>t?r(t):e())}),await this.dbManager.close(),F.info("SYSTEM","Worker shutdown complete")}summarizeRequestBody(e,r,t){return r1(e,r,t)}broadcastProcessingStatus(){let e=this.sessionManager.isAnySessionProcessing(),r=this.sessionManager.getTotalActiveWork(),t=this.sessionManager.getActiveSessionCount();F.info("WORKER","Broadcasting processing status",{isProcessing:e,queueDepth:r,activeSessions:t}),this.sseBroadcaster.broadcast({type:"processing_status",isProcessing:e,queueDepth:r})}};if(require.main===module||!module.parent){let a=new Kl;process.on("SIGTERM",async()=>{F.info("SYSTEM","Received SIGTERM, shutting down gracefully"),await a.shutdown(),process.exit(0)}),process.on("SIGINT",async()=>{F.info("SYSTEM","Received SIGINT, shutting down gracefully"),await a.shutdown(),process.exit(0)}),a.start().catch(e=>{F.failure("SYSTEM","Worker failed to start",{},e),process.exit(1)})}0&&(module.exports={WorkerService}); /*! Bundled license information: