diff --git a/plugin/scripts/context-hook.js b/plugin/scripts/context-hook.js index b458a8f0..b813e5b0 100755 --- a/plugin/scripts/context-hook.js +++ b/plugin/scripts/context-hook.js @@ -1,13 +1,13 @@ #!/usr/bin/env node -import U from"path";import{homedir as ve}from"os";import{existsSync as ye,readFileSync as Ae,unlinkSync as Le}from"fs";import{stdin as Z}from"process";import{fileURLToPath as Ce}from"url";import{dirname as De}from"path";import Ie from"better-sqlite3";import{join as f,dirname as fe,basename as Ge}from"path";import{homedir as ie}from"os";import{existsSync as qe,mkdirSync as Re}from"fs";import{fileURLToPath as Oe}from"url";function Ne(){return typeof __dirname<"u"?__dirname:fe(Oe(import.meta.url))}var Qe=Ne(),y=process.env.CLAUDE_MEM_DATA_DIR||f(ie(),".claude-mem"),K=process.env.CLAUDE_CONFIG_DIR||f(ie(),".claude"),ze=f(y,"archives"),Ze=f(y,"logs"),es=f(y,"trash"),ss=f(y,"backups"),ts=f(y,"settings.json"),ae=f(y,"claude-mem.db"),rs=f(y,"vector-db"),ns=f(K,"settings.json"),os=f(K,"commands"),is=f(K,"CLAUDE.md");function de(u){Re(u,{recursive:!0})}var q=(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))(q||{}),J=class{level;useColor;constructor(){let e=process.env.CLAUDE_MEM_LOG_LEVEL?.toUpperCase()||"INFO";this.level=q[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(e0&&(S=` {${Object.entries(p).map(([L,$])=>`${L}=${$}`).join(", ")}}`)}let A=`[${d}] [${c}] [${l}] ${g}${t}${S}${h}`;e===3?console.error(A):console.log(A)}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`})}},W=new J;var j=class{db;constructor(){de(y),this.db=new Ie(ae),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(),this.ensureDiscoveryTokensColumn()}initializeSchema(){try{this.db.exec(` +import P from"path";import{homedir as ge}from"os";import{existsSync as Se,readFileSync as be,unlinkSync as ke}from"fs";import{stdin as te}from"process";import{fileURLToPath as De}from"url";import{dirname as $e}from"path";import Ce from"better-sqlite3";import{join as O,dirname as ve,basename as qe}from"path";import{homedir as de}from"os";import{existsSync as Ze,mkdirSync as ye}from"fs";import{fileURLToPath as Ae}from"url";function Le(){return typeof __dirname<"u"?__dirname:ve(Ae(import.meta.url))}var ss=Le(),L=process.env.CLAUDE_MEM_DATA_DIR||O(de(),".claude-mem"),Q=process.env.CLAUDE_CONFIG_DIR||O(de(),".claude"),ts=O(L,"archives"),rs=O(L,"logs"),ns=O(L,"trash"),os=O(L,"backups"),is=O(L,"settings.json"),ce=O(L,"claude-mem.db"),as=O(L,"vector-db"),ds=O(Q,"settings.json"),cs=O(Q,"commands"),ps=O(Q,"CLAUDE.md");function pe(p){ye(p,{recursive:!0})}var z=(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))(z||{}),Z=class{level;useColor;constructor(){let e=process.env.CLAUDE_MEM_LOG_LEVEL?.toUpperCase()||"INFO";this.level=z[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 r=typeof s=="string"?JSON.parse(s):s;if(e==="Bash"&&r.command){let n=r.command.length>50?r.command.substring(0,50)+"...":r.command;return`${e}(${n})`}if(e==="Read"&&r.file_path){let n=r.file_path.split("/").pop()||r.file_path;return`${e}(${n})`}if(e==="Edit"&&r.file_path){let n=r.file_path.split("/").pop()||r.file_path;return`${e}(${n})`}if(e==="Write"&&r.file_path){let n=r.file_path.split("/").pop()||r.file_path;return`${e}(${n})`}return e}catch{return e}}log(e,s,r,n,i){if(e0&&(b=` {${Object.entries(t).map(([N,g])=>`${N}=${g}`).join(", ")}}`)}let f=`[${d}] [${a}] [${u}] ${S}${r}${b}${T}`;e===3?console.error(f):console.log(f)}debug(e,s,r,n){this.log(0,e,s,r,n)}info(e,s,r,n){this.log(1,e,s,r,n)}warn(e,s,r,n){this.log(2,e,s,r,n)}error(e,s,r,n){this.log(3,e,s,r,n)}dataIn(e,s,r,n){this.info(e,`\u2192 ${s}`,r,n)}dataOut(e,s,r,n){this.info(e,`\u2190 ${s}`,r,n)}success(e,s,r,n){this.info(e,`\u2713 ${s}`,r,n)}failure(e,s,r,n){this.error(e,`\u2717 ${s}`,r,n)}timing(e,s,r,n){this.info(e,`\u23F1 ${s}`,n,{duration:`${r}ms`})}},F=new Z;var Y=class{db;constructor(){pe(L),this.db=new Ce(ce),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(),this.ensureDiscoveryTokensColumn()}initializeSchema(){try{this.db.exec(` CREATE TABLE IF NOT EXISTS schema_versions ( id INTEGER PRIMARY KEY, version INTEGER UNIQUE NOT NULL, applied_at TEXT NOT NULL ) - `);let e=this.db.prepare("SELECT version FROM schema_versions ORDER BY version").all();(e.length>0?Math.max(...e.map(t=>t.version)):0)===0&&(console.error("[SessionStore] Initializing fresh database with migration004..."),this.db.exec(` + `);let e=this.db.prepare("SELECT version FROM schema_versions ORDER BY version").all();(e.length>0?Math.max(...e.map(r=>r.version)):0)===0&&(console.error("[SessionStore] Initializing fresh database with migration004..."),this.db.exec(` CREATE TABLE IF NOT EXISTS sdk_sessions ( id INTEGER PRIMARY KEY AUTOINCREMENT, claude_session_id TEXT UNIQUE NOT NULL, @@ -63,7 +63,7 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje CREATE INDEX IF NOT EXISTS idx_session_summaries_sdk_session ON session_summaries(sdk_session_id); CREATE INDEX IF NOT EXISTS idx_session_summaries_project ON session_summaries(project); CREATE INDEX IF NOT EXISTS idx_session_summaries_created ON session_summaries(created_at_epoch DESC); - `),this.db.prepare("INSERT INTO schema_versions (version, applied_at) VALUES (?, ?)").run(4,new Date().toISOString()),console.error("[SessionStore] Migration004 applied successfully"))}catch(e){throw console.error("[SessionStore] Schema initialization error:",e.message),e}}ensureWorkerPortColumn(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(5))return;this.db.pragma("table_info(sdk_sessions)").some(r=>r.name==="worker_port")||(this.db.exec("ALTER TABLE sdk_sessions ADD COLUMN worker_port INTEGER"),console.error("[SessionStore] Added worker_port column to sdk_sessions table")),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(5,new Date().toISOString())}catch(e){console.error("[SessionStore] Migration error:",e.message)}}ensurePromptTrackingColumns(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(6))return;this.db.pragma("table_info(sdk_sessions)").some(l=>l.name==="prompt_counter")||(this.db.exec("ALTER TABLE sdk_sessions ADD COLUMN prompt_counter INTEGER DEFAULT 0"),console.error("[SessionStore] Added prompt_counter column to sdk_sessions table")),this.db.pragma("table_info(observations)").some(l=>l.name==="prompt_number")||(this.db.exec("ALTER TABLE observations ADD COLUMN prompt_number INTEGER"),console.error("[SessionStore] Added prompt_number column to observations table")),this.db.pragma("table_info(session_summaries)").some(l=>l.name==="prompt_number")||(this.db.exec("ALTER TABLE session_summaries ADD COLUMN prompt_number INTEGER"),console.error("[SessionStore] Added prompt_number column to session_summaries table")),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(6,new Date().toISOString())}catch(e){console.error("[SessionStore] Prompt tracking migration error:",e.message)}}removeSessionSummariesUniqueConstraint(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(7))return;if(!this.db.pragma("index_list(session_summaries)").some(r=>r.unique===1)){this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(7,new Date().toISOString());return}console.error("[SessionStore] Removing UNIQUE constraint from session_summaries.sdk_session_id..."),this.db.exec("BEGIN TRANSACTION");try{this.db.exec(` + `),this.db.prepare("INSERT INTO schema_versions (version, applied_at) VALUES (?, ?)").run(4,new Date().toISOString()),console.error("[SessionStore] Migration004 applied successfully"))}catch(e){throw console.error("[SessionStore] Schema initialization error:",e.message),e}}ensureWorkerPortColumn(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(5))return;this.db.pragma("table_info(sdk_sessions)").some(n=>n.name==="worker_port")||(this.db.exec("ALTER TABLE sdk_sessions ADD COLUMN worker_port INTEGER"),console.error("[SessionStore] Added worker_port column to sdk_sessions table")),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(5,new Date().toISOString())}catch(e){console.error("[SessionStore] Migration error:",e.message)}}ensurePromptTrackingColumns(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(6))return;this.db.pragma("table_info(sdk_sessions)").some(u=>u.name==="prompt_counter")||(this.db.exec("ALTER TABLE sdk_sessions ADD COLUMN prompt_counter INTEGER DEFAULT 0"),console.error("[SessionStore] Added prompt_counter column to sdk_sessions table")),this.db.pragma("table_info(observations)").some(u=>u.name==="prompt_number")||(this.db.exec("ALTER TABLE observations ADD COLUMN prompt_number INTEGER"),console.error("[SessionStore] Added prompt_number column to observations table")),this.db.pragma("table_info(session_summaries)").some(u=>u.name==="prompt_number")||(this.db.exec("ALTER TABLE session_summaries ADD COLUMN prompt_number INTEGER"),console.error("[SessionStore] Added prompt_number column to session_summaries table")),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(6,new Date().toISOString())}catch(e){console.error("[SessionStore] Prompt tracking migration error:",e.message)}}removeSessionSummariesUniqueConstraint(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(7))return;if(!this.db.pragma("index_list(session_summaries)").some(n=>n.unique===1)){this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(7,new Date().toISOString());return}console.error("[SessionStore] Removing UNIQUE constraint from session_summaries.sdk_session_id..."),this.db.exec("BEGIN TRANSACTION");try{this.db.exec(` CREATE TABLE session_summaries_new ( id INTEGER PRIMARY KEY AUTOINCREMENT, sdk_session_id TEXT NOT NULL, @@ -91,7 +91,7 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje CREATE INDEX idx_session_summaries_sdk_session ON session_summaries(sdk_session_id); CREATE INDEX idx_session_summaries_project ON session_summaries(project); CREATE INDEX idx_session_summaries_created ON session_summaries(created_at_epoch DESC); - `),this.db.exec("COMMIT"),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(7,new Date().toISOString()),console.error("[SessionStore] Successfully removed UNIQUE constraint from session_summaries.sdk_session_id")}catch(r){throw this.db.exec("ROLLBACK"),r}}catch(e){console.error("[SessionStore] Migration error (remove UNIQUE constraint):",e.message)}}addObservationHierarchicalFields(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(8))return;if(this.db.pragma("table_info(observations)").some(r=>r.name==="title")){this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(8,new Date().toISOString());return}console.error("[SessionStore] Adding hierarchical fields to observations table..."),this.db.exec(` + `),this.db.exec("COMMIT"),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(7,new Date().toISOString()),console.error("[SessionStore] Successfully removed UNIQUE constraint from session_summaries.sdk_session_id")}catch(n){throw this.db.exec("ROLLBACK"),n}}catch(e){console.error("[SessionStore] Migration error (remove UNIQUE constraint):",e.message)}}addObservationHierarchicalFields(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(8))return;if(this.db.pragma("table_info(observations)").some(n=>n.name==="title")){this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(8,new Date().toISOString());return}console.error("[SessionStore] Adding hierarchical fields to observations table..."),this.db.exec(` ALTER TABLE observations ADD COLUMN title TEXT; ALTER TABLE observations ADD COLUMN subtitle TEXT; ALTER TABLE observations ADD COLUMN facts TEXT; @@ -99,7 +99,7 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje ALTER TABLE observations ADD COLUMN concepts TEXT; ALTER TABLE observations ADD COLUMN files_read TEXT; ALTER TABLE observations ADD COLUMN files_modified TEXT; - `),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(8,new Date().toISOString()),console.error("[SessionStore] Successfully added hierarchical fields to observations table")}catch(e){console.error("[SessionStore] Migration error (add hierarchical fields):",e.message)}}makeObservationsTextNullable(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(9))return;let t=this.db.pragma("table_info(observations)").find(r=>r.name==="text");if(!t||t.notnull===0){this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(9,new Date().toISOString());return}console.error("[SessionStore] Making observations.text nullable..."),this.db.exec("BEGIN TRANSACTION");try{this.db.exec(` + `),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(8,new Date().toISOString()),console.error("[SessionStore] Successfully added hierarchical fields to observations table")}catch(e){console.error("[SessionStore] Migration error (add hierarchical fields):",e.message)}}makeObservationsTextNullable(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(9))return;let r=this.db.pragma("table_info(observations)").find(n=>n.name==="text");if(!r||r.notnull===0){this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(9,new Date().toISOString());return}console.error("[SessionStore] Making observations.text nullable..."),this.db.exec("BEGIN TRANSACTION");try{this.db.exec(` CREATE TABLE observations_new ( id INTEGER PRIMARY KEY AUTOINCREMENT, sdk_session_id TEXT NOT NULL, @@ -129,7 +129,7 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje CREATE INDEX idx_observations_project ON observations(project); CREATE INDEX idx_observations_type ON observations(type); CREATE INDEX idx_observations_created ON observations(created_at_epoch DESC); - `),this.db.exec("COMMIT"),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(9,new Date().toISOString()),console.error("[SessionStore] Successfully made observations.text nullable")}catch(r){throw this.db.exec("ROLLBACK"),r}}catch(e){console.error("[SessionStore] Migration error (make text nullable):",e.message)}}createUserPromptsTable(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(10))return;if(this.db.pragma("table_info(user_prompts)").length>0){this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(10,new Date().toISOString());return}console.error("[SessionStore] Creating user_prompts table with FTS5 support..."),this.db.exec("BEGIN TRANSACTION");try{this.db.exec(` + `),this.db.exec("COMMIT"),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(9,new Date().toISOString()),console.error("[SessionStore] Successfully made observations.text nullable")}catch(n){throw this.db.exec("ROLLBACK"),n}}catch(e){console.error("[SessionStore] Migration error (make text nullable):",e.message)}}createUserPromptsTable(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(10))return;if(this.db.pragma("table_info(user_prompts)").length>0){this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(10,new Date().toISOString());return}console.error("[SessionStore] Creating user_prompts table with FTS5 support..."),this.db.exec("BEGIN TRANSACTION");try{this.db.exec(` CREATE TABLE user_prompts ( id INTEGER PRIMARY KEY AUTOINCREMENT, claude_session_id TEXT NOT NULL, @@ -167,7 +167,7 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje INSERT INTO user_prompts_fts(rowid, prompt_text) VALUES (new.id, new.prompt_text); END; - `),this.db.exec("COMMIT"),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(10,new Date().toISOString()),console.error("[SessionStore] Successfully created user_prompts table with FTS5 support")}catch(t){throw this.db.exec("ROLLBACK"),t}}catch(e){console.error("[SessionStore] Migration error (create user_prompts table):",e.message)}}ensureDiscoveryTokensColumn(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(11))return;this.db.pragma("table_info(observations)").some(d=>d.name==="discovery_tokens")||(this.db.exec("ALTER TABLE observations ADD COLUMN discovery_tokens INTEGER DEFAULT 0"),console.error("[SessionStore] Added discovery_tokens column to observations table")),this.db.pragma("table_info(session_summaries)").some(d=>d.name==="discovery_tokens")||(this.db.exec("ALTER TABLE session_summaries ADD COLUMN discovery_tokens INTEGER DEFAULT 0"),console.error("[SessionStore] Added discovery_tokens column to session_summaries table")),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(11,new Date().toISOString())}catch(e){throw console.error("[SessionStore] Discovery tokens migration error:",e.message),e}}getRecentSummaries(e,s=10){return this.db.prepare(` + `),this.db.exec("COMMIT"),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(10,new Date().toISOString()),console.error("[SessionStore] Successfully created user_prompts table with FTS5 support")}catch(r){throw this.db.exec("ROLLBACK"),r}}catch(e){console.error("[SessionStore] Migration error (create user_prompts table):",e.message)}}ensureDiscoveryTokensColumn(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(11))return;this.db.pragma("table_info(observations)").some(d=>d.name==="discovery_tokens")||(this.db.exec("ALTER TABLE observations ADD COLUMN discovery_tokens INTEGER DEFAULT 0"),console.error("[SessionStore] Added discovery_tokens column to observations table")),this.db.pragma("table_info(session_summaries)").some(d=>d.name==="discovery_tokens")||(this.db.exec("ALTER TABLE session_summaries ADD COLUMN discovery_tokens INTEGER DEFAULT 0"),console.error("[SessionStore] Added discovery_tokens column to session_summaries table")),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(11,new Date().toISOString())}catch(e){throw console.error("[SessionStore] Discovery tokens migration error:",e.message),e}}getRecentSummaries(e,s=10){return this.db.prepare(` SELECT request, investigated, learned, completed, next_steps, files_read, files_edited, notes, prompt_number, created_at @@ -219,7 +219,7 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje FROM sdk_sessions WHERE project IS NOT NULL AND project != '' ORDER BY project ASC - `).all().map(t=>t.project)}getRecentSessionsWithStatus(e,s=3){return this.db.prepare(` + `).all().map(r=>r.project)}getRecentSessionsWithStatus(e,s=3){return this.db.prepare(` SELECT * FROM ( SELECT s.sdk_session_id, @@ -245,10 +245,10 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje SELECT * FROM observations WHERE id = ? - `).get(e)||null}getObservationsByIds(e,s={}){if(e.length===0)return[];let{orderBy:t="date_desc",limit:r}=s,i=t==="date_asc"?"ASC":"DESC",d=r?`LIMIT ${r}`:"",c=e.map(()=>"?").join(",");return this.db.prepare(` + `).get(e)||null}getObservationsByIds(e,s={}){if(e.length===0)return[];let{orderBy:r="date_desc",limit:n}=s,i=r==="date_asc"?"ASC":"DESC",d=n?`LIMIT ${n}`:"",a=e.map(()=>"?").join(",");return this.db.prepare(` SELECT * FROM observations - WHERE id IN (${c}) + WHERE id IN (${a}) ORDER BY created_at_epoch ${i} ${d} `).all(...e)}getSummaryForSession(e){return this.db.prepare(` @@ -259,11 +259,11 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje WHERE sdk_session_id = ? ORDER BY created_at_epoch DESC LIMIT 1 - `).get(e)||null}getFilesForSession(e){let t=this.db.prepare(` + `).get(e)||null}getFilesForSession(e){let r=this.db.prepare(` SELECT files_read, files_modified FROM observations WHERE sdk_session_id = ? - `).all(e),r=new Set,i=new Set;for(let d of t){if(d.files_read)try{let c=JSON.parse(d.files_read);Array.isArray(c)&&c.forEach(l=>r.add(l))}catch{}if(d.files_modified)try{let c=JSON.parse(d.files_modified);Array.isArray(c)&&c.forEach(l=>i.add(l))}catch{}}return{filesRead:Array.from(r),filesModified:Array.from(i)}}getSessionById(e){return this.db.prepare(` + `).all(e),n=new Set,i=new Set;for(let d of r){if(d.files_read)try{let a=JSON.parse(d.files_read);Array.isArray(a)&&a.forEach(u=>n.add(u))}catch{}if(d.files_modified)try{let a=JSON.parse(d.files_modified);Array.isArray(a)&&a.forEach(u=>i.add(u))}catch{}}return{filesRead:Array.from(n),filesModified:Array.from(i)}}getSessionById(e){return this.db.prepare(` SELECT id, claude_session_id, sdk_session_id, project, user_prompt FROM sdk_sessions WHERE id = ? @@ -290,21 +290,21 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje SELECT prompt_counter FROM sdk_sessions WHERE id = ? `).get(e)?.prompt_counter||1}getPromptCounter(e){return this.db.prepare(` SELECT prompt_counter FROM sdk_sessions WHERE id = ? - `).get(e)?.prompt_counter||0}createSDKSession(e,s,t){let r=new Date,i=r.getTime(),c=this.db.prepare(` + `).get(e)?.prompt_counter||0}createSDKSession(e,s,r){let n=new Date,i=n.getTime(),a=this.db.prepare(` INSERT OR IGNORE INTO sdk_sessions (claude_session_id, sdk_session_id, project, user_prompt, started_at, started_at_epoch, status) VALUES (?, ?, ?, ?, ?, ?, 'active') - `).run(e,e,s,t,r.toISOString(),i);return c.lastInsertRowid===0||c.changes===0?(s&&s.trim()!==""&&this.db.prepare(` + `).run(e,e,s,r,n.toISOString(),i);return a.lastInsertRowid===0||a.changes===0?(s&&s.trim()!==""&&this.db.prepare(` UPDATE sdk_sessions SET project = ?, user_prompt = ? WHERE claude_session_id = ? - `).run(s,t,e),this.db.prepare(` + `).run(s,r,e),this.db.prepare(` SELECT id FROM sdk_sessions WHERE claude_session_id = ? LIMIT 1 - `).get(e).id):c.lastInsertRowid}updateSDKSessionId(e,s){return this.db.prepare(` + `).get(e).id):a.lastInsertRowid}updateSDKSessionId(e,s){return this.db.prepare(` UPDATE sdk_sessions SET sdk_session_id = ? WHERE id = ? AND sdk_session_id IS NULL - `).run(s,e).changes===0?(W.debug("DB","sdk_session_id already set, skipping update",{sessionId:e,sdkSessionId:s}),!1):!0}setWorkerPort(e,s){this.db.prepare(` + `).run(s,e).changes===0?(F.debug("DB","sdk_session_id already set, skipping update",{sessionId:e,sdkSessionId:s}),!1):!0}setWorkerPort(e,s){this.db.prepare(` UPDATE sdk_sessions SET worker_port = ? WHERE id = ? @@ -313,127 +313,128 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje FROM sdk_sessions WHERE id = ? LIMIT 1 - `).get(e)?.worker_port||null}saveUserPrompt(e,s,t){let r=new Date,i=r.getTime();return this.db.prepare(` + `).get(e)?.worker_port||null}saveUserPrompt(e,s,r){let n=new Date,i=n.getTime();return this.db.prepare(` INSERT INTO user_prompts (claude_session_id, prompt_number, prompt_text, created_at, created_at_epoch) VALUES (?, ?, ?, ?, ?) - `).run(e,s,t,r.toISOString(),i).lastInsertRowid}getUserPrompt(e,s){return this.db.prepare(` + `).run(e,s,r,n.toISOString(),i).lastInsertRowid}getUserPrompt(e,s){return this.db.prepare(` SELECT prompt_text FROM user_prompts WHERE claude_session_id = ? AND prompt_number = ? LIMIT 1 - `).get(e,s)?.prompt_text??null}storeObservation(e,s,t,r,i=0){let d=new Date,c=d.getTime();this.db.prepare(` + `).get(e,s)?.prompt_text??null}storeObservation(e,s,r,n,i=0){let d=new Date,a=d.getTime();this.db.prepare(` SELECT id FROM sdk_sessions WHERE sdk_session_id = ? `).get(e)||(this.db.prepare(` INSERT INTO sdk_sessions (claude_session_id, sdk_session_id, project, started_at, started_at_epoch, status) VALUES (?, ?, ?, ?, ?, 'active') - `).run(e,e,s,d.toISOString(),c),console.error(`[SessionStore] Auto-created session record for session_id: ${e}`));let S=this.db.prepare(` + `).run(e,e,s,d.toISOString(),a),console.error(`[SessionStore] Auto-created session record for session_id: ${e}`));let b=this.db.prepare(` INSERT INTO observations (sdk_session_id, project, type, title, subtitle, facts, narrative, concepts, files_read, files_modified, prompt_number, discovery_tokens, created_at, created_at_epoch) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `).run(e,s,t.type,t.title,t.subtitle,JSON.stringify(t.facts),t.narrative,JSON.stringify(t.concepts),JSON.stringify(t.files_read),JSON.stringify(t.files_modified),r||null,i,d.toISOString(),c);return{id:Number(S.lastInsertRowid),createdAtEpoch:c}}storeSummary(e,s,t,r,i=0){let d=new Date,c=d.getTime();this.db.prepare(` + `).run(e,s,r.type,r.title,r.subtitle,JSON.stringify(r.facts),r.narrative,JSON.stringify(r.concepts),JSON.stringify(r.files_read),JSON.stringify(r.files_modified),n||null,i,d.toISOString(),a);return{id:Number(b.lastInsertRowid),createdAtEpoch:a}}storeSummary(e,s,r,n,i=0){let d=new Date,a=d.getTime();this.db.prepare(` SELECT id FROM sdk_sessions WHERE sdk_session_id = ? `).get(e)||(this.db.prepare(` INSERT INTO sdk_sessions (claude_session_id, sdk_session_id, project, started_at, started_at_epoch, status) VALUES (?, ?, ?, ?, ?, 'active') - `).run(e,e,s,d.toISOString(),c),console.error(`[SessionStore] Auto-created session record for session_id: ${e}`));let S=this.db.prepare(` + `).run(e,e,s,d.toISOString(),a),console.error(`[SessionStore] Auto-created session record for session_id: ${e}`));let b=this.db.prepare(` INSERT INTO session_summaries (sdk_session_id, project, request, investigated, learned, completed, next_steps, notes, prompt_number, discovery_tokens, created_at, created_at_epoch) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `).run(e,s,t.request,t.investigated,t.learned,t.completed,t.next_steps,t.notes,r||null,i,d.toISOString(),c);return{id:Number(S.lastInsertRowid),createdAtEpoch:c}}markSessionCompleted(e){let s=new Date,t=s.getTime();this.db.prepare(` + `).run(e,s,r.request,r.investigated,r.learned,r.completed,r.next_steps,r.notes,n||null,i,d.toISOString(),a);return{id:Number(b.lastInsertRowid),createdAtEpoch:a}}markSessionCompleted(e){let s=new Date,r=s.getTime();this.db.prepare(` UPDATE sdk_sessions SET status = 'completed', completed_at = ?, completed_at_epoch = ? WHERE id = ? - `).run(s.toISOString(),t,e)}markSessionFailed(e){let s=new Date,t=s.getTime();this.db.prepare(` + `).run(s.toISOString(),r,e)}markSessionFailed(e){let s=new Date,r=s.getTime();this.db.prepare(` UPDATE sdk_sessions SET status = 'failed', completed_at = ?, completed_at_epoch = ? WHERE id = ? - `).run(s.toISOString(),t,e)}getSessionSummariesByIds(e,s={}){if(e.length===0)return[];let{orderBy:t="date_desc",limit:r}=s,i=t==="date_asc"?"ASC":"DESC",d=r?`LIMIT ${r}`:"",c=e.map(()=>"?").join(",");return this.db.prepare(` + `).run(s.toISOString(),r,e)}getSessionSummariesByIds(e,s={}){if(e.length===0)return[];let{orderBy:r="date_desc",limit:n}=s,i=r==="date_asc"?"ASC":"DESC",d=n?`LIMIT ${n}`:"",a=e.map(()=>"?").join(",");return this.db.prepare(` SELECT * FROM session_summaries - WHERE id IN (${c}) + WHERE id IN (${a}) ORDER BY created_at_epoch ${i} ${d} - `).all(...e)}getUserPromptsByIds(e,s={}){if(e.length===0)return[];let{orderBy:t="date_desc",limit:r}=s,i=t==="date_asc"?"ASC":"DESC",d=r?`LIMIT ${r}`:"",c=e.map(()=>"?").join(",");return this.db.prepare(` + `).all(...e)}getUserPromptsByIds(e,s={}){if(e.length===0)return[];let{orderBy:r="date_desc",limit:n}=s,i=r==="date_asc"?"ASC":"DESC",d=n?`LIMIT ${n}`:"",a=e.map(()=>"?").join(",");return this.db.prepare(` SELECT up.*, s.project, s.sdk_session_id FROM user_prompts up JOIN sdk_sessions s ON up.claude_session_id = s.claude_session_id - WHERE up.id IN (${c}) + WHERE up.id IN (${a}) ORDER BY up.created_at_epoch ${i} ${d} - `).all(...e)}getTimelineAroundTimestamp(e,s=10,t=10,r){return this.getTimelineAroundObservation(null,e,s,t,r)}getTimelineAroundObservation(e,s,t=10,r=10,i){let d=i?"AND project = ?":"",c=i?[i]:[],l,g;if(e!==null){let R=` + `).all(...e)}getTimelineAroundTimestamp(e,s=10,r=10,n){return this.getTimelineAroundObservation(null,e,s,r,n)}getTimelineAroundObservation(e,s,r=10,n=10,i){let d=i?"AND project = ?":"",a=i?[i]:[],u,S;if(e!==null){let h=` SELECT id, created_at_epoch FROM observations WHERE id <= ? ${d} ORDER BY id DESC LIMIT ? - `,n=` + `,C=` SELECT id, created_at_epoch FROM observations WHERE id >= ? ${d} ORDER BY id ASC LIMIT ? - `;try{let m=this.db.prepare(R).all(e,...c,t+1),p=this.db.prepare(n).all(e,...c,r+1);if(m.length===0&&p.length===0)return{observations:[],sessions:[],prompts:[]};l=m.length>0?m[m.length-1].created_at_epoch:s,g=p.length>0?p[p.length-1].created_at_epoch:s}catch(m){return console.error("[SessionStore] Error getting boundary observations:",m.message),{observations:[],sessions:[],prompts:[]}}}else{let R=` + `;try{let _=this.db.prepare(h).all(e,...a,r+1),t=this.db.prepare(C).all(e,...a,n+1);if(_.length===0&&t.length===0)return{observations:[],sessions:[],prompts:[]};u=_.length>0?_[_.length-1].created_at_epoch:s,S=t.length>0?t[t.length-1].created_at_epoch:s}catch(_){return console.error("[SessionStore] Error getting boundary observations:",_.message),{observations:[],sessions:[],prompts:[]}}}else{let h=` SELECT created_at_epoch FROM observations WHERE created_at_epoch <= ? ${d} ORDER BY created_at_epoch DESC LIMIT ? - `,n=` + `,C=` SELECT created_at_epoch FROM observations WHERE created_at_epoch >= ? ${d} ORDER BY created_at_epoch ASC LIMIT ? - `;try{let m=this.db.prepare(R).all(s,...c,t),p=this.db.prepare(n).all(s,...c,r+1);if(m.length===0&&p.length===0)return{observations:[],sessions:[],prompts:[]};l=m.length>0?m[m.length-1].created_at_epoch:s,g=p.length>0?p[p.length-1].created_at_epoch:s}catch(m){return console.error("[SessionStore] Error getting boundary timestamps:",m.message),{observations:[],sessions:[],prompts:[]}}}let h=` + `;try{let _=this.db.prepare(h).all(s,...a,r),t=this.db.prepare(C).all(s,...a,n+1);if(_.length===0&&t.length===0)return{observations:[],sessions:[],prompts:[]};u=_.length>0?_[_.length-1].created_at_epoch:s,S=t.length>0?t[t.length-1].created_at_epoch:s}catch(_){return console.error("[SessionStore] Error getting boundary timestamps:",_.message),{observations:[],sessions:[],prompts:[]}}}let T=` SELECT * FROM observations WHERE created_at_epoch >= ? AND created_at_epoch <= ? ${d} ORDER BY created_at_epoch ASC - `,S=` + `,b=` SELECT * FROM session_summaries WHERE created_at_epoch >= ? AND created_at_epoch <= ? ${d} ORDER BY created_at_epoch ASC - `,A=` + `,f=` SELECT up.*, s.project, s.sdk_session_id FROM user_prompts up JOIN sdk_sessions s ON up.claude_session_id = s.claude_session_id WHERE up.created_at_epoch >= ? AND up.created_at_epoch <= ? ${d.replace("project","s.project")} ORDER BY up.created_at_epoch ASC - `;try{let R=this.db.prepare(h).all(l,g,...c),n=this.db.prepare(S).all(l,g,...c),m=this.db.prepare(A).all(l,g,...c);return{observations:R,sessions:n.map(p=>({id:p.id,sdk_session_id:p.sdk_session_id,project:p.project,request:p.request,completed:p.completed,next_steps:p.next_steps,created_at:p.created_at,created_at_epoch:p.created_at_epoch})),prompts:m.map(p=>({id:p.id,claude_session_id:p.claude_session_id,project:p.project,prompt:p.prompt_text,created_at:p.created_at,created_at_epoch:p.created_at_epoch}))}}catch(R){return console.error("[SessionStore] Error querying timeline records:",R.message),{observations:[],sessions:[],prompts:[]}}}close(){this.db.close()}};var Q=["bugfix","feature","refactor","discovery","decision","change"],z=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"],ce={bugfix:"\u{1F534}",feature:"\u{1F7E3}",refactor:"\u{1F504}",change:"\u2705",discovery:"\u{1F535}",decision:"\u2696\uFE0F","session-request":"\u{1F3AF}"},pe={discovery:"\u{1F50D}",change:"\u{1F6E0}\uFE0F",feature:"\u{1F6E0}\uFE0F",bugfix:"\u{1F6E0}\uFE0F",refactor:"\u{1F6E0}\uFE0F",decision:"\u2696\uFE0F"},ue=Q.join(","),le=z.join(",");var ke=Ce(import.meta.url),$e=De(ke),xe=U.join($e,"../../.install-version");function Me(){let u={totalObservationCount:parseInt(process.env.CLAUDE_MEM_CONTEXT_OBSERVATIONS||"50",10),fullObservationCount:5,sessionCount:10,showReadTokens:!0,showWorkTokens:!0,showSavingsAmount:!0,showSavingsPercent:!0,observationTypes:new Set(Q),observationConcepts:new Set(z),fullObservationField:"narrative",showLastSummary:!0,showLastMessage:!1};try{let e=U.join(ve(),".claude","settings.json");if(!ye(e))return u;let t=JSON.parse(Ae(e,"utf-8")).env||{};return{totalObservationCount:parseInt(t.CLAUDE_MEM_CONTEXT_OBSERVATIONS||"50",10),fullObservationCount:parseInt(t.CLAUDE_MEM_CONTEXT_FULL_COUNT||"5",10),sessionCount:parseInt(t.CLAUDE_MEM_CONTEXT_SESSION_COUNT||"10",10),showReadTokens:t.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS!=="false",showWorkTokens:t.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS!=="false",showSavingsAmount:t.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT!=="false",showSavingsPercent:t.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT!=="false",observationTypes:new Set((t.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES||ue).split(",").map(r=>r.trim()).filter(Boolean)),observationConcepts:new Set((t.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS||le).split(",").map(r=>r.trim()).filter(Boolean)),fullObservationField:t.CLAUDE_MEM_CONTEXT_FULL_FIELD||"narrative",showLastSummary:t.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY!=="false",showLastMessage:t.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE==="true"}}catch(e){return W.warn("HOOK","Failed to load context settings, using defaults",{},e),u}}var _e=4,we=1,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",red:"\x1B[31m"};function me(u){if(!u)return[];try{let e=JSON.parse(u);return Array.isArray(e)?e:[]}catch{return[]}}function Ue(u){return new Date(u).toLocaleString("en-US",{month:"short",day:"numeric",hour:"numeric",minute:"2-digit",hour12:!0})}function Fe(u){return new Date(u).toLocaleString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0})}function Xe(u){return new Date(u).toLocaleString("en-US",{month:"short",day:"numeric",year:"numeric"})}function Pe(u,e){return U.isAbsolute(u)?U.relative(e,u):u}function H(u,e,s,t){return e?t?[`${s}${u}:${o.reset} ${e}`,""]:[`**${u}**: ${e}`,""]:[]}async function Ee(u,e=!1){let s=Me(),t=u?.cwd??process.cwd(),r=t?U.basename(t):"unknown-project",i;try{i=new j}catch(m){if(m.code==="ERR_DLOPEN_FAILED"){try{Le(xe)}catch{}console.error("\u26A0\uFE0F Native module rebuild needed - restart Claude Code to auto-fix"),console.error(" (This happens after Node.js version upgrades)"),process.exit(0)}throw m}let d=Array.from(s.observationTypes),c=d.map(()=>"?").join(","),l=Array.from(s.observationConcepts),g=l.map(()=>"?").join(","),h=i.db.prepare(` + `;try{let h=this.db.prepare(T).all(u,S,...a),C=this.db.prepare(b).all(u,S,...a),_=this.db.prepare(f).all(u,S,...a);return{observations:h,sessions:C.map(t=>({id:t.id,sdk_session_id:t.sdk_session_id,project:t.project,request:t.request,completed:t.completed,next_steps:t.next_steps,created_at:t.created_at,created_at_epoch:t.created_at_epoch})),prompts:_.map(t=>({id:t.id,claude_session_id:t.claude_session_id,project:t.project,prompt:t.prompt_text,created_at:t.created_at,created_at_epoch:t.created_at_epoch}))}}catch(h){return console.error("[SessionStore] Error querying timeline records:",h.message),{observations:[],sessions:[],prompts:[]}}}close(){this.db.close()}};var ee=["bugfix","feature","refactor","discovery","decision","change"],se=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"],ue={bugfix:"\u{1F534}",feature:"\u{1F7E3}",refactor:"\u{1F504}",change:"\u2705",discovery:"\u{1F535}",decision:"\u2696\uFE0F","session-request":"\u{1F3AF}"},le={discovery:"\u{1F50D}",change:"\u{1F6E0}\uFE0F",feature:"\u{1F6E0}\uFE0F",bugfix:"\u{1F6E0}\uFE0F",refactor:"\u{1F6E0}\uFE0F",decision:"\u2696\uFE0F"},_e=ee.join(","),me=se.join(",");var xe=De(import.meta.url),Me=$e(xe),we=P.join(Me,"../../.install-version");function Ue(){let p={totalObservationCount:parseInt(process.env.CLAUDE_MEM_CONTEXT_OBSERVATIONS||"50",10),fullObservationCount:5,sessionCount:10,showReadTokens:!0,showWorkTokens:!0,showSavingsAmount:!0,showSavingsPercent:!0,observationTypes:new Set(ee),observationConcepts:new Set(se),fullObservationField:"narrative",showLastSummary:!0,showLastMessage:!1};try{let e=P.join(ge(),".claude","settings.json");if(!Se(e))return p;let r=JSON.parse(be(e,"utf-8")).env||{};return{totalObservationCount:parseInt(r.CLAUDE_MEM_CONTEXT_OBSERVATIONS||"50",10),fullObservationCount:parseInt(r.CLAUDE_MEM_CONTEXT_FULL_COUNT||"5",10),sessionCount:parseInt(r.CLAUDE_MEM_CONTEXT_SESSION_COUNT||"10",10),showReadTokens:r.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS!=="false",showWorkTokens:r.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS!=="false",showSavingsAmount:r.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT!=="false",showSavingsPercent:r.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT!=="false",observationTypes:new Set((r.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES||_e).split(",").map(n=>n.trim()).filter(Boolean)),observationConcepts:new Set((r.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS||me).split(",").map(n=>n.trim()).filter(Boolean)),fullObservationField:r.CLAUDE_MEM_CONTEXT_FULL_FIELD||"narrative",showLastSummary:r.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY!=="false",showLastMessage:r.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE==="true"}}catch(e){return F.warn("HOOK","Failed to load context settings, using defaults",{},e),p}}var Ee=4,Fe=1,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",red:"\x1B[31m"};function Te(p){if(!p)return[];try{let e=JSON.parse(p);return Array.isArray(e)?e:[]}catch{return[]}}function Pe(p){return new Date(p).toLocaleString("en-US",{month:"short",day:"numeric",hour:"numeric",minute:"2-digit",hour12:!0})}function Xe(p){return new Date(p).toLocaleString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0})}function Be(p){return new Date(p).toLocaleString("en-US",{month:"short",day:"numeric",year:"numeric"})}function je(p,e){return P.isAbsolute(p)?P.relative(e,p):p}function V(p,e,s,r){return e?r?[`${s}${p}:${o.reset} ${e}`,""]:[`**${p}**: ${e}`,""]:[]}function We(p){return p.replace(/^\//,"").replace(/\//g,"-")}function He(p){try{if(!Se(p))return{userMessage:"",assistantMessage:""};let e=be(p,"utf-8").trim();if(!e)return{userMessage:"",assistantMessage:""};let s=e.split(` +`).filter(i=>i.trim()),r="",n="";for(let i=s.length-1;i>=0;i--)try{let d=JSON.parse(s[i]);if(!n&&d.type==="assistant"&&d.message?.content){let a="";for(let u of d.message.content)u.type==="text"&&(a+=u.text);a=a.replace(/[\s\S]*?<\/system-reminder>/g,"").trim(),a&&(n=a)}if(!r&&d.type==="user"&&d.message?.content){let a="";for(let u of d.message.content)u.type==="text"&&(a+=u.text);a&&(r=a)}if(r&&n)break}catch{continue}return{userMessage:r,assistantMessage:n}}catch(e){return F.debug("HOOK",`Failed to extract prior messages from ${p}:`,{},e),{userMessage:"",assistantMessage:""}}}async function he(p,e=!1){let s=Ue(),r=p?.cwd??process.cwd(),n=r?P.basename(r):"unknown-project",i;try{i=new Y}catch(v){if(v.code==="ERR_DLOPEN_FAILED"){try{ke(we)}catch{}console.error("\u26A0\uFE0F Native module rebuild needed - restart Claude Code to auto-fix"),console.error(" (This happens after Node.js version upgrades)"),process.exit(0)}throw v}let d=Array.from(s.observationTypes),a=d.map(()=>"?").join(","),u=Array.from(s.observationConcepts),S=u.map(()=>"?").join(","),T=i.db.prepare(` SELECT id, sdk_session_id, type, title, subtitle, narrative, facts, concepts, files_read, files_modified, discovery_tokens, created_at, created_at_epoch FROM observations WHERE project = ? - AND type IN (${c}) + AND type IN (${a}) AND EXISTS ( SELECT 1 FROM json_each(concepts) - WHERE value IN (${g}) + WHERE value IN (${S}) ) ORDER BY created_at_epoch DESC LIMIT ? - `).all(r,...d,...l,s.totalObservationCount),S=i.db.prepare(` + `).all(n,...d,...u,s.totalObservationCount),b=i.db.prepare(` SELECT id, sdk_session_id, request, investigated, learned, completed, next_steps, created_at, created_at_epoch FROM session_summaries WHERE project = ? ORDER BY created_at_epoch DESC LIMIT ? - `).all(r,s.sessionCount+we);if(h.length===0&&S.length===0)return i.close(),e?` -${o.bright}${o.cyan}\u{1F4DD} [${r}] recent context${o.reset} + `).all(n,s.sessionCount+Fe),f="",h="";if(s.showLastMessage&&T.length>0)try{let v=p?.session_id,N=T.find(g=>g.sdk_session_id!==v);if(N){let g=N.sdk_session_id,k=We(r),M=P.join(ge(),".claude","projects",k,`${g}.jsonl`),X=He(M);f=X.userMessage,h=X.assistantMessage}}catch(v){F.debug("HOOK","Failed to retrieve prior session messages:",{},v)}if(T.length===0&&b.length===0)return i.close(),e?` +${o.bright}${o.cyan}\u{1F4DD} [${n}] recent context${o.reset} ${o.gray}${"\u2500".repeat(60)}${o.reset} ${o.dim}No previous sessions found for this project yet.${o.reset} -`:`# [${r}] recent context +`:`# [${n}] recent context -No previous sessions found for this project yet.`;let A=S.slice(0,s.sessionCount),R=h,n=[];if(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("")):(n.push(`# [${r}] recent context`),n.push("")),R.length>0){e?n.push(`${o.dim}Legend: \u{1F3AF} session-request | \u{1F534} bugfix | \u{1F7E3} feature | \u{1F504} refactor | \u2705 change | \u{1F535} discovery | \u2696\uFE0F decision${o.reset}`):n.push("**Legend:** \u{1F3AF} session-request | \u{1F534} bugfix | \u{1F7E3} feature | \u{1F504} refactor | \u2705 change | \u{1F535} discovery | \u2696\uFE0F decision"),n.push(""),e?(n.push(`${o.bright}\u{1F4A1} Column Key${o.reset}`),n.push(`${o.dim} Read: Tokens to read this observation (cost to learn it now)${o.reset}`),n.push(`${o.dim} Work: Tokens spent on work that produced this record (\u{1F50D} research, \u{1F6E0}\uFE0F building, \u2696\uFE0F deciding)${o.reset}`)):(n.push("\u{1F4A1} **Column Key**:"),n.push("- **Read**: Tokens to read this observation (cost to learn it now)"),n.push("- **Work**: Tokens spent on work that produced this record (\u{1F50D} research, \u{1F6E0}\uFE0F building, \u2696\uFE0F deciding)")),n.push(""),e?(n.push(`${o.dim}\u{1F4A1} Context Index: This semantic index (titles, types, files, tokens) is usually sufficient to understand past work.${o.reset}`),n.push(""),n.push(`${o.dim}When you need implementation details, rationale, or debugging context:${o.reset}`),n.push(`${o.dim} - Use the mem-search skill to fetch full observations on-demand${o.reset}`),n.push(`${o.dim} - Critical types (\u{1F534} bugfix, \u2696\uFE0F decision) often need detailed fetching${o.reset}`),n.push(`${o.dim} - Trust this index over re-reading code for past decisions and learnings${o.reset}`)):(n.push("\u{1F4A1} **Context Index:** This semantic index (titles, types, files, tokens) is usually sufficient to understand past work."),n.push(""),n.push("When you need implementation details, rationale, or debugging context:"),n.push("- Use the mem-search skill to fetch full observations on-demand"),n.push("- Critical types (\u{1F534} bugfix, \u2696\uFE0F decision) often need detailed fetching"),n.push("- Trust this index over re-reading code for past decisions and learnings")),n.push("");let m=h.length,p=h.reduce((a,E)=>{let T=(E.title?.length||0)+(E.subtitle?.length||0)+(E.narrative?.length||0)+JSON.stringify(E.facts||[]).length;return a+Math.ceil(T/_e)},0),N=h.reduce((a,E)=>a+(E.discovery_tokens||0),0),L=N-p,$=N>0?Math.round(L/N*100):0,ee=s.showReadTokens||s.showWorkTokens||s.showSavingsAmount||s.showSavingsPercent;if(ee)if(e){if(n.push(`${o.bright}${o.cyan}\u{1F4CA} Context Economics${o.reset}`),n.push(`${o.dim} Loading: ${m} observations (${p.toLocaleString()} tokens to read)${o.reset}`),n.push(`${o.dim} Work investment: ${N.toLocaleString()} tokens spent on research, building, and decisions${o.reset}`),N>0&&(s.showSavingsAmount||s.showSavingsPercent)){let a=" Your savings: ";s.showSavingsAmount&&s.showSavingsPercent?a+=`${L.toLocaleString()} tokens (${$}% reduction from reuse)`:s.showSavingsAmount?a+=`${L.toLocaleString()} tokens`:a+=`${$}% reduction from reuse`,n.push(`${o.green}${a}${o.reset}`)}n.push("")}else{if(n.push("\u{1F4CA} **Context Economics**:"),n.push(`- Loading: ${m} observations (${p.toLocaleString()} tokens to read)`),n.push(`- Work investment: ${N.toLocaleString()} tokens spent on research, building, and decisions`),N>0&&(s.showSavingsAmount||s.showSavingsPercent)){let a="- Your savings: ";s.showSavingsAmount&&s.showSavingsPercent?a+=`${L.toLocaleString()} tokens (${$}% reduction from reuse)`:s.showSavingsAmount?a+=`${L.toLocaleString()} tokens`:a+=`${$}% reduction from reuse`,n.push(a)}n.push("")}let Te=S[0]?.id,he=A.map((a,E)=>{let T=E===0?null:S[E+1];return{...a,displayEpoch:T?T.created_at_epoch:a.created_at_epoch,displayTime:T?T.created_at:a.created_at,shouldShowLink:a.id!==Te}}),ge=new Set(h.slice(0,s.fullObservationCount).map(a=>a.id)),se=[...R.map(a=>({type:"observation",data:a})),...he.map(a=>({type:"summary",data:a}))];se.sort((a,E)=>{let T=a.type==="observation"?a.data.created_at_epoch:a.data.displayEpoch,C=E.type==="observation"?E.data.created_at_epoch:E.data.displayEpoch;return T-C});let F=new Map;for(let a of se){let E=a.type==="observation"?a.data.created_at:a.data.displayTime,T=Xe(E);F.has(T)||F.set(T,[]),F.get(T).push(a)}let Se=Array.from(F.entries()).sort((a,E)=>{let T=new Date(a[0]).getTime(),C=new Date(E[0]).getTime();return T-C});for(let[a,E]of Se){e?(n.push(`${o.bright}${o.cyan}${a}${o.reset}`),n.push("")):(n.push(`### ${a}`),n.push(""));let T=null,C="",k=!1;for(let G of E)if(G.type==="summary"){k&&(n.push(""),k=!1,T=null,C="");let _=G.data,w=`${_.request||"Session started"} (${Ue(_.displayTime)})`,v=_.shouldShowLink?`claude-mem://session-summary/${_.id}`:"";if(e){let O=v?`${o.dim}[${v}]${o.reset}`:"";n.push(`\u{1F3AF} ${o.yellow}#S${_.id}${o.reset} ${w} ${O}`)}else{let O=v?` [\u2192](${v})`:"";n.push(`**\u{1F3AF} #S${_.id}** ${w}${O}`)}n.push("")}else{let _=G.data,w=me(_.files_modified),v=w.length>0?Pe(w[0],t):"General";v!==T&&(k&&n.push(""),e?n.push(`${o.dim}${v}${o.reset}`):n.push(`**${v}**`),e||(n.push("| ID | Time | T | Title | Read | Work |"),n.push("|----|------|---|-------|------|------|")),T=v,k=!0,C="");let O=Fe(_.created_at),X=_.title||"Untitled",P=ce[_.type]||"\u2022",be=(_.title?.length||0)+(_.subtitle?.length||0)+(_.narrative?.length||0)+JSON.stringify(_.facts||[]).length,x=Math.ceil(be/_e),M=_.discovery_tokens||0,Y=pe[_.type]||"\u{1F50D}",re=M>0?`${Y} ${M.toLocaleString()}`:"-",V=O!==C,ne=V?O:"";if(C=O,ge.has(_.id)){let D=s.fullObservationField==="narrative"?_.narrative:_.facts?me(_.facts).join(` -`):null;if(e){let I=V?`${o.dim}${O}${o.reset}`:" ".repeat(O.length),B=s.showReadTokens&&x>0?`${o.dim}(~${x}t)${o.reset}`:"",oe=s.showWorkTokens&&M>0?`${o.dim}(${Y} ${M.toLocaleString()}t)${o.reset}`:"";n.push(` ${o.dim}#${_.id}${o.reset} ${I} ${P} ${o.bright}${X}${o.reset}`),D&&n.push(` ${o.dim}${D}${o.reset}`),(B||oe)&&n.push(` ${B} ${oe}`),n.push("")}else{k&&(n.push(""),k=!1),n.push(`**#${_.id}** ${ne||"\u2033"} ${P} **${X}**`),D&&(n.push(""),n.push(D),n.push(""));let I=[];s.showReadTokens&&I.push(`Read: ~${x}`),s.showWorkTokens&&I.push(`Work: ${re}`),I.length>0&&n.push(I.join(", ")),n.push(""),T=null}}else if(e){let D=V?`${o.dim}${O}${o.reset}`:" ".repeat(O.length),I=s.showReadTokens&&x>0?`${o.dim}(~${x}t)${o.reset}`:"",B=s.showWorkTokens&&M>0?`${o.dim}(${Y} ${M.toLocaleString()}t)${o.reset}`:"";n.push(` ${o.dim}#${_.id}${o.reset} ${D} ${P} ${X} ${I} ${B}`)}else{let D=s.showReadTokens?`~${x}`:"",I=s.showWorkTokens?re:"";n.push(`| #${_.id} | ${ne||"\u2033"} | ${P} | ${X} | ${D} | ${I} |`)}}k&&n.push("")}let b=S[0],te=h[0];if(s.showLastSummary&&b&&(b.investigated||b.learned||b.completed||b.next_steps)&&(!te||b.created_at_epoch>te.created_at_epoch)&&(n.push(...H("Investigated",b.investigated,o.blue,e)),n.push(...H("Learned",b.learned,o.yellow,e)),n.push(...H("Completed",b.completed,o.green,e)),n.push(...H("Next Steps",b.next_steps,o.magenta,e))),s.showLastMessage&&b){let a=b.last_assistant_message;a&&(n.push(""),e?(n.push(`${o.bright}${o.magenta}\u{1F4AC} Last Message from Previous Session${o.reset}`),n.push(`${o.dim}${a}${o.reset}`)):(n.push("**\u{1F4AC} Last Message from Previous Session**"),n.push(""),n.push(a)),n.push(""))}if(ee&&N>0&&L>0){let a=Math.round(N/1e3);n.push(""),e?n.push(`${o.dim}\u{1F4B0} Access ${a}k tokens of past research & decisions for just ${p.toLocaleString()}t. Use the mem-search skill to access memories by ID instead of re-reading files.${o.reset}`):n.push(`\u{1F4B0} Access ${a}k tokens of past research & decisions for just ${p.toLocaleString()}t. Use the mem-search skill to access memories by ID instead of re-reading files.`)}}return i.close(),n.join(` -`).trimEnd()}var Be=process.argv.includes("--colors");if(Z.isTTY||Be)Ee(void 0,!0).then(u=>{console.log(u),process.exit(0)});else{let u="";Z.on("data",e=>u+=e),Z.on("end",async()=>{let e=u.trim()?JSON.parse(u):void 0,t={hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:await Ee(e,!1)}};console.log(JSON.stringify(t)),process.exit(0)})} +No previous sessions found for this project yet.`;let C=b.slice(0,s.sessionCount),_=T,t=[];if(e?(t.push(""),t.push(`${o.bright}${o.cyan}\u{1F4DD} [${n}] recent context${o.reset}`),t.push(`${o.gray}${"\u2500".repeat(60)}${o.reset}`),t.push("")):(t.push(`# [${n}] recent context`),t.push("")),(f||h)&&(e?(t.push(`${o.bright}${o.magenta}\u{1F4CB} Previously${o.reset}`),t.push(""),f&&(t.push(`${o.dim}User: ${f}${o.reset}`),t.push("")),h&&(t.push(`${o.dim}Assistant: ${h}${o.reset}`),t.push("")),t.push(`${o.gray}${"\u2500".repeat(60)}${o.reset}`),t.push("")):(t.push("**\u{1F4CB} Previously**"),t.push(""),f&&(t.push(`User: ${f}`),t.push("")),h&&(t.push(`Assistant: ${h}`),t.push("")),t.push("---"),t.push(""))),_.length>0){e?t.push(`${o.dim}Legend: \u{1F3AF} session-request | \u{1F534} bugfix | \u{1F7E3} feature | \u{1F504} refactor | \u2705 change | \u{1F535} discovery | \u2696\uFE0F decision${o.reset}`):t.push("**Legend:** \u{1F3AF} session-request | \u{1F534} bugfix | \u{1F7E3} feature | \u{1F504} refactor | \u2705 change | \u{1F535} discovery | \u2696\uFE0F decision"),t.push(""),e?(t.push(`${o.bright}\u{1F4A1} Column Key${o.reset}`),t.push(`${o.dim} Read: Tokens to read this observation (cost to learn it now)${o.reset}`),t.push(`${o.dim} Work: Tokens spent on work that produced this record (\u{1F50D} research, \u{1F6E0}\uFE0F building, \u2696\uFE0F deciding)${o.reset}`)):(t.push("\u{1F4A1} **Column Key**:"),t.push("- **Read**: Tokens to read this observation (cost to learn it now)"),t.push("- **Work**: Tokens spent on work that produced this record (\u{1F50D} research, \u{1F6E0}\uFE0F building, \u2696\uFE0F deciding)")),t.push(""),e?(t.push(`${o.dim}\u{1F4A1} Context Index: This semantic index (titles, types, files, tokens) is usually sufficient to understand past work.${o.reset}`),t.push(""),t.push(`${o.dim}When you need implementation details, rationale, or debugging context:${o.reset}`),t.push(`${o.dim} - Use the mem-search skill to fetch full observations on-demand${o.reset}`),t.push(`${o.dim} - Critical types (\u{1F534} bugfix, \u2696\uFE0F decision) often need detailed fetching${o.reset}`),t.push(`${o.dim} - Trust this index over re-reading code for past decisions and learnings${o.reset}`)):(t.push("\u{1F4A1} **Context Index:** This semantic index (titles, types, files, tokens) is usually sufficient to understand past work."),t.push(""),t.push("When you need implementation details, rationale, or debugging context:"),t.push("- Use the mem-search skill to fetch full observations on-demand"),t.push("- Critical types (\u{1F534} bugfix, \u2696\uFE0F decision) often need detailed fetching"),t.push("- Trust this index over re-reading code for past decisions and learnings")),t.push("");let v=T.length,N=T.reduce((c,m)=>{let E=(m.title?.length||0)+(m.subtitle?.length||0)+(m.narrative?.length||0)+JSON.stringify(m.facts||[]).length;return c+Math.ceil(E/Ee)},0),g=T.reduce((c,m)=>c+(m.discovery_tokens||0),0),k=g-N,M=g>0?Math.round(k/g*100):0,X=s.showReadTokens||s.showWorkTokens||s.showSavingsAmount||s.showSavingsPercent;if(X)if(e){if(t.push(`${o.bright}${o.cyan}\u{1F4CA} Context Economics${o.reset}`),t.push(`${o.dim} Loading: ${v} observations (${N.toLocaleString()} tokens to read)${o.reset}`),t.push(`${o.dim} Work investment: ${g.toLocaleString()} tokens spent on research, building, and decisions${o.reset}`),g>0&&(s.showSavingsAmount||s.showSavingsPercent)){let c=" Your savings: ";s.showSavingsAmount&&s.showSavingsPercent?c+=`${k.toLocaleString()} tokens (${M}% reduction from reuse)`:s.showSavingsAmount?c+=`${k.toLocaleString()} tokens`:c+=`${M}% reduction from reuse`,t.push(`${o.green}${c}${o.reset}`)}t.push("")}else{if(t.push("\u{1F4CA} **Context Economics**:"),t.push(`- Loading: ${v} observations (${N.toLocaleString()} tokens to read)`),t.push(`- Work investment: ${g.toLocaleString()} tokens spent on research, building, and decisions`),g>0&&(s.showSavingsAmount||s.showSavingsPercent)){let c="- Your savings: ";s.showSavingsAmount&&s.showSavingsPercent?c+=`${k.toLocaleString()} tokens (${M}% reduction from reuse)`:s.showSavingsAmount?c+=`${k.toLocaleString()} tokens`:c+=`${M}% reduction from reuse`,t.push(c)}t.push("")}let fe=b[0]?.id,Re=C.map((c,m)=>{let E=m===0?null:b[m+1];return{...c,displayEpoch:E?E.created_at_epoch:c.created_at_epoch,displayTime:E?E.created_at:c.created_at,shouldShowLink:c.id!==fe}}),Oe=new Set(T.slice(0,s.fullObservationCount).map(c=>c.id)),re=[..._.map(c=>({type:"observation",data:c})),...Re.map(c=>({type:"summary",data:c}))];re.sort((c,m)=>{let E=c.type==="observation"?c.data.created_at_epoch:c.data.displayEpoch,D=m.type==="observation"?m.data.created_at_epoch:m.data.displayEpoch;return E-D});let j=new Map;for(let c of re){let m=c.type==="observation"?c.data.created_at:c.data.displayTime,E=Be(m);j.has(E)||j.set(E,[]),j.get(E).push(c)}let Ne=Array.from(j.entries()).sort((c,m)=>{let E=new Date(c[0]).getTime(),D=new Date(m[0]).getTime();return E-D});for(let[c,m]of Ne){e?(t.push(`${o.bright}${o.cyan}${c}${o.reset}`),t.push("")):(t.push(`### ${c}`),t.push(""));let E=null,D="",x=!1;for(let K of m)if(K.type==="summary"){x&&(t.push(""),x=!1,E=null,D="");let l=K.data,B=`${l.request||"Session started"} (${Pe(l.displayTime)})`,A=l.shouldShowLink?`claude-mem://session-summary/${l.id}`:"";if(e){let I=A?`${o.dim}[${A}]${o.reset}`:"";t.push(`\u{1F3AF} ${o.yellow}#S${l.id}${o.reset} ${B} ${I}`)}else{let I=A?` [\u2192](${A})`:"";t.push(`**\u{1F3AF} #S${l.id}** ${B}${I}`)}t.push("")}else{let l=K.data,B=Te(l.files_modified),A=B.length>0?je(B[0],r):"General";A!==E&&(x&&t.push(""),e?t.push(`${o.dim}${A}${o.reset}`):t.push(`**${A}**`),e||(t.push("| ID | Time | T | Title | Read | Work |"),t.push("|----|------|---|-------|------|------|")),E=A,x=!0,D="");let I=Xe(l.created_at),W=l.title||"Untitled",H=ue[l.type]||"\u2022",Ie=(l.title?.length||0)+(l.subtitle?.length||0)+(l.narrative?.length||0)+JSON.stringify(l.facts||[]).length,w=Math.ceil(Ie/Ee),U=l.discovery_tokens||0,q=le[l.type]||"\u{1F50D}",oe=U>0?`${q} ${U.toLocaleString()}`:"-",J=I!==D,ie=J?I:"";if(D=I,Oe.has(l.id)){let $=s.fullObservationField==="narrative"?l.narrative:l.facts?Te(l.facts).join(` +`):null;if(e){let y=J?`${o.dim}${I}${o.reset}`:" ".repeat(I.length),G=s.showReadTokens&&w>0?`${o.dim}(~${w}t)${o.reset}`:"",ae=s.showWorkTokens&&U>0?`${o.dim}(${q} ${U.toLocaleString()}t)${o.reset}`:"";t.push(` ${o.dim}#${l.id}${o.reset} ${y} ${H} ${o.bright}${W}${o.reset}`),$&&t.push(` ${o.dim}${$}${o.reset}`),(G||ae)&&t.push(` ${G} ${ae}`),t.push("")}else{x&&(t.push(""),x=!1),t.push(`**#${l.id}** ${ie||"\u2033"} ${H} **${W}**`),$&&(t.push(""),t.push($),t.push(""));let y=[];s.showReadTokens&&y.push(`Read: ~${w}`),s.showWorkTokens&&y.push(`Work: ${oe}`),y.length>0&&t.push(y.join(", ")),t.push(""),E=null}}else if(e){let $=J?`${o.dim}${I}${o.reset}`:" ".repeat(I.length),y=s.showReadTokens&&w>0?`${o.dim}(~${w}t)${o.reset}`:"",G=s.showWorkTokens&&U>0?`${o.dim}(${q} ${U.toLocaleString()}t)${o.reset}`:"";t.push(` ${o.dim}#${l.id}${o.reset} ${$} ${H} ${W} ${y} ${G}`)}else{let $=s.showReadTokens?`~${w}`:"",y=s.showWorkTokens?oe:"";t.push(`| #${l.id} | ${ie||"\u2033"} | ${H} | ${W} | ${$} | ${y} |`)}}x&&t.push("")}let R=b[0],ne=T[0];if(s.showLastSummary&&R&&(R.investigated||R.learned||R.completed||R.next_steps)&&(!ne||R.created_at_epoch>ne.created_at_epoch)&&(t.push(...V("Investigated",R.investigated,o.blue,e)),t.push(...V("Learned",R.learned,o.yellow,e)),t.push(...V("Completed",R.completed,o.green,e)),t.push(...V("Next Steps",R.next_steps,o.magenta,e))),s.showLastMessage&&R){let c=R.last_assistant_message;c&&(t.push(""),e?(t.push(`${o.bright}${o.magenta}\u{1F4AC} Last Message from Previous Session${o.reset}`),t.push(`${o.dim}${c}${o.reset}`)):(t.push("**\u{1F4AC} Last Message from Previous Session**"),t.push(""),t.push(c)),t.push(""))}if(X&&g>0&&k>0){let c=Math.round(g/1e3);t.push(""),e?t.push(`${o.dim}\u{1F4B0} Access ${c}k tokens of past research & decisions for just ${N.toLocaleString()}t. Use the mem-search skill to access memories by ID instead of re-reading files.${o.reset}`):t.push(`\u{1F4B0} Access ${c}k tokens of past research & decisions for just ${N.toLocaleString()}t. Use the mem-search skill to access memories by ID instead of re-reading files.`)}}return i.close(),t.join(` +`).trimEnd()}var Ge=process.argv.includes("--colors");if(te.isTTY||Ge)he(void 0,!0).then(p=>{console.log(p),process.exit(0)});else{let p="";te.on("data",e=>p+=e),te.on("end",async()=>{let e=p.trim()?JSON.parse(p):void 0,r={hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:await he(e,!1)}};console.log(JSON.stringify(r)),process.exit(0)})} diff --git a/src/hooks/context-hook.ts b/src/hooks/context-hook.ts index 35c2b1ee..ce5e8796 100644 --- a/src/hooks/context-hook.ts +++ b/src/hooks/context-hook.ts @@ -219,6 +219,78 @@ function renderSummaryField(label: string, value: string | null, color: string, return [`**${label}**: ${value}`, '']; } +// Helper: Convert cwd path to dashed format for transcript directory name +function cwdToDashed(cwd: string): string { + // Remove leading slash and convert remaining slashes to dashes + return cwd.replace(/^\//, '').replace(/\//g, '-'); +} + +// Helper: Extract last user and assistant messages from transcript file +function extractPriorMessages(transcriptPath: string): { userMessage: string; assistantMessage: string } { + try { + if (!existsSync(transcriptPath)) { + return { userMessage: '', assistantMessage: '' }; + } + + const content = readFileSync(transcriptPath, 'utf-8').trim(); + if (!content) { + return { userMessage: '', assistantMessage: '' }; + } + + const lines = content.split('\n').filter(line => line.trim()); + let lastUserMessage = ''; + let lastAssistantMessage = ''; + + // Parse JSONL backwards to find last user and assistant messages + for (let i = lines.length - 1; i >= 0; i--) { + try { + const entry = JSON.parse(lines[i]); + + // Find last assistant message + if (!lastAssistantMessage && entry.type === 'assistant' && entry.message?.content) { + let text = ''; + for (const block of entry.message.content) { + if (block.type === 'text') { + text += block.text; + } + } + // Remove system-reminder tags + text = text.replace(/[\s\S]*?<\/system-reminder>/g, '').trim(); + if (text) { + lastAssistantMessage = text; + } + } + + // Find last user message + if (!lastUserMessage && entry.type === 'user' && entry.message?.content) { + let text = ''; + for (const block of entry.message.content) { + if (block.type === 'text') { + text += block.text; + } + } + if (text) { + lastUserMessage = text; + } + } + + // Stop once we have both + if (lastUserMessage && lastAssistantMessage) { + break; + } + } catch (parseError) { + // Skip malformed lines + continue; + } + } + + return { userMessage: lastUserMessage, assistantMessage: lastAssistantMessage }; + } catch (error) { + logger.debug('HOOK', `Failed to extract prior messages from ${transcriptPath}:`, {}, error as Error); + return { userMessage: '', assistantMessage: '' }; + } +} + /** * Context Hook Main Logic */ @@ -286,6 +358,33 @@ async function contextHook(input?: SessionStartInput, useColors: boolean = false LIMIT ? `).all(project, config.sessionCount + SUMMARY_LOOKAHEAD) as SessionSummary[]; + // Retrieve prior session messages if enabled + let priorUserMessage = ''; + let priorAssistantMessage = ''; + if (config.showLastMessage && observations.length > 0) { + try { + const currentSessionId = input?.session_id; + + // Find the first observation from a different session (the prior session) + const priorSessionObs = observations.find(obs => obs.sdk_session_id !== currentSessionId); + + if (priorSessionObs) { + const priorSessionId = priorSessionObs.sdk_session_id; + + // Construct transcript path: ~/.claude/projects/{dashed-cwd}/{session_id}.jsonl + const dashedCwd = cwdToDashed(cwd); + const transcriptPath = path.join(homedir(), '.claude', 'projects', dashedCwd, `${priorSessionId}.jsonl`); + + // Extract messages from transcript + const messages = extractPriorMessages(transcriptPath); + priorUserMessage = messages.userMessage; + priorAssistantMessage = messages.assistantMessage; + } + } catch (error) { + logger.debug('HOOK', 'Failed to retrieve prior session messages:', {}, error as Error); + } + } + // If we have neither observations nor summaries, show empty state if (observations.length === 0 && recentSummaries.length === 0) { db.close(); @@ -314,6 +413,37 @@ async function contextHook(input?: SessionStartInput, useColors: boolean = false output.push(''); } + // Previously section (last messages from prior session) + if (priorUserMessage || priorAssistantMessage) { + if (useColors) { + output.push(`${colors.bright}${colors.magenta}📋 Previously${colors.reset}`); + output.push(''); + if (priorUserMessage) { + output.push(`${colors.dim}User: ${priorUserMessage}${colors.reset}`); + output.push(''); + } + if (priorAssistantMessage) { + output.push(`${colors.dim}Assistant: ${priorAssistantMessage}${colors.reset}`); + output.push(''); + } + output.push(`${colors.gray}${'─'.repeat(60)}${colors.reset}`); + output.push(''); + } else { + output.push(`**📋 Previously**`); + output.push(''); + if (priorUserMessage) { + output.push(`User: ${priorUserMessage}`); + output.push(''); + } + if (priorAssistantMessage) { + output.push(`Assistant: ${priorAssistantMessage}`); + output.push(''); + } + output.push('---'); + output.push(''); + } + } + // Chronological Timeline if (timelineObs.length > 0) { // Legend/Key