Fix: Restore hookSpecificOutput JSON format for SessionStart hook
The previous 'fix' to output plain text broke context injection. SessionStart hooks require hookSpecificOutput JSON format. Changes: - Restore JSON output with hookSpecificOutput wrapper - Keep improved error handling with JSON error output - Tested with real SessionStart hook input format
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
#!/usr/bin/env node
|
||||
import G from"path";import de from"better-sqlite3";import{join as T,dirname as re,basename as fe}from"path";import{homedir as Y}from"os";import{existsSync as Ne,mkdirSync as ne}from"fs";import{fileURLToPath as ie}from"url";function oe(){return typeof __dirname<"u"?__dirname:re(ie(import.meta.url))}var ae=oe(),I=process.env.CLAUDE_MEM_DATA_DIR||T(Y(),".claude-mem"),U=process.env.CLAUDE_CONFIG_DIR||T(Y(),".claude"),Oe=T(I,"archives"),Le=T(I,"logs"),ve=T(I,"trash"),Ae=T(I,"backups"),ye=T(I,"settings.json"),K=T(I,"claude-mem.db"),De=T(U,"settings.json"),ke=T(U,"commands"),Ce=T(U,"CLAUDE.md");function V(o){ne(o,{recursive:!0})}function q(){return T(ae,"..","..")}var $=(a=>(a[a.DEBUG=0]="DEBUG",a[a.INFO=1]="INFO",a[a.WARN=2]="WARN",a[a.ERROR=3]="ERROR",a[a.SILENT=4]="SILENT",a))($||{}),M=class{level;useColor;constructor(){let e=process.env.CLAUDE_MEM_LOG_LEVEL?.toUpperCase()||"INFO";this.level=$[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,a){if(e<this.level)return;let c=new Date().toISOString().replace("T"," ").substring(0,23),p=$[e].padEnd(5),u=s.padEnd(6),O="";r?.correlationId?O=`[${r.correlationId}] `:r?.sessionId&&(O=`[session-${r.sessionId}] `);let S="";a!=null&&(this.level===0&&typeof a=="object"?S=`
|
||||
`+JSON.stringify(a,null,2):S=" "+this.formatData(a));let n="";if(r){let{sessionId:h,sdkSessionId:C,correlationId:L,...D}=r;Object.keys(D).length>0&&(n=` {${Object.entries(D).map(([d,m])=>`${d}=${m}`).join(", ")}}`)}let R=`[${c}] [${p}] [${u}] ${O}${t}${n}${S}`;e===3?console.error(R):console.log(R)}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`})}},J=new M;var k=class{db;constructor(){V(I),this.db=new de(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 G from"path";import de from"better-sqlite3";import{join as T,dirname as re,basename as fe}from"path";import{homedir as Y}from"os";import{existsSync as Re,mkdirSync as ne}from"fs";import{fileURLToPath as ie}from"url";function oe(){return typeof __dirname<"u"?__dirname:re(ie(import.meta.url))}var ae=oe(),I=process.env.CLAUDE_MEM_DATA_DIR||T(Y(),".claude-mem"),U=process.env.CLAUDE_CONFIG_DIR||T(Y(),".claude"),Oe=T(I,"archives"),Le=T(I,"logs"),ve=T(I,"trash"),Ae=T(I,"backups"),ye=T(I,"settings.json"),K=T(I,"claude-mem.db"),ke=T(U,"settings.json"),De=T(U,"commands"),Ce=T(U,"CLAUDE.md");function V(o){ne(o,{recursive:!0})}function q(){return T(ae,"..","..")}var $=(a=>(a[a.DEBUG=0]="DEBUG",a[a.INFO=1]="INFO",a[a.WARN=2]="WARN",a[a.ERROR=3]="ERROR",a[a.SILENT=4]="SILENT",a))($||{}),M=class{level;useColor;constructor(){let e=process.env.CLAUDE_MEM_LOG_LEVEL?.toUpperCase()||"INFO";this.level=$[e]??1,this.useColor=process.stdout.isTTY??!1}correlationId(e,t){return`obs-${e}-${t}`}sessionId(e){return`session-${e}`}formatData(e){if(e==null)return"";if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="boolean")return e.toString();if(typeof e=="object"){if(e instanceof Error)return this.level===0?`${e.message}
|
||||
${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let t=Object.keys(e);return t.length===0?"{}":t.length<=3?JSON.stringify(e):`{${t.length} keys: ${t.slice(0,3).join(", ")}...}`}return String(e)}formatTool(e,t){if(!t)return e;try{let s=typeof t=="string"?JSON.parse(t):t;if(e==="Bash"&&s.command){let r=s.command.length>50?s.command.substring(0,50)+"...":s.command;return`${e}(${r})`}if(e==="Read"&&s.file_path){let r=s.file_path.split("/").pop()||s.file_path;return`${e}(${r})`}if(e==="Edit"&&s.file_path){let r=s.file_path.split("/").pop()||s.file_path;return`${e}(${r})`}if(e==="Write"&&s.file_path){let r=s.file_path.split("/").pop()||s.file_path;return`${e}(${r})`}return e}catch{return e}}log(e,t,s,r,a){if(e<this.level)return;let c=new Date().toISOString().replace("T"," ").substring(0,23),p=$[e].padEnd(5),u=t.padEnd(6),O="";r?.correlationId?O=`[${r.correlationId}] `:r?.sessionId&&(O=`[session-${r.sessionId}] `);let b="";a!=null&&(this.level===0&&typeof a=="object"?b=`
|
||||
`+JSON.stringify(a,null,2):b=" "+this.formatData(a));let n="";if(r){let{sessionId:h,sdkSessionId:C,correlationId:L,...k}=r;Object.keys(k).length>0&&(n=` {${Object.entries(k).map(([d,m])=>`${d}=${m}`).join(", ")}}`)}let N=`[${c}] [${p}] [${u}] ${O}${s}${n}${b}`;e===3?console.error(N):console.log(N)}debug(e,t,s,r){this.log(0,e,t,s,r)}info(e,t,s,r){this.log(1,e,t,s,r)}warn(e,t,s,r){this.log(2,e,t,s,r)}error(e,t,s,r){this.log(3,e,t,s,r)}dataIn(e,t,s,r){this.info(e,`\u2192 ${t}`,s,r)}dataOut(e,t,s,r){this.info(e,`\u2190 ${t}`,s,r)}success(e,t,s,r){this.info(e,`\u2713 ${t}`,s,r)}failure(e,t,s,r){this.error(e,`\u2717 ${t}`,s,r)}timing(e,t,s,r){this.info(e,`\u23F1 ${t}`,r,{duration:`${s}ms`})}},J=new M;var D=class{db;constructor(){V(I),this.db=new de(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,
|
||||
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(s=>s.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,
|
||||
@@ -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 s=this.db.pragma("table_info(observations)").find(r=>r.name==="text");if(!s||s.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,
|
||||
@@ -166,7 +166,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)}}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(s){throw this.db.exec("ROLLBACK"),s}}catch(e){console.error("[SessionStore] Migration error (create user_prompts table):",e.message)}}getRecentSummaries(e,t=10){return this.db.prepare(`
|
||||
SELECT
|
||||
request, investigated, learned, completed, next_steps,
|
||||
files_read, files_edited, notes, prompt_number, created_at
|
||||
@@ -174,7 +174,7 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje
|
||||
WHERE project = ?
|
||||
ORDER BY created_at_epoch DESC
|
||||
LIMIT ?
|
||||
`).all(e,s)}getRecentSummariesWithSessionInfo(e,s=3){return this.db.prepare(`
|
||||
`).all(e,t)}getRecentSummariesWithSessionInfo(e,t=3){return this.db.prepare(`
|
||||
SELECT
|
||||
sdk_session_id, request, learned, completed, next_steps,
|
||||
prompt_number, created_at
|
||||
@@ -182,13 +182,13 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje
|
||||
WHERE project = ?
|
||||
ORDER BY created_at_epoch DESC
|
||||
LIMIT ?
|
||||
`).all(e,s)}getRecentObservations(e,s=20){return this.db.prepare(`
|
||||
`).all(e,t)}getRecentObservations(e,t=20){return this.db.prepare(`
|
||||
SELECT type, text, prompt_number, created_at
|
||||
FROM observations
|
||||
WHERE project = ?
|
||||
ORDER BY created_at_epoch DESC
|
||||
LIMIT ?
|
||||
`).all(e,s)}getRecentSessionsWithStatus(e,s=3){return this.db.prepare(`
|
||||
`).all(e,t)}getRecentSessionsWithStatus(e,t=3){return this.db.prepare(`
|
||||
SELECT * FROM (
|
||||
SELECT
|
||||
s.sdk_session_id,
|
||||
@@ -205,7 +205,7 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje
|
||||
LIMIT ?
|
||||
)
|
||||
ORDER BY started_at_epoch ASC
|
||||
`).all(e,s)}getObservationsForSession(e){return this.db.prepare(`
|
||||
`).all(e,t)}getObservationsForSession(e){return this.db.prepare(`
|
||||
SELECT title, subtitle, type, prompt_number
|
||||
FROM observations
|
||||
WHERE sdk_session_id = ?
|
||||
@@ -218,11 +218,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 s=this.db.prepare(`
|
||||
SELECT files_read, files_modified
|
||||
FROM observations
|
||||
WHERE sdk_session_id = ?
|
||||
`).all(e),r=new Set,a=new Set;for(let c of t){if(c.files_read)try{let p=JSON.parse(c.files_read);Array.isArray(p)&&p.forEach(u=>r.add(u))}catch{}if(c.files_modified)try{let p=JSON.parse(c.files_modified);Array.isArray(p)&&p.forEach(u=>a.add(u))}catch{}}return{filesRead:Array.from(r),filesModified:Array.from(a)}}getSessionById(e){return this.db.prepare(`
|
||||
`).all(e),r=new Set,a=new Set;for(let c of s){if(c.files_read)try{let p=JSON.parse(c.files_read);Array.isArray(p)&&p.forEach(u=>r.add(u))}catch{}if(c.files_modified)try{let p=JSON.parse(c.files_modified);Array.isArray(p)&&p.forEach(u=>a.add(u))}catch{}}return{filesRead:Array.from(r),filesModified:Array.from(a)}}getSessionById(e){return this.db.prepare(`
|
||||
SELECT id, claude_session_id, sdk_session_id, project, user_prompt
|
||||
FROM sdk_sessions
|
||||
WHERE id = ?
|
||||
@@ -237,11 +237,11 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje
|
||||
FROM sdk_sessions
|
||||
WHERE claude_session_id = ?
|
||||
LIMIT 1
|
||||
`).get(e)||null}reactivateSession(e,s){this.db.prepare(`
|
||||
`).get(e)||null}reactivateSession(e,t){this.db.prepare(`
|
||||
UPDATE sdk_sessions
|
||||
SET status = 'active', user_prompt = ?, worker_port = NULL
|
||||
WHERE id = ?
|
||||
`).run(s,e)}incrementPromptCounter(e){return this.db.prepare(`
|
||||
`).run(t,e)}incrementPromptCounter(e){return this.db.prepare(`
|
||||
UPDATE sdk_sessions
|
||||
SET prompt_counter = COALESCE(prompt_counter, 0) + 1
|
||||
WHERE id = ?
|
||||
@@ -249,72 +249,72 @@ ${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,a=r.getTime(),p=this.db.prepare(`
|
||||
`).get(e)?.prompt_counter||0}createSDKSession(e,t,s){let r=new Date,a=r.getTime(),p=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(),a);return p.lastInsertRowid===0||p.changes===0?this.db.prepare(`
|
||||
`).run(e,e,t,s,r.toISOString(),a);return p.lastInsertRowid===0||p.changes===0?this.db.prepare(`
|
||||
SELECT id FROM sdk_sessions WHERE claude_session_id = ? LIMIT 1
|
||||
`).get(e).id:p.lastInsertRowid}updateSDKSessionId(e,s){return this.db.prepare(`
|
||||
`).get(e).id:p.lastInsertRowid}updateSDKSessionId(e,t){return this.db.prepare(`
|
||||
UPDATE sdk_sessions
|
||||
SET sdk_session_id = ?
|
||||
WHERE id = ? AND sdk_session_id IS NULL
|
||||
`).run(s,e).changes===0?(J.debug("DB","sdk_session_id already set, skipping update",{sessionId:e,sdkSessionId:s}),!1):!0}setWorkerPort(e,s){this.db.prepare(`
|
||||
`).run(t,e).changes===0?(J.debug("DB","sdk_session_id already set, skipping update",{sessionId:e,sdkSessionId:t}),!1):!0}setWorkerPort(e,t){this.db.prepare(`
|
||||
UPDATE sdk_sessions
|
||||
SET worker_port = ?
|
||||
WHERE id = ?
|
||||
`).run(s,e)}getWorkerPort(e){return this.db.prepare(`
|
||||
`).run(t,e)}getWorkerPort(e){return this.db.prepare(`
|
||||
SELECT worker_port
|
||||
FROM sdk_sessions
|
||||
WHERE id = ?
|
||||
LIMIT 1
|
||||
`).get(e)?.worker_port||null}saveUserPrompt(e,s,t){let r=new Date,a=r.getTime();return this.db.prepare(`
|
||||
`).get(e)?.worker_port||null}saveUserPrompt(e,t,s){let r=new Date,a=r.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(),a).lastInsertRowid}storeObservation(e,s,t,r){let a=new Date,c=a.getTime();this.db.prepare(`
|
||||
`).run(e,t,s,r.toISOString(),a).lastInsertRowid}storeObservation(e,t,s,r){let a=new Date,c=a.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,a.toISOString(),c),console.error(`[SessionStore] Auto-created session record for session_id: ${e}`)),this.db.prepare(`
|
||||
`).run(e,e,t,a.toISOString(),c),console.error(`[SessionStore] Auto-created session record for session_id: ${e}`)),this.db.prepare(`
|
||||
INSERT INTO observations
|
||||
(sdk_session_id, project, type, title, subtitle, facts, narrative, concepts,
|
||||
files_read, files_modified, prompt_number, 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,a.toISOString(),c)}storeSummary(e,s,t,r){let a=new Date,c=a.getTime();this.db.prepare(`
|
||||
`).run(e,t,s.type,s.title,s.subtitle,JSON.stringify(s.facts),s.narrative,JSON.stringify(s.concepts),JSON.stringify(s.files_read),JSON.stringify(s.files_modified),r||null,a.toISOString(),c)}storeSummary(e,t,s,r){let a=new Date,c=a.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,a.toISOString(),c),console.error(`[SessionStore] Auto-created session record for session_id: ${e}`)),this.db.prepare(`
|
||||
`).run(e,e,t,a.toISOString(),c),console.error(`[SessionStore] Auto-created session record for session_id: ${e}`)),this.db.prepare(`
|
||||
INSERT INTO session_summaries
|
||||
(sdk_session_id, project, request, investigated, learned, completed,
|
||||
next_steps, notes, prompt_number, created_at, created_at_epoch)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(e,s,t.request,t.investigated,t.learned,t.completed,t.next_steps,t.notes,r||null,a.toISOString(),c)}markSessionCompleted(e){let s=new Date,t=s.getTime();this.db.prepare(`
|
||||
`).run(e,t,s.request,s.investigated,s.learned,s.completed,s.next_steps,s.notes,r||null,a.toISOString(),c)}markSessionCompleted(e){let t=new Date,s=t.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(t.toISOString(),s,e)}markSessionFailed(e){let t=new Date,s=t.getTime();this.db.prepare(`
|
||||
UPDATE sdk_sessions
|
||||
SET status = 'failed', completed_at = ?, completed_at_epoch = ?
|
||||
WHERE id = ?
|
||||
`).run(s.toISOString(),t,e)}cleanupOrphanedSessions(){let e=new Date,s=e.getTime();return this.db.prepare(`
|
||||
`).run(t.toISOString(),s,e)}cleanupOrphanedSessions(){let e=new Date,t=e.getTime();return this.db.prepare(`
|
||||
UPDATE sdk_sessions
|
||||
SET status = 'failed', completed_at = ?, completed_at_epoch = ?
|
||||
WHERE status = 'active'
|
||||
`).run(e.toISOString(),s).changes}close(){this.db.close()}};import X from"path";import{existsSync as F}from"fs";import{spawn as ce}from"child_process";var pe=parseInt(process.env.CLAUDE_MEM_WORKER_PORT||"37777",10),ue=`http://127.0.0.1:${pe}/health`;async function Q(){try{return(await fetch(ue,{signal:AbortSignal.timeout(500)})).ok}catch{return!1}}async function z(){try{if(await Q())return!0;console.error("[claude-mem] Worker not responding, starting...");let o=q(),e=X.join(o,"plugin","scripts","worker-service.cjs");if(!F(e))return console.error(`[claude-mem] Worker service not found at ${e}`),!1;let s=X.join(o,"ecosystem.config.cjs"),t=X.join(o,"node_modules",".bin","pm2");if(!F(t))throw new Error(`PM2 binary not found at ${t}. This is a bundled dependency - try running: npm install`);if(!F(s))throw new Error(`PM2 ecosystem config not found at ${s}. Plugin installation may be corrupted.`);let r=ce(t,["start",s],{detached:!0,stdio:"ignore",cwd:o});r.on("error",a=>{throw new Error(`Failed to spawn PM2: ${a.message}`)}),r.unref(),console.error("[claude-mem] Worker started with PM2");for(let a=0;a<3;a++)if(await new Promise(c=>setTimeout(c,500)),await Q())return console.error("[claude-mem] Worker is healthy"),!0;return console.error("[claude-mem] Worker failed to become healthy after startup"),!1}catch(o){return console.error(`[claude-mem] Failed to start worker: ${o.message}`),!1}}var i={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 P(o){if(!o)return[];try{let e=JSON.parse(o);return Array.isArray(e)?e:[]}catch{return[]}}function le(o){return new Date(o).toLocaleString("en-US",{month:"short",day:"numeric",hour:"numeric",minute:"2-digit",hour12:!0})}function me(o){return new Date(o).toLocaleString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0})}function _e(o){return new Date(o).toLocaleString("en-US",{month:"short",day:"numeric",year:"numeric"})}function Ee(o){return o?Math.ceil(o.length/4):0}function Te(o,e){try{return G.isAbsolute(o)?G.relative(e,o):o}catch{return o}}function he(o,e){if(e.length===0)return[];let s=e.map(()=>"?").join(",");return o.db.prepare(`
|
||||
`).run(e.toISOString(),t).changes}close(){this.db.close()}};import X from"path";import{existsSync as F}from"fs";import{spawn as ce}from"child_process";var pe=parseInt(process.env.CLAUDE_MEM_WORKER_PORT||"37777",10),ue=`http://127.0.0.1:${pe}/health`;async function Q(){try{return(await fetch(ue,{signal:AbortSignal.timeout(500)})).ok}catch{return!1}}async function z(){try{if(await Q())return!0;console.error("[claude-mem] Worker not responding, starting...");let o=q(),e=X.join(o,"plugin","scripts","worker-service.cjs");if(!F(e))return console.error(`[claude-mem] Worker service not found at ${e}`),!1;let t=X.join(o,"ecosystem.config.cjs"),s=X.join(o,"node_modules",".bin","pm2");if(!F(s))throw new Error(`PM2 binary not found at ${s}. This is a bundled dependency - try running: npm install`);if(!F(t))throw new Error(`PM2 ecosystem config not found at ${t}. Plugin installation may be corrupted.`);let r=ce(s,["start",t],{detached:!0,stdio:"ignore",cwd:o});r.on("error",a=>{throw new Error(`Failed to spawn PM2: ${a.message}`)}),r.unref(),console.error("[claude-mem] Worker started with PM2");for(let a=0;a<3;a++)if(await new Promise(c=>setTimeout(c,500)),await Q())return console.error("[claude-mem] Worker is healthy"),!0;return console.error("[claude-mem] Worker failed to become healthy after startup"),!1}catch(o){return console.error(`[claude-mem] Failed to start worker: ${o.message}`),!1}}var i={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 P(o){if(!o)return[];try{let e=JSON.parse(o);return Array.isArray(e)?e:[]}catch{return[]}}function le(o){return new Date(o).toLocaleString("en-US",{month:"short",day:"numeric",hour:"numeric",minute:"2-digit",hour12:!0})}function me(o){return new Date(o).toLocaleString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0})}function _e(o){return new Date(o).toLocaleString("en-US",{month:"short",day:"numeric",year:"numeric"})}function Ee(o){return o?Math.ceil(o.length/4):0}function Te(o,e){try{return G.isAbsolute(o)?G.relative(e,o):o}catch{return o}}function he(o,e){if(e.length===0)return[];let t=e.map(()=>"?").join(",");return o.db.prepare(`
|
||||
SELECT
|
||||
id, sdk_session_id, type, title, subtitle, narrative,
|
||||
facts, concepts, files_read, files_modified,
|
||||
created_at, created_at_epoch
|
||||
FROM observations
|
||||
WHERE sdk_session_id IN (${s})
|
||||
WHERE sdk_session_id IN (${t})
|
||||
ORDER BY created_at_epoch DESC
|
||||
`).all(...e)}function W(o,e=!1,s=!1){z();let t=o?.cwd??process.cwd(),r=t?G.basename(t):"unknown-project",a=new k;try{let c=a.db.prepare(`
|
||||
`).all(...e)}function W(o,e=!1,t=!1){z();let s=o?.cwd??process.cwd(),r=s?G.basename(s):"unknown-project",a=new D;try{let c=a.db.prepare(`
|
||||
SELECT id, sdk_session_id, request, completed, next_steps, created_at, created_at_epoch
|
||||
FROM session_summaries
|
||||
WHERE project = ?
|
||||
@@ -327,5 +327,7 @@ ${i.gray}${"\u2500".repeat(60)}${i.reset}
|
||||
${i.dim}No previous sessions found for this project yet.${i.reset}
|
||||
`:`# [${r}] recent context
|
||||
|
||||
No previous sessions found for this project yet.`;let p=c.slice(0,3),u=[...new Set(p.map(R=>R.sdk_session_id))],S=he(a,u).filter(R=>{let h=P(R.concepts);return h.includes("what-changed")||h.includes("how-it-works")||h.includes("problem-solution")||h.includes("gotcha")||h.includes("discovery")||h.includes("why-it-exists")||h.includes("decision")||h.includes("trade-off")}),n=[];if(e?(n.push(""),n.push(`${i.bright}${i.cyan}\u{1F4DD} [${r}] recent context${i.reset}`),n.push(`${i.gray}${"\u2500".repeat(60)}${i.reset}`),n.push("")):(n.push(`# [${r}] recent context`),n.push("")),S.length>0){e?(n.push(`${i.dim}Legend: \u{1F3AF} session-request | \u{1F534} gotcha | \u{1F7E1} problem-solution | \u{1F535} how-it-works | \u{1F7E2} what-changed | \u{1F7E3} discovery | \u{1F7E0} why-it-exists | \u{1F7E4} decision | \u2696\uFE0F trade-off${i.reset}`),n.push("")):(n.push("**Legend:** \u{1F3AF} session-request | \u{1F534} gotcha | \u{1F7E1} problem-solution | \u{1F535} how-it-works | \u{1F7E2} what-changed | \u{1F7E3} discovery | \u{1F7E0} why-it-exists | \u{1F7E4} decision | \u2696\uFE0F trade-off"),n.push("")),e?(n.push(`${i.dim}\u{1F4A1} Progressive Disclosure: This index shows WHAT exists (titles) and retrieval COST (token counts).${i.reset}`),n.push(`${i.dim} \u2192 Use MCP search tools to fetch full observation details on-demand (Layer 2)${i.reset}`),n.push(`${i.dim} \u2192 Prefer searching observations over re-reading code for past decisions and learnings${i.reset}`),n.push(`${i.dim} \u2192 Critical types (\u{1F534} gotcha, \u{1F7E4} decision, \u2696\uFE0F trade-off) often worth fetching immediately${i.reset}`),n.push("")):(n.push("\u{1F4A1} **Progressive Disclosure:** This index shows WHAT exists (titles) and retrieval COST (token counts)."),n.push("- Use MCP search tools to fetch full observation details on-demand (Layer 2)"),n.push("- Prefer searching observations over re-reading code for past decisions and learnings"),n.push("- Critical types (\u{1F534} gotcha, \u{1F7E4} decision, \u2696\uFE0F trade-off) often worth fetching immediately"),n.push(""));let R=c[0]?.id,h=p.map((d,m)=>{let l=m===0?null:c[m+1];return{...d,displayEpoch:l?l.created_at_epoch:d.created_at_epoch,displayTime:l?l.created_at:d.created_at,isMostRecent:d.id===R}}),C=[...S.map(d=>({type:"observation",data:d})),...h.map(d=>({type:"summary",data:d}))];C.sort((d,m)=>{let l=d.type==="observation"?d.data.created_at_epoch:d.data.displayEpoch,N=m.type==="observation"?m.data.created_at_epoch:m.data.displayEpoch;return l-N});let L=new Map;for(let d of C){let m=d.type==="observation"?d.data.created_at:d.data.displayTime,l=_e(m);L.has(l)||L.set(l,[]),L.get(l).push(d)}let D=Array.from(L.entries()).sort((d,m)=>{let l=new Date(d[0]).getTime(),N=new Date(m[0]).getTime();return l-N});for(let[d,m]of D){e?(n.push(`${i.bright}${i.cyan}${d}${i.reset}`),n.push("")):(n.push(`### ${d}`),n.push(""));let l=null,N="",v=!1;for(let x of m)if(x.type==="summary"){v&&(n.push(""),v=!1,l=null,N="");let _=x.data,A=`${_.request||"Session started"} (${le(_.displayTime)})`,b=_.isMostRecent?"":`claude-mem://session-summary/${_.id}`;if(e){let E=b?`${i.dim}[${b}]${i.reset}`:"";n.push(`\u{1F3AF} ${i.yellow}#S${_.id}${i.reset} ${A} ${E}`)}else{let E=b?` [\u2192](${b})`:"";n.push(`**\u{1F3AF} #S${_.id}** ${A}${E}`)}n.push("")}else{let _=x.data,A=P(_.files_modified),b=A.length>0?Te(A[0],t):"General";b!==l&&(v&&n.push(""),e?n.push(`${i.dim}${b}${i.reset}`):n.push(`**${b}**`),e||(n.push("| ID | Time | T | Title | Tokens |"),n.push("|----|------|---|-------|--------|")),l=b,v=!0,N="");let E=P(_.concepts),f="\u2022";E.includes("gotcha")?f="\u{1F534}":E.includes("decision")?f="\u{1F7E4}":E.includes("trade-off")?f="\u2696\uFE0F":E.includes("problem-solution")?f="\u{1F7E1}":E.includes("discovery")?f="\u{1F7E3}":E.includes("why-it-exists")?f="\u{1F7E0}":E.includes("how-it-works")?f="\u{1F535}":E.includes("what-changed")&&(f="\u{1F7E2}");let y=me(_.created_at),B=_.title||"Untitled",w=Ee(_.narrative),j=y!==N,ee=j?y:"";if(N=y,e){let se=j?`${i.dim}${y}${i.reset}`:" ".repeat(y.length),te=w>0?`${i.dim}(~${w}t)${i.reset}`:"";n.push(` ${i.dim}#${_.id}${i.reset} ${se} ${f} ${B} ${te}`)}else n.push(`| #${_.id} | ${ee||"\u2033"} | ${f} | ${B} | ~${w} |`)}v&&n.push("")}let g=c[0];g&&(g.completed||g.next_steps)&&(g.completed&&(e?n.push(`${i.green}Completed:${i.reset} ${g.completed}`):n.push(`**Completed**: ${g.completed}`),n.push("")),g.next_steps&&(e?n.push(`${i.magenta}Next Steps:${i.reset} ${g.next_steps}`):n.push(`**Next Steps**: ${g.next_steps}`),n.push(""))),e?n.push(`${i.dim}Use claude-mem MCP search to access records with the given ID${i.reset}`):n.push("*Use claude-mem MCP search to access records with the given ID*"),n.push("")}return e&&(n.push(`${i.gray}${"\u2500".repeat(60)}${i.reset}`),n.push("")),n.join(`
|
||||
`)}finally{a.close()}}import{stdin as H}from"process";var Z=process.argv.includes("--index");if(H.isTTY)try{let o=W(void 0,!0,Z);console.log(o),process.exit(0)}catch(o){console.error(`[claude-mem context-hook error: ${o.message}]`),console.error(o.stack),process.exit(1)}else{let o="";H.on("data",e=>o+=e),H.on("end",()=>{try{let e=o.trim()?JSON.parse(o):void 0,s=W(e,!1,Z);console.log(s),process.exit(0)}catch(e){console.log(`[claude-mem context-hook ERROR: ${e.message}]`),console.log(`Input received: ${o.substring(0,200)}...`),console.log(e.stack),process.exit(1)}})}
|
||||
No previous sessions found for this project yet.`;let p=c.slice(0,3),u=[...new Set(p.map(N=>N.sdk_session_id))],b=he(a,u).filter(N=>{let h=P(N.concepts);return h.includes("what-changed")||h.includes("how-it-works")||h.includes("problem-solution")||h.includes("gotcha")||h.includes("discovery")||h.includes("why-it-exists")||h.includes("decision")||h.includes("trade-off")}),n=[];if(e?(n.push(""),n.push(`${i.bright}${i.cyan}\u{1F4DD} [${r}] recent context${i.reset}`),n.push(`${i.gray}${"\u2500".repeat(60)}${i.reset}`),n.push("")):(n.push(`# [${r}] recent context`),n.push("")),b.length>0){e?(n.push(`${i.dim}Legend: \u{1F3AF} session-request | \u{1F534} gotcha | \u{1F7E1} problem-solution | \u{1F535} how-it-works | \u{1F7E2} what-changed | \u{1F7E3} discovery | \u{1F7E0} why-it-exists | \u{1F7E4} decision | \u2696\uFE0F trade-off${i.reset}`),n.push("")):(n.push("**Legend:** \u{1F3AF} session-request | \u{1F534} gotcha | \u{1F7E1} problem-solution | \u{1F535} how-it-works | \u{1F7E2} what-changed | \u{1F7E3} discovery | \u{1F7E0} why-it-exists | \u{1F7E4} decision | \u2696\uFE0F trade-off"),n.push("")),e?(n.push(`${i.dim}\u{1F4A1} Progressive Disclosure: This index shows WHAT exists (titles) and retrieval COST (token counts).${i.reset}`),n.push(`${i.dim} \u2192 Use MCP search tools to fetch full observation details on-demand (Layer 2)${i.reset}`),n.push(`${i.dim} \u2192 Prefer searching observations over re-reading code for past decisions and learnings${i.reset}`),n.push(`${i.dim} \u2192 Critical types (\u{1F534} gotcha, \u{1F7E4} decision, \u2696\uFE0F trade-off) often worth fetching immediately${i.reset}`),n.push("")):(n.push("\u{1F4A1} **Progressive Disclosure:** This index shows WHAT exists (titles) and retrieval COST (token counts)."),n.push("- Use MCP search tools to fetch full observation details on-demand (Layer 2)"),n.push("- Prefer searching observations over re-reading code for past decisions and learnings"),n.push("- Critical types (\u{1F534} gotcha, \u{1F7E4} decision, \u2696\uFE0F trade-off) often worth fetching immediately"),n.push(""));let N=c[0]?.id,h=p.map((d,m)=>{let l=m===0?null:c[m+1];return{...d,displayEpoch:l?l.created_at_epoch:d.created_at_epoch,displayTime:l?l.created_at:d.created_at,isMostRecent:d.id===N}}),C=[...b.map(d=>({type:"observation",data:d})),...h.map(d=>({type:"summary",data:d}))];C.sort((d,m)=>{let l=d.type==="observation"?d.data.created_at_epoch:d.data.displayEpoch,R=m.type==="observation"?m.data.created_at_epoch:m.data.displayEpoch;return l-R});let L=new Map;for(let d of C){let m=d.type==="observation"?d.data.created_at:d.data.displayTime,l=_e(m);L.has(l)||L.set(l,[]),L.get(l).push(d)}let k=Array.from(L.entries()).sort((d,m)=>{let l=new Date(d[0]).getTime(),R=new Date(m[0]).getTime();return l-R});for(let[d,m]of k){e?(n.push(`${i.bright}${i.cyan}${d}${i.reset}`),n.push("")):(n.push(`### ${d}`),n.push(""));let l=null,R="",v=!1;for(let x of m)if(x.type==="summary"){v&&(n.push(""),v=!1,l=null,R="");let _=x.data,A=`${_.request||"Session started"} (${le(_.displayTime)})`,S=_.isMostRecent?"":`claude-mem://session-summary/${_.id}`;if(e){let E=S?`${i.dim}[${S}]${i.reset}`:"";n.push(`\u{1F3AF} ${i.yellow}#S${_.id}${i.reset} ${A} ${E}`)}else{let E=S?` [\u2192](${S})`:"";n.push(`**\u{1F3AF} #S${_.id}** ${A}${E}`)}n.push("")}else{let _=x.data,A=P(_.files_modified),S=A.length>0?Te(A[0],s):"General";S!==l&&(v&&n.push(""),e?n.push(`${i.dim}${S}${i.reset}`):n.push(`**${S}**`),e||(n.push("| ID | Time | T | Title | Tokens |"),n.push("|----|------|---|-------|--------|")),l=S,v=!0,R="");let E=P(_.concepts),f="\u2022";E.includes("gotcha")?f="\u{1F534}":E.includes("decision")?f="\u{1F7E4}":E.includes("trade-off")?f="\u2696\uFE0F":E.includes("problem-solution")?f="\u{1F7E1}":E.includes("discovery")?f="\u{1F7E3}":E.includes("why-it-exists")?f="\u{1F7E0}":E.includes("how-it-works")?f="\u{1F535}":E.includes("what-changed")&&(f="\u{1F7E2}");let y=me(_.created_at),B=_.title||"Untitled",w=Ee(_.narrative),j=y!==R,ee=j?y:"";if(R=y,e){let se=j?`${i.dim}${y}${i.reset}`:" ".repeat(y.length),te=w>0?`${i.dim}(~${w}t)${i.reset}`:"";n.push(` ${i.dim}#${_.id}${i.reset} ${se} ${f} ${B} ${te}`)}else n.push(`| #${_.id} | ${ee||"\u2033"} | ${f} | ${B} | ~${w} |`)}v&&n.push("")}let g=c[0];g&&(g.completed||g.next_steps)&&(g.completed&&(e?n.push(`${i.green}Completed:${i.reset} ${g.completed}`):n.push(`**Completed**: ${g.completed}`),n.push("")),g.next_steps&&(e?n.push(`${i.magenta}Next Steps:${i.reset} ${g.next_steps}`):n.push(`**Next Steps**: ${g.next_steps}`),n.push(""))),e?n.push(`${i.dim}Use claude-mem MCP search to access records with the given ID${i.reset}`):n.push("*Use claude-mem MCP search to access records with the given ID*"),n.push("")}return e&&(n.push(`${i.gray}${"\u2500".repeat(60)}${i.reset}`),n.push("")),n.join(`
|
||||
`)}finally{a.close()}}import{stdin as H}from"process";var Z=process.argv.includes("--index");if(H.isTTY)try{let o=W(void 0,!0,Z);console.log(o),process.exit(0)}catch(o){console.error(`[claude-mem context-hook error: ${o.message}]`),console.error(o.stack),process.exit(1)}else{let o="";H.on("data",e=>o+=e),H.on("end",()=>{try{let e=o.trim()?JSON.parse(o):void 0,s={hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:W(e,!1,Z)}};console.log(JSON.stringify(s)),process.exit(0)}catch(e){let t={hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:`[claude-mem ERROR: ${e.message}]
|
||||
Input: ${o.substring(0,200)}...
|
||||
${e.stack}`}};console.log(JSON.stringify(t)),process.exit(1)}})}
|
||||
|
||||
Reference in New Issue
Block a user