Refactor hooks to standardize response format and improve error handling
- Introduced a new `hook-response.ts` module to create standardized hook responses. - Updated `context-hook.ts`, `new.ts`, `save.ts`, and `summary.ts` to utilize the new response format. - Enhanced error handling in `context-hook.ts` to check for input from stdin. - Refactored database interaction in hooks to ensure consistent session management. - Improved readability and maintainability of hook implementations by restructuring code. - Updated database queries to use consistent variable naming and formatting. - Modified the handling of socket connections in `save.ts` and `summary.ts` to ensure proper response on close and error events.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bun
|
||||
// @bun
|
||||
import U from"net";import{Database as O}from"bun:sqlite";import{join as Z,dirname as b,basename as E}from"path";import{homedir as M}from"os";import{existsSync as C,mkdirSync as N}from"fs";var K=process.env.CLAUDE_MEM_DATA_DIR||Z(M(),".claude-mem"),v=process.env.CLAUDE_CONFIG_DIR||Z(M(),".claude"),P=Z(K,"archives"),l=Z(K,"logs"),S=Z(K,"trash"),k=Z(K,"backups"),y=Z(K,"chroma"),h=Z(K,"settings.json"),q=Z(K,"claude-mem.db"),R=Z(v,"settings.json"),j=Z(v,"commands"),A=Z(v,"CLAUDE.md");function F(z){return Z(K,`worker-${z}.sock`)}function G(z){N(z,{recursive:!0})}class J{db;constructor(){G(K),this.db=new O(q,{create:!0,readwrite:!0}),this.db.run("PRAGMA journal_mode = WAL"),this.db.run("PRAGMA synchronous = NORMAL"),this.db.run("PRAGMA foreign_keys = ON")}getRecentSummaries(z,Q=10){return this.db.query(`
|
||||
import E from"net";import{Database as f}from"bun:sqlite";import{join as $,dirname as w,basename as P}from"path";import{homedir as F}from"os";import{existsSync as T,mkdirSync as U}from"fs";var z=process.env.CLAUDE_MEM_DATA_DIR||$(F(),".claude-mem"),v=process.env.CLAUDE_CONFIG_DIR||$(F(),".claude"),A=$(z,"archives"),j=$(z,"logs"),R=$(z,"trash"),_=$(z,"backups"),y=$(z,"chroma"),I=$(z,"settings.json"),G=$(z,"claude-mem.db"),k=$(v,"settings.json"),h=$(v,"commands"),D=$(v,"CLAUDE.md");function x(Q){return $(z,`worker-${Q}.sock`)}function L(Q){U(Q,{recursive:!0})}class J{db;constructor(){L(z),this.db=new f(G,{create:!0,readwrite:!0}),this.db.run("PRAGMA journal_mode = WAL"),this.db.run("PRAGMA synchronous = NORMAL"),this.db.run("PRAGMA foreign_keys = ON")}getRecentSummaries(Q,Y=10){return this.db.query(`
|
||||
SELECT
|
||||
request, investigated, learned, completed, next_steps,
|
||||
files_read, files_edited, notes, created_at
|
||||
@@ -8,36 +8,35 @@ import U from"net";import{Database as O}from"bun:sqlite";import{join as Z,dirnam
|
||||
WHERE project = ?
|
||||
ORDER BY created_at_epoch DESC
|
||||
LIMIT ?
|
||||
`).all(z,Q)}findActiveSDKSession(z){return this.db.query(`
|
||||
`).all(Q,Y)}findActiveSDKSession(Q){return this.db.query(`
|
||||
SELECT id, sdk_session_id, project
|
||||
FROM sdk_sessions
|
||||
WHERE claude_session_id = ? AND status = 'active'
|
||||
LIMIT 1
|
||||
`).get(z)||null}createSDKSession(z,Q,X){let Y=new Date,$=Y.getTime();return this.db.query(`
|
||||
`).get(Q)||null}createSDKSession(Q,Y,X){let Z=new Date,K=Z.getTime();return this.db.query(`
|
||||
INSERT INTO sdk_sessions
|
||||
(claude_session_id, project, user_prompt, started_at, started_at_epoch, status)
|
||||
VALUES (?, ?, ?, ?, ?, 'active')
|
||||
`).run(z,Q,X,Y.toISOString(),$),this.db.query("SELECT last_insert_rowid() as id").get().id}updateSDKSessionId(z,Q){this.db.query(`
|
||||
`).run(Q,Y,X,Z.toISOString(),K),this.db.query("SELECT last_insert_rowid() as id").get().id}updateSDKSessionId(Q,Y){this.db.query(`
|
||||
UPDATE sdk_sessions
|
||||
SET sdk_session_id = ?
|
||||
WHERE id = ?
|
||||
`).run(Q,z)}storeObservation(z,Q,X,Y){let $=new Date,W=$.getTime();this.db.query(`
|
||||
`).run(Y,Q)}storeObservation(Q,Y,X,Z){let K=new Date,B=K.getTime();this.db.query(`
|
||||
INSERT INTO observations
|
||||
(sdk_session_id, project, text, type, created_at, created_at_epoch)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`).run(z,Q,Y,X,$.toISOString(),W)}storeSummary(z,Q,X){let Y=new Date,$=Y.getTime();this.db.query(`
|
||||
`).run(Q,Y,Z,X,K.toISOString(),B)}storeSummary(Q,Y,X){let Z=new Date,K=Z.getTime();this.db.query(`
|
||||
INSERT INTO session_summaries
|
||||
(sdk_session_id, project, request, investigated, learned, completed,
|
||||
next_steps, files_read, files_edited, notes, created_at, created_at_epoch)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(z,Q,X.request||null,X.investigated||null,X.learned||null,X.completed||null,X.next_steps||null,X.files_read||null,X.files_edited||null,X.notes||null,Y.toISOString(),$)}markSessionCompleted(z){let Q=new Date,X=Q.getTime();this.db.query(`
|
||||
`).run(Q,Y,X.request||null,X.investigated||null,X.learned||null,X.completed||null,X.next_steps||null,X.files_read||null,X.files_edited||null,X.notes||null,Z.toISOString(),K)}markSessionCompleted(Q){let Y=new Date,X=Y.getTime();this.db.query(`
|
||||
UPDATE sdk_sessions
|
||||
SET status = 'completed', completed_at = ?, completed_at_epoch = ?
|
||||
WHERE id = ?
|
||||
`).run(Q.toISOString(),X,z)}markSessionFailed(z){let Q=new Date,X=Q.getTime();this.db.query(`
|
||||
`).run(Y.toISOString(),X,Q)}markSessionFailed(Q){let Y=new Date,X=Y.getTime();this.db.query(`
|
||||
UPDATE sdk_sessions
|
||||
SET status = 'failed', completed_at = ?, completed_at_epoch = ?
|
||||
WHERE id = ?
|
||||
`).run(Q.toISOString(),X,z)}close(){this.db.close()}}function x(z){try{if(console.error("[claude-mem summary] Hook fired",{input:z?{session_id:z.session_id,cwd:z.cwd}:null}),!z)console.log("No input provided - this script is designed to run as a Claude Code Stop hook"),console.log(`
|
||||
Expected input format:`),console.log(JSON.stringify({session_id:"string",cwd:"string"},null,2)),process.exit(0);let{session_id:Q}=z;console.error("[claude-mem summary] Searching for active SDK session",{session_id:Q});let X=new J,Y=X.findActiveSDKSession(Q);if(X.close(),!Y)console.error("[claude-mem summary] No active SDK session found",{session_id:Q}),console.log('{"continue": true, "suppressOutput": true}'),process.exit(0);console.error("[claude-mem summary] Active SDK session found",{session_id:Y.id,collection_name:Y.collection_name,worker_pid:Y.worker_pid});let $=F(Y.id),W={type:"finalize"};console.error("[claude-mem summary] Attempting to send FINALIZE message to worker socket",{socketPath:$,message:W});let B=U.connect($,()=>{console.error("[claude-mem summary] Socket connection established, sending message"),B.write(JSON.stringify(W)+`
|
||||
`),B.end()});B.on("error",(V)=>{console.error("[claude-mem summary] Socket error occurred",{error:V.message,code:V.code,socketPath:$})}),B.on("close",()=>{console.error("[claude-mem summary] Socket connection closed successfully"),console.log('{"continue": true, "suppressOutput": true}'),process.exit(0)})}catch(Q){console.error("[claude-mem summary] Unexpected error in hook",{error:Q.message,stack:Q.stack,name:Q.name}),console.log('{"continue": true, "suppressOutput": true}'),process.exit(0)}}var L=await Bun.stdin.text();try{let z=L.trim()?JSON.parse(L):void 0;x(z)}catch(z){console.error(`[claude-mem summary-hook error: ${z.message}]`),console.log('{"continue": true, "suppressOutput": true}'),process.exit(0)}
|
||||
`).run(Y.toISOString(),X,Q)}close(){this.db.close()}}function b(Q,Y,X){if(Q==="PreCompact"){if(Y)return{continue:!0,suppressOutput:!0};return{continue:!1,stopReason:X.reason||"Pre-compact operation failed",suppressOutput:!0}}if(Q==="SessionStart"){if(Y&&X.context)return{continue:!0,suppressOutput:!0,hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:X.context}};return{continue:!0,suppressOutput:!0}}if(Q==="UserPromptSubmit"||Q==="PostToolUse")return{continue:!0,suppressOutput:!0};if(Q==="Stop")return{continue:!0,suppressOutput:!0};return{continue:Y,suppressOutput:!0,...X.reason&&!Y?{stopReason:X.reason}:{}}}function M(Q,Y,X={}){let Z=b(Q,Y,X);return JSON.stringify(Z)}function N(Q){if(!Q)throw new Error("summaryHook requires input");let{session_id:Y}=Q,X=new J,Z=X.findActiveSDKSession(Y);if(X.close(),!Z){console.log(M("Stop",!0));return}let K=x(Z.id),B={type:"finalize"},W=E.connect(K,()=>{W.write(JSON.stringify(B)+`
|
||||
`),W.end()}),V=!1,q=()=>{if(V)return;V=!0,console.log(M("Stop",!0))};W.on("close",q),W.on("error",q)}var O=await Bun.stdin.text();try{let Q=O.trim()?JSON.parse(O):void 0;N(Q)}catch(Q){console.error(`[claude-mem summary-hook error: ${Q.message}]`),console.log('{"continue": true, "suppressOutput": true}'),process.exit(0)}
|
||||
|
||||
Reference in New Issue
Block a user