a3d6bfc7dd
- Added detailed entries for test activities across various modules including `export-types.test.ts`, `server.test.ts`, `smart-install.test.ts`, and others. - Documented significant changes in test coverage, cleanup efforts, and regression tests. - Enhanced the `plans` documentation with recent implementation strategies and PR follow-ups. - Introduced a comprehensive report on Windows platform challenges and solutions, detailing issues like zombie ports, console popups, and process management. - Established a new `ProcessManager` architecture to address Windows-specific issues and improve reliability. - Updated integration tests to reflect recent changes and ensure comprehensive coverage.
20 lines
10 KiB
JavaScript
Executable File
20 lines
10 KiB
JavaScript
Executable File
#!/usr/bin/env bun
|
|
import{stdin as A}from"process";import C from"path";import{homedir as z}from"os";import{readFileSync as q}from"fs";import{appendFileSync as j,existsSync as h,mkdirSync as H,readFileSync as G}from"fs";import{join as T}from"path";import{homedir as K}from"os";var O=(i=>(i[i.DEBUG=0]="DEBUG",i[i.INFO=1]="INFO",i[i.WARN=2]="WARN",i[i.ERROR=3]="ERROR",i[i.SILENT=4]="SILENT",i))(O||{}),U=T(K(),".claude-mem"),M=class{level=null;useColor;logFilePath=null;logFileInitialized=!1;constructor(){this.useColor=process.stdout.isTTY??!1}ensureLogFileInitialized(){if(!this.logFileInitialized){this.logFileInitialized=!0;try{let t=T(U,"logs");h(t)||H(t,{recursive:!0});let r=new Date().toISOString().split("T")[0];this.logFilePath=T(t,`claude-mem-${r}.log`)}catch(t){console.error("[LOGGER] Failed to initialize log file:",t),this.logFilePath=null}}}getLevel(){if(this.level===null)try{let t=T(U,"settings.json");if(h(t)){let r=G(t,"utf-8"),n=(JSON.parse(r).CLAUDE_MEM_LOG_LEVEL||"INFO").toUpperCase();this.level=O[n]??1}else this.level=1}catch{this.level=1}return this.level}correlationId(t,r){return`obs-${t}-${r}`}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.getLevel()===0?`${t.message}
|
|
${t.stack}`:t.message;if(Array.isArray(t))return`[${t.length} items]`;let r=Object.keys(t);return r.length===0?"{}":r.length<=3?JSON.stringify(t):`{${r.length} keys: ${r.slice(0,3).join(", ")}...}`}return String(t)}formatTool(t,r){if(!r)return t;let e=r;if(typeof r=="string")try{e=JSON.parse(r)}catch{e=r}if(t==="Bash"&&e.command)return`${t}(${e.command})`;if(e.file_path)return`${t}(${e.file_path})`;if(e.notebook_path)return`${t}(${e.notebook_path})`;if(t==="Glob"&&e.pattern)return`${t}(${e.pattern})`;if(t==="Grep"&&e.pattern)return`${t}(${e.pattern})`;if(e.url)return`${t}(${e.url})`;if(e.query)return`${t}(${e.query})`;if(t==="Task"){if(e.subagent_type)return`${t}(${e.subagent_type})`;if(e.description)return`${t}(${e.description})`}return t==="Skill"&&e.skill?`${t}(${e.skill})`:t==="LSP"&&e.operation?`${t}(${e.operation})`:t}formatTimestamp(t){let r=t.getFullYear(),e=String(t.getMonth()+1).padStart(2,"0"),n=String(t.getDate()).padStart(2,"0"),i=String(t.getHours()).padStart(2,"0"),s=String(t.getMinutes()).padStart(2,"0"),E=String(t.getSeconds()).padStart(2,"0"),c=String(t.getMilliseconds()).padStart(3,"0");return`${r}-${e}-${n} ${i}:${s}:${E}.${c}`}log(t,r,e,n,i){if(t<this.getLevel())return;this.ensureLogFileInitialized();let s=this.formatTimestamp(new Date),E=O[t].padEnd(5),c=r.padEnd(6),a="";n?.correlationId?a=`[${n.correlationId}] `:n?.sessionId&&(a=`[session-${n.sessionId}] `);let _="";i!=null&&(i instanceof Error?_=this.getLevel()===0?`
|
|
${i.message}
|
|
${i.stack}`:` ${i.message}`:this.getLevel()===0&&typeof i=="object"?_=`
|
|
`+JSON.stringify(i,null,2):_=" "+this.formatData(i));let p="";if(n){let{sessionId:d,memorySessionId:at,correlationId:lt,...R}=n;Object.keys(R).length>0&&(p=` {${Object.entries(R).map(([x,b])=>`${x}=${b}`).join(", ")}}`)}let D=`[${s}] [${E}] [${c}] ${a}${e}${p}${_}`;if(this.logFilePath)try{j(this.logFilePath,D+`
|
|
`,"utf8")}catch(d){process.stderr.write(`[LOGGER] Failed to write to log file: ${d}
|
|
`)}else process.stderr.write(D+`
|
|
`)}debug(t,r,e,n){this.log(0,t,r,e,n)}info(t,r,e,n){this.log(1,t,r,e,n)}warn(t,r,e,n){this.log(2,t,r,e,n)}error(t,r,e,n){this.log(3,t,r,e,n)}dataIn(t,r,e,n){this.info(t,`\u2192 ${r}`,e,n)}dataOut(t,r,e,n){this.info(t,`\u2190 ${r}`,e,n)}success(t,r,e,n){this.info(t,`\u2713 ${r}`,e,n)}failure(t,r,e,n){this.error(t,`\u2717 ${r}`,e,n)}timing(t,r,e,n){this.info(t,`\u23F1 ${r}`,n,{duration:`${e}ms`})}happyPathError(t,r,e,n,i=""){let a=((new Error().stack||"").split(`
|
|
`)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),_=a?`${a[1].split("/").pop()}:${a[2]}`:"unknown",p={...e,location:_};return this.warn(t,`[HAPPY-PATH] ${r}`,p,n),i}},l=new M;var m={DEFAULT:3e5,HEALTH_CHECK:3e4,WORKER_STARTUP_WAIT:1e3,WORKER_STARTUP_RETRIES:300,PRE_RESTART_SETTLE_DELAY:2e3,WINDOWS_MULTIPLIER:1.5};function N(o){return process.platform==="win32"?Math.round(o*m.WINDOWS_MULTIPLIER):o}import{readFileSync as X,writeFileSync as P,existsSync as y,mkdirSync as V}from"fs";import{join as Y,dirname as B}from"path";import{homedir as J}from"os";var I="bugfix,feature,refactor,discovery,decision,change",k="how-it-works,why-it-exists,what-changed,problem-solution,gotcha,pattern,trade-off";var g=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-sonnet-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_PROVIDER:"claude",CLAUDE_MEM_GEMINI_API_KEY:"",CLAUDE_MEM_GEMINI_MODEL:"gemini-2.5-flash-lite",CLAUDE_MEM_GEMINI_RATE_LIMITING_ENABLED:"true",CLAUDE_MEM_OPENROUTER_API_KEY:"",CLAUDE_MEM_OPENROUTER_MODEL:"xiaomi/mimo-v2-flash:free",CLAUDE_MEM_OPENROUTER_SITE_URL:"",CLAUDE_MEM_OPENROUTER_APP_NAME:"claude-mem",CLAUDE_MEM_OPENROUTER_MAX_CONTEXT_MESSAGES:"20",CLAUDE_MEM_OPENROUTER_MAX_TOKENS:"100000",CLAUDE_MEM_DATA_DIR:Y(J(),".claude-mem"),CLAUDE_MEM_LOG_LEVEL:"INFO",CLAUDE_MEM_PYTHON_VERSION:"3.13",CLAUDE_CODE_PATH:"",CLAUDE_MEM_MODE:"code",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:I,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:k,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 this.DEFAULTS[t]}static getInt(t){let r=this.get(t);return parseInt(r,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){try{if(!y(t)){let s=this.getAllDefaults();try{let E=B(t);y(E)||V(E,{recursive:!0}),P(t,JSON.stringify(s,null,2),"utf-8"),console.log("[SETTINGS] Created settings file with defaults:",t)}catch(E){console.warn("[SETTINGS] Failed to create settings file, using in-memory defaults:",t,E)}return s}let r=X(t,"utf-8"),e=JSON.parse(r),n=e;if(e.env&&typeof e.env=="object"){n=e.env;try{P(t,JSON.stringify(n,null,2),"utf-8"),console.log("[SETTINGS] Migrated settings file from nested to flat schema:",t)}catch(s){console.warn("[SETTINGS] Failed to auto-migrate settings file:",t,s)}}let i={...this.DEFAULTS};for(let s of Object.keys(this.DEFAULTS))n[s]!==void 0&&(i[s]=n[s]);return i}catch(r){return console.warn("[SETTINGS] Failed to load settings, using defaults:",t,r),this.getAllDefaults()}}};function $(o={}){let{port:t,includeSkillFallback:r=!1,customPrefix:e,actualError:n}=o,i=e||"Worker service connection failed.",s=t?` (port ${t})`:"",E=`${i}${s}
|
|
|
|
`;return E+=`To restart the worker:
|
|
`,E+=`1. Exit Claude Code completely
|
|
`,E+=`2. Run: npm run worker:restart
|
|
`,E+="3. Restart Claude Code",r&&(E+=`
|
|
|
|
If that doesn't work, try: /troubleshoot`),n&&(E=`Worker Error: ${n}
|
|
|
|
${E}`),E}var Q=C.join(z(),".claude","plugins","marketplaces","thedotmack"),It=N(m.HEALTH_CHECK),S=null;function u(){if(S!==null)return S;let o=C.join(g.get("CLAUDE_MEM_DATA_DIR"),"settings.json"),t=g.loadFromFile(o);return S=parseInt(t.CLAUDE_MEM_WORKER_PORT,10),S}async function Z(){let o=u();return(await fetch(`http://127.0.0.1:${o}/api/readiness`)).ok}function tt(){let o=C.join(Q,"package.json");return JSON.parse(q(o,"utf-8")).version}async function et(){let o=u(),t=await fetch(`http://127.0.0.1:${o}/api/version`);if(!t.ok)throw new Error(`Failed to get worker version: ${t.status}`);return(await t.json()).version}async function rt(){let o=tt(),t=await et();o!==t&&l.debug("SYSTEM","Version check",{pluginVersion:o,workerVersion:t,note:"Mismatch will be auto-restarted by worker-service start command"})}async function v(){for(let r=0;r<75;r++){try{if(await Z()){await rt();return}}catch(e){l.debug("SYSTEM","Worker health check failed, will retry",{attempt:r+1,maxRetries:75,error:e instanceof Error?e.message:String(e)})}await new Promise(e=>setTimeout(e,200))}throw new Error($({port:u(),customPrefix:"Worker did not become ready within 15 seconds."}))}import it from"path";import{statSync as nt,readFileSync as ot}from"fs";import L from"path";var f={isWorktree:!1,worktreeName:null,parentRepoPath:null,parentProjectName:null};function W(o){let t=L.join(o,".git"),r;try{r=nt(t)}catch{return f}if(!r.isFile())return f;let e;try{e=ot(t,"utf-8").trim()}catch{return f}let n=e.match(/^gitdir:\s*(.+)$/);if(!n)return f;let s=n[1].match(/^(.+)[/\\]\.git[/\\]worktrees[/\\]([^/\\]+)$/);if(!s)return f;let E=s[1],c=L.basename(o),a=L.basename(E);return{isWorktree:!0,worktreeName:c,parentRepoPath:E,parentProjectName:a}}function st(o){if(!o||o.trim()==="")return l.warn("PROJECT_NAME","Empty cwd provided, using fallback",{cwd:o}),"unknown-project";let t=it.basename(o);if(t===""){if(process.platform==="win32"){let e=o.match(/^([A-Z]):\\/i);if(e){let i=`drive-${e[1].toUpperCase()}`;return l.info("PROJECT_NAME","Drive root detected",{cwd:o,projectName:i}),i}}return l.warn("PROJECT_NAME","Root directory detected, using fallback",{cwd:o}),"unknown-project"}return t}function F(o){let t=st(o);if(!o)return{primary:t,parent:null,isWorktree:!1,allProjects:[t]};let r=W(o);return r.isWorktree&&r.parentProjectName?{primary:t,parent:r.parentProjectName,isWorktree:!0,allProjects:[r.parentProjectName,t]}:{primary:t,parent:null,isWorktree:!1,allProjects:[t]}}async function w(o){await v();let t=o?.cwd??process.cwd(),r=F(t),e=u(),n=r.allProjects.join(","),i=`http://127.0.0.1:${e}/api/context/inject?projects=${encodeURIComponent(n)}`,s=await fetch(i);if(!s.ok)throw new Error(`Context generation failed: ${s.status}`);return(await s.text()).trim()}var Et=process.argv.includes("--colors");if(A.isTTY||Et)w(void 0).then(o=>{console.log(o),process.exit(0)});else{let o="";A.on("data",t=>o+=t),A.on("end",async()=>{let t;try{t=o.trim()?JSON.parse(o):void 0}catch(e){throw new Error(`Failed to parse hook input: ${e instanceof Error?e.message:String(e)}`)}let r=await w(t);console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:r}})),process.exit(0)})}
|