feat(context-hook): simplify to 3-tier system with 10 sessions
New structure (10 sessions total): - Tier 3 (oldest 6): Request + Date only - Tier 2 (middle 3): Request + Learned + Completed + Date - Tier 1 (most recent): Full verbosity (all fields) This provides cleaner, more focused context with less redundancy. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
import V from"path";import H from"better-sqlite3";import{join as _,dirname as F,basename as J}from"path";import{homedir as x}from"os";import{existsSync as ee,mkdirSync as P}from"fs";import{fileURLToPath as G}from"url";function W(){return typeof __dirname<"u"?__dirname:F(G(import.meta.url))}var j=W(),m=process.env.CLAUDE_MEM_DATA_DIR||_(x(),".claude-mem"),R=process.env.CLAUDE_CONFIG_DIR||_(x(),".claude"),te=_(m,"archives"),re=_(m,"logs"),ne=_(m,"trash"),ie=_(m,"backups"),oe=_(m,"settings.json"),k=_(m,"claude-mem.db"),ae=_(R,"settings.json"),de=_(R,"commands"),pe=_(R,"CLAUDE.md");function U(p){P(p,{recursive:!0})}function w(){return _(j,"..","..")}var I=(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))(I||{}),O=class{level;useColor;constructor(){let e=process.env.CLAUDE_MEM_LOG_LEVEL?.toUpperCase()||"INFO";this.level=I[e]??1,this.useColor=process.stdout.isTTY??!1}correlationId(e,s){return`obs-${e}-${s}`}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.level===0?`${e.message}
|
||||
${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Object.keys(e);return s.length===0?"{}":s.length<=3?JSON.stringify(e):`{${s.length} keys: ${s.slice(0,3).join(", ")}...}`}return String(e)}formatTool(e,s){if(!s)return e;try{let t=typeof s=="string"?JSON.parse(s):s;if(e==="Bash"&&t.command){let r=t.command.length>50?t.command.substring(0,50)+"...":t.command;return`${e}(${r})`}if(e==="Read"&&t.file_path){let r=t.file_path.split("/").pop()||t.file_path;return`${e}(${r})`}if(e==="Edit"&&t.file_path){let r=t.file_path.split("/").pop()||t.file_path;return`${e}(${r})`}if(e==="Write"&&t.file_path){let r=t.file_path.split("/").pop()||t.file_path;return`${e}(${r})`}return e}catch{return e}}log(e,s,t,r,i){if(e<this.level)return;let d=new Date().toISOString().replace("T"," ").substring(0,23),n=I[e].padEnd(5),c=s.padEnd(6),E="";r?.correlationId?E=`[${r.correlationId}] `:r?.sessionId&&(E=`[session-${r.sessionId}] `);let a="";i!=null&&(this.level===0&&typeof i=="object"?a=`
|
||||
`+JSON.stringify(i,null,2):a=" "+this.formatData(i));let g="";if(r){let{sessionId:C,sdkSessionId:D,correlationId:f,...u}=r;Object.keys(u).length>0&&(g=` {${Object.entries(u).map(([T,l])=>`${T}=${l}`).join(", ")}}`)}let h=`[${d}] [${n}] [${c}] ${E}${t}${g}${a}`;e===3?console.error(h):console.log(h)}debug(e,s,t,r){this.log(0,e,s,t,r)}info(e,s,t,r){this.log(1,e,s,t,r)}warn(e,s,t,r){this.log(2,e,s,t,r)}error(e,s,t,r){this.log(3,e,s,t,r)}dataIn(e,s,t,r){this.info(e,`\u2192 ${s}`,t,r)}dataOut(e,s,t,r){this.info(e,`\u2190 ${s}`,t,r)}success(e,s,t,r){this.info(e,`\u2713 ${s}`,t,r)}failure(e,s,t,r){this.error(e,`\u2717 ${s}`,t,r)}timing(e,s,t,r){this.info(e,`\u23F1 ${s}`,r,{duration:`${t}ms`})}},$=new O;var S=class{db;constructor(){U(m),this.db=new H(k),this.db.pragma("journal_mode = WAL"),this.db.pragma("synchronous = NORMAL"),this.db.pragma("foreign_keys = ON"),this.initializeSchema(),this.ensureWorkerPortColumn(),this.ensurePromptTrackingColumns(),this.removeSessionSummariesUniqueConstraint(),this.addObservationHierarchicalFields(),this.makeObservationsTextNullable(),this.createUserPromptsTable()}initializeSchema(){try{this.db.exec(`
|
||||
import V from"path";import B from"better-sqlite3";import{join as E,dirname as P,basename as Q}from"path";import{homedir as x}from"os";import{existsSync as se,mkdirSync as G}from"fs";import{fileURLToPath as W}from"url";function j(){return typeof __dirname<"u"?__dirname:P(W(import.meta.url))}var H=j(),m=process.env.CLAUDE_MEM_DATA_DIR||E(x(),".claude-mem"),I=process.env.CLAUDE_CONFIG_DIR||E(x(),".claude"),re=E(m,"archives"),ne=E(m,"logs"),ie=E(m,"trash"),oe=E(m,"backups"),ae=E(m,"settings.json"),k=E(m,"claude-mem.db"),de=E(I,"settings.json"),pe=E(I,"commands"),ce=E(I,"CLAUDE.md");function U(p){G(p,{recursive:!0})}function w(){return E(H,"..","..")}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||{}),L=class{level;useColor;constructor(){let e=process.env.CLAUDE_MEM_LOG_LEVEL?.toUpperCase()||"INFO";this.level=O[e]??1,this.useColor=process.stdout.isTTY??!1}correlationId(e,s){return`obs-${e}-${s}`}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.level===0?`${e.message}
|
||||
${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Object.keys(e);return s.length===0?"{}":s.length<=3?JSON.stringify(e):`{${s.length} keys: ${s.slice(0,3).join(", ")}...}`}return String(e)}formatTool(e,s){if(!s)return e;try{let t=typeof s=="string"?JSON.parse(s):s;if(e==="Bash"&&t.command){let r=t.command.length>50?t.command.substring(0,50)+"...":t.command;return`${e}(${r})`}if(e==="Read"&&t.file_path){let r=t.file_path.split("/").pop()||t.file_path;return`${e}(${r})`}if(e==="Edit"&&t.file_path){let r=t.file_path.split("/").pop()||t.file_path;return`${e}(${r})`}if(e==="Write"&&t.file_path){let r=t.file_path.split("/").pop()||t.file_path;return`${e}(${r})`}return e}catch{return e}}log(e,s,t,r,i){if(e<this.level)return;let d=new Date().toISOString().replace("T"," ").substring(0,23),n=O[e].padEnd(5),c=s.padEnd(6),_="";r?.correlationId?_=`[${r.correlationId}] `:r?.sessionId&&(_=`[session-${r.sessionId}] `);let a="";i!=null&&(this.level===0&&typeof i=="object"?a=`
|
||||
`+JSON.stringify(i,null,2):a=" "+this.formatData(i));let u="";if(r){let{sessionId:F,sdkSessionId:D,correlationId:N,...l}=r;Object.keys(l).length>0&&(u=` {${Object.entries(l).map(([T,f])=>`${T}=${f}`).join(", ")}}`)}let g=`[${d}] [${n}] [${c}] ${_}${t}${u}${a}`;e===3?console.error(g):console.log(g)}debug(e,s,t,r){this.log(0,e,s,t,r)}info(e,s,t,r){this.log(1,e,s,t,r)}warn(e,s,t,r){this.log(2,e,s,t,r)}error(e,s,t,r){this.log(3,e,s,t,r)}dataIn(e,s,t,r){this.info(e,`\u2192 ${s}`,t,r)}dataOut(e,s,t,r){this.info(e,`\u2190 ${s}`,t,r)}success(e,s,t,r){this.info(e,`\u2713 ${s}`,t,r)}failure(e,s,t,r){this.error(e,`\u2717 ${s}`,t,r)}timing(e,s,t,r){this.info(e,`\u23F1 ${s}`,r,{duration:`${t}ms`})}},$=new L;var b=class{db;constructor(){U(m),this.db=new B(k),this.db.pragma("journal_mode = WAL"),this.db.pragma("synchronous = NORMAL"),this.db.pragma("foreign_keys = ON"),this.initializeSchema(),this.ensureWorkerPortColumn(),this.ensurePromptTrackingColumns(),this.removeSessionSummariesUniqueConstraint(),this.addObservationHierarchicalFields(),this.makeObservationsTextNullable(),this.createUserPromptsTable()}initializeSchema(){try{this.db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS schema_versions (
|
||||
id INTEGER PRIMARY KEY,
|
||||
version INTEGER UNIQUE NOT NULL,
|
||||
@@ -306,13 +306,13 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje
|
||||
UPDATE sdk_sessions
|
||||
SET status = 'failed', completed_at = ?, completed_at_epoch = ?
|
||||
WHERE status = 'active'
|
||||
`).run(e.toISOString(),s).changes}close(){this.db.close()}};import L from"path";import{existsSync as A}from"fs";import{spawn as B}from"child_process";var Y=parseInt(process.env.CLAUDE_MEM_WORKER_PORT||"37777",10),K=`http://127.0.0.1:${Y}/health`;async function M(){try{return(await fetch(K,{signal:AbortSignal.timeout(500)})).ok}catch{return!1}}async function X(){try{if(await M())return!0;console.error("[claude-mem] Worker not responding, starting...");let p=w(),e=L.join(p,"plugin","scripts","worker-service.cjs");if(!A(e))return console.error(`[claude-mem] Worker service not found at ${e}`),!1;let s=L.join(p,"ecosystem.config.cjs"),t=L.join(p,"node_modules",".bin","pm2");if(!A(t))throw new Error(`PM2 binary not found at ${t}. This is a bundled dependency - try running: npm install`);if(!A(s))throw new Error(`PM2 ecosystem config not found at ${s}. Plugin installation may be corrupted.`);let r=B(t,["start",s],{detached:!0,stdio:"ignore",cwd:p});r.on("error",i=>{throw new Error(`Failed to spawn PM2: ${i.message}`)}),r.unref(),console.error("[claude-mem] Worker started with PM2");for(let i=0;i<3;i++)if(await new Promise(d=>setTimeout(d,500)),await M())return console.error("[claude-mem] Worker is healthy"),!0;return console.error("[claude-mem] Worker failed to become healthy after startup"),!1}catch(p){return console.error(`[claude-mem] Failed to start worker: ${p.message}`),!1}}var o={reset:"\x1B[0m",bright:"\x1B[1m",dim:"\x1B[2m",cyan:"\x1B[36m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",magenta:"\x1B[35m",gray:"\x1B[90m"};function v(p,e=!1,s=!1){X();let t=p?.cwd??process.cwd(),r=t?V.basename(t):"unknown-project",i=new S;try{let d=i.db.prepare(`
|
||||
`).run(e.toISOString(),s).changes}close(){this.db.close()}};import A from"path";import{existsSync as v}from"fs";import{spawn as Y}from"child_process";var q=parseInt(process.env.CLAUDE_MEM_WORKER_PORT||"37777",10),K=`http://127.0.0.1:${q}/health`;async function M(){try{return(await fetch(K,{signal:AbortSignal.timeout(500)})).ok}catch{return!1}}async function X(){try{if(await M())return!0;console.error("[claude-mem] Worker not responding, starting...");let p=w(),e=A.join(p,"plugin","scripts","worker-service.cjs");if(!v(e))return console.error(`[claude-mem] Worker service not found at ${e}`),!1;let s=A.join(p,"ecosystem.config.cjs"),t=A.join(p,"node_modules",".bin","pm2");if(!v(t))throw new Error(`PM2 binary not found at ${t}. This is a bundled dependency - try running: npm install`);if(!v(s))throw new Error(`PM2 ecosystem config not found at ${s}. Plugin installation may be corrupted.`);let r=Y(t,["start",s],{detached:!0,stdio:"ignore",cwd:p});r.on("error",i=>{throw new Error(`Failed to spawn PM2: ${i.message}`)}),r.unref(),console.error("[claude-mem] Worker started with PM2");for(let i=0;i<3;i++)if(await new Promise(d=>setTimeout(d,500)),await M())return console.error("[claude-mem] Worker is healthy"),!0;return console.error("[claude-mem] Worker failed to become healthy after startup"),!1}catch(p){return console.error(`[claude-mem] Failed to start worker: ${p.message}`),!1}}var o={reset:"\x1B[0m",bright:"\x1B[1m",dim:"\x1B[2m",cyan:"\x1B[36m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",magenta:"\x1B[35m",gray:"\x1B[90m"};function y(p,e=!1,s=!1){X();let t=p?.cwd??process.cwd(),r=t?V.basename(t):"unknown-project",i=new b;try{let d=i.db.prepare(`
|
||||
SELECT * FROM (
|
||||
SELECT sdk_session_id, request, learned, completed, next_steps, created_at, created_at_epoch
|
||||
FROM session_summaries
|
||||
WHERE project = ?
|
||||
ORDER BY created_at_epoch DESC
|
||||
LIMIT 30
|
||||
LIMIT 10
|
||||
)
|
||||
ORDER BY created_at_epoch ASC
|
||||
`).all(r);if(d.length===0)return e?`
|
||||
@@ -322,9 +322,9 @@ ${o.gray}${"\u2500".repeat(60)}${o.reset}
|
||||
${o.dim}No previous summaries found for this project yet.${o.reset}
|
||||
`:`# [${r}] recent context
|
||||
|
||||
No previous summaries found for this project yet.`;let n=[];e?(n.push(""),n.push(`${o.bright}${o.cyan}\u{1F4DD} [${r}] recent context${o.reset}`),n.push(`${o.gray}${"\u2500".repeat(60)}${o.reset}`)):(n.push(`# [${r}] recent context`),n.push(""));let c=!0;for(let E=0;E<d.length;E++){let a=d[E],g=d.length-1-E,h=g===0,C=g>4;if(c?e&&n.push(""):e?(n.push(`${o.gray}${"\u2500".repeat(60)}${o.reset}`),n.push("")):(n.push("---"),n.push("")),c=!1,C){a.learned&&(e?(n.push(`${o.bright}${o.blue}Learned:${o.reset} ${a.learned}`),n.push(""),n.push(`${o.dim}[Details: claude-mem://session/${a.sdk_session_id}]${o.reset}`)):(n.push(`**Learned:** ${a.learned}`),n.push(""),n.push(`[Details: claude-mem://session/${a.sdk_session_id}]`),n.push("")));continue}a.request&&(e?(n.push(`${o.bright}${o.yellow}Request:${o.reset} ${a.request}`),n.push("")):(n.push(`**Request:** ${a.request}`),n.push(""))),a.learned&&(e?(n.push(`${o.bright}${o.blue}Learned:${o.reset} ${a.learned}`),n.push("")):(n.push(`**Learned:** ${a.learned}`),n.push(""))),a.completed&&(e?(n.push(`${o.bright}${o.green}Completed:${o.reset} ${a.completed}`),n.push("")):(n.push(`**Completed:** ${a.completed}`),n.push(""))),a.next_steps&&(e?(n.push(`${o.bright}${o.magenta}Next Steps:${o.reset} ${a.next_steps}`),n.push("")):(n.push(`**Next Steps:** ${a.next_steps}`),n.push("")));let D=i.db.prepare(`
|
||||
SELECT files_read, files_modified
|
||||
FROM observations
|
||||
WHERE sdk_session_id = ?
|
||||
`).all(a.sdk_session_id),f=new Set,u=new Set;for(let T of D){if(T.files_read)try{let l=JSON.parse(T.files_read);Array.isArray(l)&&l.forEach(N=>f.add(N))}catch{}if(T.files_modified)try{let l=JSON.parse(T.files_modified);Array.isArray(l)&&l.forEach(N=>u.add(N))}catch{}}h&&f.size>0&&(e?n.push(`${o.dim}Files Read: ${Array.from(f).join(", ")}${o.reset}`):n.push(`**Files Read:** ${Array.from(f).join(", ")}`)),u.size>0&&(e?n.push(`${o.dim}Files Modified: ${Array.from(u).join(", ")}${o.reset}`):n.push(`**Files Modified:** ${Array.from(u).join(", ")}`));let b=new Date(a.created_at).toLocaleString();e?n.push(`${o.dim}Date: ${b}${o.reset}`):n.push(`**Date:** ${b}`),e||n.push("")}return e&&(n.push(""),n.push(`${o.gray}${"\u2500".repeat(60)}${o.reset}`)),n.join(`
|
||||
`)}finally{i.close()}}import{stdin as y}from"process";try{let p=process.argv.includes("--index");if(y.isTTY){let e=v(void 0,!0,p);console.log(e),process.exit(0)}else{let e="";y.on("data",s=>e+=s),y.on("end",()=>{let s=e.trim()?JSON.parse(e):void 0,r={hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:v(s,!1,p)}};console.log(JSON.stringify(r)),process.exit(0)})}}catch(p){console.error(`[claude-mem context-hook error: ${p.message}]`),process.exit(0)}
|
||||
No previous summaries found for this project yet.`;let n=[];e?(n.push(""),n.push(`${o.bright}${o.cyan}\u{1F4DD} [${r}] recent context${o.reset}`),n.push(`${o.gray}${"\u2500".repeat(60)}${o.reset}`)):(n.push(`# [${r}] recent context`),n.push(""));let c=!0;for(let _=0;_<d.length;_++){let a=d[_],u=d.length-1-_,g=u===0,F=u>=1&&u<=3,D=u>3;if(c?e&&n.push(""):e?(n.push(`${o.gray}${"\u2500".repeat(60)}${o.reset}`),n.push("")):(n.push("---"),n.push("")),c=!1,D){a.request&&(e?(n.push(`${o.bright}${o.yellow}Request:${o.reset} ${a.request}`),n.push("")):(n.push(`**Request:** ${a.request}`),n.push("")));let l=new Date(a.created_at).toLocaleString();e?n.push(`${o.dim}Date: ${l}${o.reset}`):(n.push(`**Date:** ${l}`),n.push(""));continue}if(a.request&&(e?(n.push(`${o.bright}${o.yellow}Request:${o.reset} ${a.request}`),n.push("")):(n.push(`**Request:** ${a.request}`),n.push(""))),a.learned&&(e?(n.push(`${o.bright}${o.blue}Learned:${o.reset} ${a.learned}`),n.push("")):(n.push(`**Learned:** ${a.learned}`),n.push(""))),a.completed&&(e?(n.push(`${o.bright}${o.green}Completed:${o.reset} ${a.completed}`),n.push("")):(n.push(`**Completed:** ${a.completed}`),n.push(""))),g&&a.next_steps&&(e?(n.push(`${o.bright}${o.magenta}Next Steps:${o.reset} ${a.next_steps}`),n.push("")):(n.push(`**Next Steps:** ${a.next_steps}`),n.push(""))),g){let l=i.db.prepare(`
|
||||
SELECT files_read, files_modified
|
||||
FROM observations
|
||||
WHERE sdk_session_id = ?
|
||||
`).all(a.sdk_session_id),h=new Set,T=new Set;for(let f of l){if(f.files_read)try{let S=JSON.parse(f.files_read);Array.isArray(S)&&S.forEach(R=>h.add(R))}catch{}if(f.files_modified)try{let S=JSON.parse(f.files_modified);Array.isArray(S)&&S.forEach(R=>T.add(R))}catch{}}h.size>0&&(e?n.push(`${o.dim}Files Read: ${Array.from(h).join(", ")}${o.reset}`):n.push(`**Files Read:** ${Array.from(h).join(", ")}`)),T.size>0&&(e?n.push(`${o.dim}Files Modified: ${Array.from(T).join(", ")}${o.reset}`):n.push(`**Files Modified:** ${Array.from(T).join(", ")}`))}let N=new Date(a.created_at).toLocaleString();e?n.push(`${o.dim}Date: ${N}${o.reset}`):n.push(`**Date:** ${N}`),e||n.push("")}return e&&(n.push(""),n.push(`${o.gray}${"\u2500".repeat(60)}${o.reset}`)),n.join(`
|
||||
`)}finally{i.close()}}import{stdin as C}from"process";try{let p=process.argv.includes("--index");if(C.isTTY){let e=y(void 0,!0,p);console.log(e),process.exit(0)}else{let e="";C.on("data",s=>e+=s),C.on("end",()=>{let s=e.trim()?JSON.parse(e):void 0,r={hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:y(s,!1,p)}};console.log(JSON.stringify(r)),process.exit(0)})}}catch(p){console.error(`[claude-mem context-hook error: ${p.message}]`),process.exit(0)}
|
||||
|
||||
Reference in New Issue
Block a user