feat: Implement Phase 1 of SDK agent architecture with hook integration

- Added CLI commands for context, new session, save observation, and summary.
- Created HooksDatabase for managing SDK sessions and observations.
- Implemented migration 004 to add new tables: sdk_sessions, observation_queue, observations, and session_summaries.
- Developed hook functions for context display, session initialization, observation queuing, and session finalization.
- Added comprehensive tests for database schema and hook functionality.
- Documented Phase 1 implementation in PHASE1-COMPLETE.md.
This commit is contained in:
Alex Newman
2025-10-15 19:06:51 -04:00
parent 917ab9740c
commit e81ea69143
12 changed files with 1294 additions and 129 deletions
+92
View File
@@ -0,0 +1,92 @@
import { HooksDatabase } from '../services/sqlite/HooksDatabase.js';
import { PathDiscovery } from '../services/path-discovery.js';
import path from 'path';
export interface SessionStartInput {
session_id: string;
cwd: string;
source?: string;
[key: string]: any;
}
/**
* Context Hook - SessionStart
* Shows user what happened in recent sessions
*/
export function contextHook(input: SessionStartInput): void {
try {
// Only run on startup (not on resume)
if (input.source && input.source !== 'startup') {
console.log(''); // Output nothing, just exit
process.exit(0);
}
// Extract project from cwd
const project = path.basename(input.cwd);
// Get recent summaries
const db = new HooksDatabase();
const summaries = db.getRecentSummaries(project, 5);
db.close();
// If no summaries, exit silently
if (summaries.length === 0) {
console.log(''); // Output nothing
process.exit(0);
}
// Format output for Claude
const output: string[] = [];
output.push('# Recent Session Context');
output.push('');
output.push(`Here's what happened in recent ${project} sessions:`);
output.push('');
for (const summary of summaries) {
output.push('---');
output.push('');
if (summary.request) {
output.push(`**Request:** ${summary.request}`);
}
if (summary.completed) {
output.push(`**Completed:** ${summary.completed}`);
}
if (summary.learned) {
output.push(`**Learned:** ${summary.learned}`);
}
if (summary.next_steps) {
output.push(`**Next Steps:** ${summary.next_steps}`);
}
if (summary.files_edited) {
try {
const files = JSON.parse(summary.files_edited);
if (Array.isArray(files) && files.length > 0) {
output.push(`**Files Edited:** ${files.join(', ')}`);
}
} catch {
// If not valid JSON, show as text
if (summary.files_edited.trim()) {
output.push(`**Files Edited:** ${summary.files_edited}`);
}
}
}
output.push(`**Date:** ${summary.created_at.split('T')[0]}`);
output.push('');
}
// Output to stdout for Claude Code to inject
console.log(output.join('\n'));
process.exit(0);
} catch (error: any) {
// On error, exit silently - don't block Claude Code
console.error(`[claude-mem context error: ${error.message}]`);
process.exit(0);
}
}