Add comprehensive documentation for claude-mem codebase and create a test worker script
- Introduced CODEMAP.md detailing project overview, architecture, directory structure, core components, commands, hooks system, SDK, services, shared components, utilities, and key workflows. - Added a test-worker.sh script to automate testing of the SDK worker, including session creation, worker initiation, socket communication, and cleanup after finalization.
This commit is contained in:
+2
-20
@@ -1,7 +1,6 @@
|
||||
import { HooksDatabase } from '../services/sqlite/HooksDatabase.js';
|
||||
import path from 'path';
|
||||
import { spawn } from 'child_process';
|
||||
import fs from 'fs';
|
||||
|
||||
export interface UserPromptSubmitInput {
|
||||
session_id: string;
|
||||
@@ -37,25 +36,8 @@ export function newHook(input: UserPromptSubmitInput): void {
|
||||
db.close();
|
||||
|
||||
// Start SDK worker in background as detached process
|
||||
// Try source first (development), then fall back to dist (production)
|
||||
const srcWorkerPath = path.join(__dirname, '..', 'sdk', 'worker.ts');
|
||||
const distWorkerPath = path.join(__dirname, '..', 'sdk', 'worker.js');
|
||||
|
||||
let workerPath: string;
|
||||
if (fs.existsSync(srcWorkerPath)) {
|
||||
workerPath = srcWorkerPath;
|
||||
} else if (fs.existsSync(distWorkerPath)) {
|
||||
workerPath = distWorkerPath;
|
||||
} else {
|
||||
// Fallback: assume we're in the bundled CLI
|
||||
// In this case, we can't spawn the worker since it's bundled
|
||||
// This is a limitation we'll need to address
|
||||
console.error('[claude-mem] Worker not found, skipping background processing');
|
||||
console.log('{"continue": true, "suppressOutput": true}');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const child = spawn('bun', [workerPath, sessionId.toString()], {
|
||||
// Use 'claude-mem worker' CLI command which is always available
|
||||
const child = spawn('claude-mem', ['worker', sessionId.toString()], {
|
||||
detached: true,
|
||||
stdio: 'ignore'
|
||||
});
|
||||
|
||||
+29
-20
@@ -1,5 +1,7 @@
|
||||
import net from 'net';
|
||||
import { join } from 'path';
|
||||
import { HooksDatabase } from '../services/sqlite/HooksDatabase.js';
|
||||
import path from 'path';
|
||||
import { PathDiscovery } from '../services/path-discovery.js';
|
||||
|
||||
export interface PostToolUseInput {
|
||||
session_id: string;
|
||||
@@ -18,11 +20,11 @@ const SKIP_TOOLS = new Set([
|
||||
|
||||
/**
|
||||
* Save Hook - PostToolUse
|
||||
* Queues tool observations for SDK processing
|
||||
* Sends tool observations to worker via Unix socket
|
||||
*/
|
||||
export function saveHook(input: PostToolUseInput): void {
|
||||
try {
|
||||
const { session_id, cwd, tool_name, tool_input, tool_output } = input;
|
||||
const { session_id, tool_name, tool_input, tool_output } = input;
|
||||
|
||||
// Skip certain tools
|
||||
if (SKIP_TOOLS.has(tool_name)) {
|
||||
@@ -30,38 +32,45 @@ export function saveHook(input: PostToolUseInput): void {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Extract project from cwd
|
||||
const project = path.basename(cwd);
|
||||
|
||||
// Find active SDK session
|
||||
const db = new HooksDatabase();
|
||||
const session = db.findActiveSDKSession(session_id);
|
||||
db.close();
|
||||
|
||||
if (!session) {
|
||||
// No active session yet - this can happen if UserPromptSubmit hasn't run
|
||||
// Just exit silently
|
||||
db.close();
|
||||
console.log('{"continue": true, "suppressOutput": true}');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Queue the observation
|
||||
// SDK session ID might be null if init message hasn't arrived yet
|
||||
// Use the internal ID as a fallback
|
||||
const sdkSessionId = session.sdk_session_id || `pending-${session.id}`;
|
||||
// Get socket path
|
||||
const dataDir = PathDiscovery.getInstance().getDataDirectory();
|
||||
const socketPath = join(dataDir, `worker-${session.id}.sock`);
|
||||
|
||||
db.queueObservation(
|
||||
sdkSessionId,
|
||||
// Send observation via Unix socket
|
||||
const message = {
|
||||
type: 'observation',
|
||||
tool_name,
|
||||
JSON.stringify(tool_input),
|
||||
JSON.stringify(tool_output)
|
||||
);
|
||||
tool_input: JSON.stringify(tool_input),
|
||||
tool_output: JSON.stringify(tool_output)
|
||||
};
|
||||
|
||||
db.close();
|
||||
const client = net.connect(socketPath, () => {
|
||||
client.write(JSON.stringify(message) + '\n');
|
||||
client.end();
|
||||
});
|
||||
|
||||
// Output hook response
|
||||
console.log('{"continue": true, "suppressOutput": true}');
|
||||
process.exit(0);
|
||||
client.on('error', (err) => {
|
||||
// Socket not available - worker may have crashed or not started
|
||||
console.error(`[claude-mem save] Socket error: ${err.message}`);
|
||||
// Continue anyway, don't block Claude
|
||||
});
|
||||
|
||||
client.on('close', () => {
|
||||
console.log('{"continue": true, "suppressOutput": true}');
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
} catch (error: any) {
|
||||
// On error, don't block Claude Code
|
||||
|
||||
+26
-14
@@ -1,4 +1,7 @@
|
||||
import net from 'net';
|
||||
import { join } from 'path';
|
||||
import { HooksDatabase } from '../services/sqlite/HooksDatabase.js';
|
||||
import { PathDiscovery } from '../services/path-discovery.js';
|
||||
|
||||
export interface StopInput {
|
||||
session_id: string;
|
||||
@@ -8,7 +11,7 @@ export interface StopInput {
|
||||
|
||||
/**
|
||||
* Summary Hook - Stop
|
||||
* Signals SDK to finalize and generate summary
|
||||
* Sends FINALIZE message to worker via Unix socket
|
||||
*/
|
||||
export function summaryHook(input: StopInput): void {
|
||||
try {
|
||||
@@ -17,29 +20,38 @@ export function summaryHook(input: StopInput): void {
|
||||
// Find active SDK session
|
||||
const db = new HooksDatabase();
|
||||
const session = db.findActiveSDKSession(session_id);
|
||||
db.close();
|
||||
|
||||
if (!session) {
|
||||
// No active session - nothing to finalize
|
||||
db.close();
|
||||
console.log('{"continue": true, "suppressOutput": true}');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Insert special FINALIZE message into observation queue
|
||||
const sdkSessionId = session.sdk_session_id || `pending-${session.id}`;
|
||||
// Get socket path
|
||||
const dataDir = PathDiscovery.getInstance().getDataDirectory();
|
||||
const socketPath = join(dataDir, `worker-${session.id}.sock`);
|
||||
|
||||
db.queueObservation(
|
||||
sdkSessionId,
|
||||
'FINALIZE',
|
||||
'{}',
|
||||
'{}'
|
||||
);
|
||||
// Send FINALIZE message via Unix socket
|
||||
const message = {
|
||||
type: 'finalize'
|
||||
};
|
||||
|
||||
db.close();
|
||||
const client = net.connect(socketPath, () => {
|
||||
client.write(JSON.stringify(message) + '\n');
|
||||
client.end();
|
||||
});
|
||||
|
||||
// Output hook response
|
||||
console.log('{"continue": true, "suppressOutput": true}');
|
||||
process.exit(0);
|
||||
client.on('error', (err) => {
|
||||
// Socket not available - worker may have already finished or crashed
|
||||
console.error(`[claude-mem summary] Socket error: ${err.message}`);
|
||||
// Continue anyway, don't block Claude
|
||||
});
|
||||
|
||||
client.on('close', () => {
|
||||
console.log('{"continue": true, "suppressOutput": true}');
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
} catch (error: any) {
|
||||
// On error, don't block Claude Code
|
||||
|
||||
Reference in New Issue
Block a user