Refactor summary and context handling in hooks
- Updated `summary-hook.js` to improve logging and session handling. - Modified `context.ts` to fetch recent sessions with status and summary info, enhancing output formatting. - Added new methods in `HooksDatabase.ts` for retrieving recent sessions and their summaries. - Improved observation retrieval logic in `context.ts` to display relevant information for active sessions. - Enhanced prompt documentation in `prompts.ts` to clarify output expectations. - Refactored logger methods in `logger.ts` to instance methods for better encapsulation.
This commit is contained in:
+89
-78
@@ -23,10 +23,9 @@ export function contextHook(input?: SessionStartInput): void {
|
||||
const db = new HooksDatabase();
|
||||
|
||||
try {
|
||||
const summaries = db.getRecentSummaries(project, 5);
|
||||
const observations = db.getRecentObservations(project, 20);
|
||||
const sessions = db.getRecentSessionsWithStatus(project, 3);
|
||||
|
||||
if (summaries.length === 0 && observations.length === 0) {
|
||||
if (sessions.length === 0) {
|
||||
// Output directly to stdout for injection into context
|
||||
console.log('# Recent Session Context\n\nNo previous sessions found for this project yet.');
|
||||
return;
|
||||
@@ -35,95 +34,107 @@ export function contextHook(input?: SessionStartInput): void {
|
||||
const output: string[] = [];
|
||||
output.push('# Recent Session Context');
|
||||
output.push('');
|
||||
|
||||
// Show observations first
|
||||
if (observations.length > 0) {
|
||||
output.push(`## Recent Observations (${observations.length})`);
|
||||
output.push('');
|
||||
|
||||
// Group observations by type
|
||||
const byType: Record<string, Array<{text: string; prompt_number: number | null; created_at: string}>> = {};
|
||||
for (const obs of observations) {
|
||||
if (!byType[obs.type]) byType[obs.type] = [];
|
||||
byType[obs.type].push({ text: obs.text, prompt_number: obs.prompt_number, created_at: obs.created_at });
|
||||
}
|
||||
|
||||
// Display each type
|
||||
const typeOrder = ['feature', 'bugfix', 'refactor', 'discovery', 'decision'];
|
||||
for (const type of typeOrder) {
|
||||
if (byType[type] && byType[type].length > 0) {
|
||||
output.push(`### ${type.charAt(0).toUpperCase() + type.slice(1)}s`);
|
||||
for (const obs of byType[type]) {
|
||||
const promptLabel = obs.prompt_number ? ` (prompt #${obs.prompt_number})` : '';
|
||||
output.push(`- ${obs.text}${promptLabel}`);
|
||||
}
|
||||
output.push('');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (summaries.length === 0) {
|
||||
console.log(output.join('\n'));
|
||||
return;
|
||||
}
|
||||
|
||||
output.push('## Recent Sessions');
|
||||
output.push('');
|
||||
const sessionWord = summaries.length === 1 ? 'session' : 'sessions';
|
||||
output.push(`Showing last ${summaries.length} ${sessionWord} for **${project}**:`);
|
||||
output.push(`Showing last ${sessions.length} session(s) for **${project}**:`);
|
||||
output.push('');
|
||||
|
||||
for (const summary of summaries) {
|
||||
for (const session of sessions) {
|
||||
if (!session.sdk_session_id) continue;
|
||||
|
||||
output.push('---');
|
||||
output.push('');
|
||||
|
||||
const promptLabel = summary.prompt_number ? ` (Prompt #${summary.prompt_number})` : '';
|
||||
output.push(`**Summary${promptLabel}**`);
|
||||
output.push('');
|
||||
// Check if session has a summary
|
||||
if (session.has_summary) {
|
||||
const summary = db.getSummaryForSession(session.sdk_session_id);
|
||||
|
||||
if (summary.request) {
|
||||
output.push(`**Request:** ${summary.request}`);
|
||||
}
|
||||
if (summary) {
|
||||
const promptLabel = summary.prompt_number ? ` (Prompt #${summary.prompt_number})` : '';
|
||||
output.push(`**Summary${promptLabel}**`);
|
||||
output.push('');
|
||||
|
||||
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_read) {
|
||||
try {
|
||||
const files = JSON.parse(summary.files_read);
|
||||
if (Array.isArray(files) && files.length > 0) {
|
||||
output.push(`**Files Read:** ${files.join(', ')}`);
|
||||
if (summary.request) {
|
||||
output.push(`**Request:** ${summary.request}`);
|
||||
}
|
||||
} catch {
|
||||
if (summary.files_read.trim()) {
|
||||
output.push(`**Files Read:** ${summary.files_read}`);
|
||||
|
||||
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_read) {
|
||||
try {
|
||||
const files = JSON.parse(summary.files_read);
|
||||
if (Array.isArray(files) && files.length > 0) {
|
||||
output.push(`**Files Read:** ${files.join(', ')}`);
|
||||
}
|
||||
} catch {
|
||||
if (summary.files_read.trim()) {
|
||||
output.push(`**Files Read:** ${summary.files_read}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 (summary.files_edited.trim()) {
|
||||
output.push(`**Files Edited:** ${summary.files_edited}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output.push(`**Date:** ${summary.created_at.split('T')[0]}`);
|
||||
}
|
||||
}
|
||||
} else if (session.status === 'active') {
|
||||
// Active session without summary - show observation titles
|
||||
output.push(`**In Progress**`);
|
||||
output.push('');
|
||||
|
||||
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 (summary.files_edited.trim()) {
|
||||
output.push(`**Files Edited:** ${summary.files_edited}`);
|
||||
}
|
||||
if (session.user_prompt) {
|
||||
output.push(`**Request:** ${session.user_prompt}`);
|
||||
}
|
||||
|
||||
const observations = db.getObservationsForSession(session.sdk_session_id);
|
||||
|
||||
if (observations.length > 0) {
|
||||
output.push('');
|
||||
output.push(`**Observations (${observations.length}):**`);
|
||||
for (const obs of observations) {
|
||||
output.push(`- ${obs.title}`);
|
||||
}
|
||||
} else {
|
||||
output.push('');
|
||||
output.push('*No observations yet*');
|
||||
}
|
||||
|
||||
output.push('');
|
||||
output.push(`**Status:** Active - summary pending`);
|
||||
output.push(`**Date:** ${session.started_at.split('T')[0]}`);
|
||||
} else {
|
||||
// Failed or completed session without summary
|
||||
output.push(`**${session.status.charAt(0).toUpperCase() + session.status.slice(1)}**`);
|
||||
output.push('');
|
||||
|
||||
if (session.user_prompt) {
|
||||
output.push(`**Request:** ${session.user_prompt}`);
|
||||
}
|
||||
|
||||
output.push('');
|
||||
output.push(`**Status:** ${session.status} - no summary available`);
|
||||
output.push(`**Date:** ${session.started_at.split('T')[0]}`);
|
||||
}
|
||||
|
||||
output.push(`**Date:** ${summary.created_at.split('T')[0]}`);
|
||||
output.push('');
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ Skip routine operations:
|
||||
- Package installations with no errors
|
||||
- Simple file listings
|
||||
- Repetitive operations you've already documented
|
||||
- **No output necessary if skipping.**
|
||||
|
||||
OUTPUT FORMAT
|
||||
-------------
|
||||
|
||||
@@ -325,6 +325,81 @@ export class HooksDatabase {
|
||||
return stmt.all(project, limit) as any[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recent sessions with their status and summary info
|
||||
*/
|
||||
getRecentSessionsWithStatus(project: string, limit: number = 3): Array<{
|
||||
sdk_session_id: string | null;
|
||||
status: string;
|
||||
started_at: string;
|
||||
user_prompt: string | null;
|
||||
has_summary: boolean;
|
||||
}> {
|
||||
const stmt = this.db.prepare(`
|
||||
SELECT
|
||||
s.sdk_session_id,
|
||||
s.status,
|
||||
s.started_at,
|
||||
s.user_prompt,
|
||||
CASE WHEN sum.sdk_session_id IS NOT NULL THEN 1 ELSE 0 END as has_summary
|
||||
FROM sdk_sessions s
|
||||
LEFT JOIN session_summaries sum ON s.sdk_session_id = sum.sdk_session_id
|
||||
WHERE s.project = ? AND s.sdk_session_id IS NOT NULL
|
||||
GROUP BY s.sdk_session_id
|
||||
ORDER BY s.started_at_epoch DESC
|
||||
LIMIT ?
|
||||
`);
|
||||
|
||||
return stmt.all(project, limit) as any[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get observations for a specific session
|
||||
*/
|
||||
getObservationsForSession(sdkSessionId: string): Array<{
|
||||
title: string;
|
||||
subtitle: string;
|
||||
type: string;
|
||||
prompt_number: number | null;
|
||||
}> {
|
||||
const stmt = this.db.prepare(`
|
||||
SELECT title, subtitle, type, prompt_number
|
||||
FROM observations
|
||||
WHERE sdk_session_id = ?
|
||||
ORDER BY created_at_epoch ASC
|
||||
`);
|
||||
|
||||
return stmt.all(sdkSessionId) as any[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get summary for a specific session
|
||||
*/
|
||||
getSummaryForSession(sdkSessionId: string): {
|
||||
request: string | null;
|
||||
investigated: string | null;
|
||||
learned: string | null;
|
||||
completed: string | null;
|
||||
next_steps: string | null;
|
||||
files_read: string | null;
|
||||
files_edited: string | null;
|
||||
notes: string | null;
|
||||
prompt_number: number | null;
|
||||
created_at: string;
|
||||
} | null {
|
||||
const stmt = this.db.prepare(`
|
||||
SELECT
|
||||
request, investigated, learned, completed, next_steps,
|
||||
files_read, files_edited, notes, prompt_number, created_at
|
||||
FROM session_summaries
|
||||
WHERE sdk_session_id = ?
|
||||
ORDER BY created_at_epoch DESC
|
||||
LIMIT 1
|
||||
`);
|
||||
|
||||
return stmt.get(sdkSessionId) as any || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get session by ID
|
||||
*/
|
||||
|
||||
+2
-2
@@ -36,14 +36,14 @@ class Logger {
|
||||
/**
|
||||
* Create correlation ID for tracking an observation through the pipeline
|
||||
*/
|
||||
static correlationId(sessionId: number, observationNum: number): string {
|
||||
correlationId(sessionId: number, observationNum: number): string {
|
||||
return `obs-${sessionId}-${observationNum}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create session correlation ID
|
||||
*/
|
||||
static sessionId(sessionId: number): string {
|
||||
sessionId(sessionId: number): string {
|
||||
return `session-${sessionId}`;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user