Add support for index view in context hook and refactor summary retrieval method

This commit is contained in:
Alex Newman
2025-10-21 17:28:39 -04:00
parent ef572ec032
commit 86214b93a9
3 changed files with 232 additions and 159 deletions
+5 -2
View File
@@ -8,9 +8,12 @@ import { contextHook } from '../../hooks/context.js';
import { stdin } from 'process';
try {
// Check for --index flag
const useIndexView = process.argv.includes('--index');
if (stdin.isTTY) {
// Running manually from terminal - print formatted output with colors
const contextOutput = contextHook(undefined, true);
const contextOutput = contextHook(undefined, true, useIndexView);
console.log(contextOutput);
process.exit(0);
} else {
@@ -19,7 +22,7 @@ try {
stdin.on('data', (chunk) => input += chunk);
stdin.on('end', () => {
const parsed = input.trim() ? JSON.parse(input) : undefined;
const contextOutput = contextHook(parsed, false);
const contextOutput = contextHook(parsed, false, useIndexView);
const result = {
hookSpecificOutput: {
hookEventName: "SessionStart",
+202 -157
View File
@@ -30,7 +30,7 @@ const colors = {
*
* Output: Returns formatted context string to be wrapped in hookSpecificOutput
*/
export function contextHook(input?: SessionStartInput, useColors: boolean = false): string {
export function contextHook(input?: SessionStartInput, useColors: boolean = false, useIndexView: boolean = false): string {
// v4.0.0: Ensure worker is running before loading context
ensureWorkerRunning();
const cwd = input?.cwd ?? process.cwd();
@@ -39,215 +39,260 @@ export function contextHook(input?: SessionStartInput, useColors: boolean = fals
const db = new SessionStore();
try {
const sessions = db.getRecentSessionsWithStatus(project, 3);
const summaries = db.getRecentSummariesWithSessionInfo(project, 3);
if (sessions.length === 0) {
if (summaries.length === 0) {
if (useColors) {
return `\n${colors.bright}${colors.cyan}📝 Recent Session Context${colors.reset}\n\n${colors.dim}No previous sessions found for this project yet.${colors.reset}\n`;
return `\n${colors.bright}${colors.cyan}📝 [${project}] recent context${colors.reset}\n${colors.gray}${'─'.repeat(60)}${colors.reset}\n\n${colors.dim}No previous summaries found for this project yet.${colors.reset}\n`;
}
return '# Recent Session Context\n\nNo previous sessions found for this project yet.';
return `# [${project}] recent context\n\nNo previous summaries found for this project yet.`;
}
const output: string[] = [];
if (useColors) {
output.push('');
output.push(`${colors.bright}${colors.cyan}📝 Recent Session Context${colors.reset}`);
output.push(`${colors.gray}${'─'.repeat(60)}${colors.reset}`);
output.push(`${colors.dim}Showing last ${sessions.length} session(s) for ${colors.reset}${colors.bright}${project}${colors.reset}`);
output.push('');
} else {
output.push('# Recent Session Context');
output.push('');
output.push(`Showing last ${sessions.length} session(s) for **${project}**:`);
output.push('');
}
for (const session of sessions) {
if (!session.sdk_session_id) continue;
// Index view: Show previous as index, latest in full at bottom (chat-style)
if (useIndexView) {
if (useColors) {
output.push('');
output.push(`${colors.bright}${colors.cyan}📝 [${project}] recent context${colors.reset}`);
output.push(`${colors.gray}${'─'.repeat(60)}${colors.reset}`);
output.push('');
} else {
output.push('---');
output.push(`# [${project}] recent context`);
output.push('');
}
// Check if session has a summary
if (session.has_summary) {
const summary = db.getSummaryForSession(session.sdk_session_id);
if (summary) {
const promptLabel = summary.prompt_number ? ` (Prompt #${summary.prompt_number})` : '';
if (useColors) {
output.push(`${colors.bright}${colors.green}✓ Summary${promptLabel}${colors.reset}`);
output.push('');
} else {
output.push(`**Summary${promptLabel}**`);
output.push('');
}
if (summary.request) {
if (useColors) {
output.push(`${colors.bright}${colors.yellow}Request:${colors.reset} ${summary.request}`);
output.push('');
} else {
output.push(`**Request:** ${summary.request}`);
}
}
if (summary.learned) {
if (useColors) {
output.push(`${colors.bright}${colors.blue}Learned:${colors.reset} ${summary.learned}`);
output.push('');
} else {
output.push(`**Learned:** ${summary.learned}`);
}
}
if (summary.completed) {
if (useColors) {
output.push(`${colors.bright}${colors.green}Completed:${colors.reset} ${summary.completed}`);
output.push('');
} else {
output.push(`**Completed:** ${summary.completed}`);
}
}
if (summary.next_steps) {
if (useColors) {
output.push(`${colors.bright}${colors.magenta}Next Steps:${colors.reset} ${summary.next_steps}`);
output.push('');
} else {
output.push(`**Next Steps:** ${summary.next_steps}`);
}
}
// Get files from observations (not from summary which is never populated)
const sessionFiles = db.getFilesForSession(session.sdk_session_id);
if (sessionFiles.filesRead.length > 0) {
if (useColors) {
output.push(`${colors.dim}Files Read: ${sessionFiles.filesRead.join(', ')}${colors.reset}`);
} else {
output.push(`**Files Read:** ${sessionFiles.filesRead.join(', ')}`);
}
}
if (sessionFiles.filesModified.length > 0) {
if (useColors) {
output.push(`${colors.dim}Files Modified: ${sessionFiles.filesModified.join(', ')}${colors.reset}`);
} else {
output.push(`**Files Modified:** ${sessionFiles.filesModified.join(', ')}`);
}
}
const dateTime = new Date(summary.created_at).toLocaleString();
if (useColors) {
output.push(`${colors.dim}Date: ${dateTime}${colors.reset}`);
} else {
output.push(`**Date:** ${dateTime}`);
}
}
} else if (session.status === 'active') {
// Active session without summary - show observation titles
// Show index of previous summaries (oldest to newest)
if (summaries.length > 1) {
if (useColors) {
output.push(`${colors.bright}${colors.yellow}⏳ In Progress${colors.reset}`);
output.push(`${colors.bright}${colors.dim}Previous Requests:${colors.reset}`);
output.push('');
} else {
output.push(`**In Progress**`);
output.push('**Previous Requests:**');
output.push('');
}
if (session.user_prompt) {
// Iterate backwards through array (skip first which is most recent)
for (let i = summaries.length - 1; i >= 1; i--) {
const prev = summaries[i];
const prevDate = new Date(prev.created_at);
const dateTimeStr = prevDate.toLocaleString();
if (useColors) {
output.push(`${colors.bright}${colors.yellow}Request:${colors.reset} ${session.user_prompt}`);
output.push('');
output.push(`${colors.dim}${dateTimeStr}:${colors.reset} ${prev.request || '(no request)'}`);
} else {
output.push(`**Request:** ${session.user_prompt}`);
output.push(`- ${dateTimeStr}: ${prev.request || '(no request)'}`);
}
}
const observations = db.getObservationsForSession(session.sdk_session_id);
if (observations.length > 0) {
if (useColors) {
output.push('');
output.push(`${colors.gray}${'─'.repeat(60)}${colors.reset}`);
output.push('');
if (useColors) {
output.push(`${colors.bright}Observations (${observations.length}):${colors.reset}`);
for (const obs of observations) {
output.push(` ${colors.dim}${colors.reset} ${obs.title}`);
}
output.push('');
} else {
output.push(`**Observations (${observations.length}):**`);
for (const obs of observations) {
output.push(`- ${obs.title}`);
}
}
} else {
output.push('');
if (useColors) {
output.push(`${colors.dim}No observations yet${colors.reset}`);
output.push('');
} else {
output.push('*No observations yet*');
}
output.push('---');
output.push('');
}
}
// Show most recent summary in full at the bottom
const latest = summaries[0];
if (latest.request) {
if (useColors) {
output.push(`${colors.bright}${colors.yellow}Request:${colors.reset} ${latest.request}`);
output.push('');
} else {
output.push(`**Request:** ${latest.request}`);
output.push('');
}
}
if (latest.learned) {
if (useColors) {
output.push(`${colors.bright}${colors.blue}Learned:${colors.reset} ${latest.learned}`);
output.push('');
} else {
output.push(`**Learned:** ${latest.learned}`);
output.push('');
}
}
if (latest.completed) {
if (useColors) {
output.push(`${colors.bright}${colors.green}Completed:${colors.reset} ${latest.completed}`);
output.push('');
} else {
output.push(`**Completed:** ${latest.completed}`);
output.push('');
}
}
if (latest.next_steps) {
if (useColors) {
output.push(`${colors.bright}${colors.magenta}Next Steps:${colors.reset} ${latest.next_steps}`);
output.push('');
} else {
output.push(`**Next Steps:** ${latest.next_steps}`);
output.push('');
}
}
// Get files for latest summary
const latestFiles = db.getFilesForSession(latest.sdk_session_id);
if (latestFiles.filesRead.length > 0) {
if (useColors) {
output.push(`${colors.dim}Files Read: ${latestFiles.filesRead.join(', ')}${colors.reset}`);
} else {
output.push(`**Files Read:** ${latestFiles.filesRead.join(', ')}`);
}
}
if (latestFiles.filesModified.length > 0) {
if (useColors) {
output.push(`${colors.dim}Files Modified: ${latestFiles.filesModified.join(', ')}${colors.reset}`);
} else {
output.push(`**Files Modified:** ${latestFiles.filesModified.join(', ')}`);
}
}
const latestDate = new Date(latest.created_at).toLocaleString();
if (useColors) {
output.push(`${colors.dim}Date: ${latestDate}${colors.reset}`);
} else {
output.push(`**Date:** ${latestDate}`);
}
if (useColors) {
output.push('');
const activeDateTime = new Date(session.started_at).toLocaleString();
output.push(`${colors.gray}${'─'.repeat(60)}${colors.reset}`);
}
return output.join('\n');
}
if (useColors) {
output.push('');
output.push(`${colors.bright}${colors.cyan}📝 [${project}] recent context${colors.reset}`);
output.push(`${colors.gray}${'─'.repeat(60)}${colors.reset}`);
} else {
output.push(`# [${project}] recent context`);
output.push('');
}
let previousSessionId: string | null = null;
let isFirstSummary = true;
for (const summary of summaries) {
// Add session break indicator if this is a different session
const isNewSession = previousSessionId !== null && summary.sdk_session_id !== previousSessionId;
if (isNewSession) {
if (useColors) {
output.push(`${colors.dim}Status: Active - summary pending${colors.reset}`);
output.push(`${colors.dim}Date: ${activeDateTime}${colors.reset}`);
output.push('');
output.push(`${colors.dim}${'─'.repeat(23)} New Session ${'─'.repeat(24)}${colors.reset}`);
output.push('');
} else {
output.push(`**Status:** Active - summary pending`);
output.push(`**Date:** ${activeDateTime}`);
output.push('');
output.push('--- New Session ---');
output.push('');
}
} else if (!isFirstSummary) {
// Only show regular separator if not first summary and not showing "New Session"
if (useColors) {
output.push(`${colors.gray}${'─'.repeat(60)}${colors.reset}`);
output.push('');
} else {
output.push('---');
output.push('');
}
} else {
// Failed or completed session without summary
const displayStatus = session.status === 'failed' ? 'stopped' : session.status;
const statusIcon = session.status === 'failed' ? '⚠️' : '○';
// First summary - just add a blank line after header
if (useColors) {
const statusColor = session.status === 'failed' ? colors.yellow : colors.gray;
output.push(`${colors.bright}${statusColor}${statusIcon} ${displayStatus.charAt(0).toUpperCase() + displayStatus.slice(1)}${colors.reset}`);
output.push('');
} else {
output.push(`**${displayStatus.charAt(0).toUpperCase() + displayStatus.slice(1)}**`);
output.push('');
}
}
if (session.user_prompt) {
if (useColors) {
output.push(`${colors.bright}${colors.yellow}Request:${colors.reset} ${session.user_prompt}`);
output.push('');
} else {
output.push(`**Request:** ${session.user_prompt}`);
}
}
isFirstSummary = false;
output.push('');
const failedDateTime = new Date(session.started_at).toLocaleString();
if (summary.request) {
if (useColors) {
output.push(`${colors.dim}Status: ${displayStatus} - no summary available${colors.reset}`);
output.push(`${colors.dim}Date: ${failedDateTime}${colors.reset}`);
output.push(`${colors.bright}${colors.yellow}Request:${colors.reset} ${summary.request}`);
output.push('');
} else {
output.push(`**Status:** ${displayStatus} - no summary available`);
output.push(`**Date:** ${failedDateTime}`);
output.push(`**Request:** ${summary.request}`);
output.push('');
}
}
if (summary.learned) {
if (useColors) {
output.push(`${colors.bright}${colors.blue}Learned:${colors.reset} ${summary.learned}`);
output.push('');
} else {
output.push(`**Learned:** ${summary.learned}`);
output.push('');
}
}
if (summary.completed) {
if (useColors) {
output.push(`${colors.bright}${colors.green}Completed:${colors.reset} ${summary.completed}`);
output.push('');
} else {
output.push(`**Completed:** ${summary.completed}`);
output.push('');
}
}
if (summary.next_steps) {
if (useColors) {
output.push(`${colors.bright}${colors.magenta}Next Steps:${colors.reset} ${summary.next_steps}`);
output.push('');
} else {
output.push(`**Next Steps:** ${summary.next_steps}`);
output.push('');
}
}
// Get files from observations (not from summary which is never populated)
const sessionFiles = db.getFilesForSession(summary.sdk_session_id);
if (sessionFiles.filesRead.length > 0) {
if (useColors) {
output.push(`${colors.dim}Files Read: ${sessionFiles.filesRead.join(', ')}${colors.reset}`);
} else {
output.push(`**Files Read:** ${sessionFiles.filesRead.join(', ')}`);
}
}
if (sessionFiles.filesModified.length > 0) {
if (useColors) {
output.push(`${colors.dim}Files Modified: ${sessionFiles.filesModified.join(', ')}${colors.reset}`);
} else {
output.push(`**Files Modified:** ${sessionFiles.filesModified.join(', ')}`);
}
}
const dateTime = new Date(summary.created_at).toLocaleString();
if (useColors) {
output.push(`${colors.dim}Date: ${dateTime}${colors.reset}`);
} else {
output.push(`**Date:** ${dateTime}`);
}
if (!useColors) {
output.push('');
}
previousSessionId = summary.sdk_session_id;
}
if (useColors) {
output.push('');
output.push(`${colors.gray}${'─'.repeat(60)}${colors.reset}`);
output.push('');
}
return output.join('\n');
+25
View File
@@ -433,6 +433,31 @@ export class SessionStore {
return stmt.all(project, limit) as any[];
}
/**
* Get recent summaries with session info for context display
*/
getRecentSummariesWithSessionInfo(project: string, limit: number = 3): Array<{
sdk_session_id: string;
request: string | null;
learned: string | null;
completed: string | null;
next_steps: string | null;
prompt_number: number | null;
created_at: string;
}> {
const stmt = this.db.prepare(`
SELECT
sdk_session_id, request, learned, completed, next_steps,
prompt_number, created_at
FROM session_summaries
WHERE project = ?
ORDER BY created_at_epoch DESC
LIMIT ?
`);
return stmt.all(project, limit) as any[];
}
/**
* Get recent observations for a project
*/