Add support for index view in context hook and refactor summary retrieval method
This commit is contained in:
@@ -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
@@ -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');
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user