302 lines
9.4 KiB
TypeScript
302 lines
9.4 KiB
TypeScript
import path from 'path';
|
|
import { SessionStore } from '../services/sqlite/SessionStore.js';
|
|
import { ensureWorkerRunning } from '../shared/worker-utils.js';
|
|
|
|
export interface SessionStartInput {
|
|
session_id?: string;
|
|
transcript_path?: string;
|
|
cwd?: string;
|
|
hook_event_name?: string;
|
|
source?: "startup" | "resume" | "clear" | "compact";
|
|
[key: string]: any;
|
|
}
|
|
|
|
// ANSI color codes for terminal output
|
|
const colors = {
|
|
reset: '\x1b[0m',
|
|
bright: '\x1b[1m',
|
|
dim: '\x1b[2m',
|
|
cyan: '\x1b[36m',
|
|
green: '\x1b[32m',
|
|
yellow: '\x1b[33m',
|
|
blue: '\x1b[34m',
|
|
magenta: '\x1b[35m',
|
|
gray: '\x1b[90m',
|
|
};
|
|
|
|
/**
|
|
* Context Hook - SessionStart
|
|
* Shows user what happened in recent sessions
|
|
*
|
|
* Output: Returns formatted context string to be wrapped in hookSpecificOutput
|
|
*/
|
|
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();
|
|
const project = cwd ? path.basename(cwd) : 'unknown-project';
|
|
|
|
const db = new SessionStore();
|
|
|
|
try {
|
|
const summaries = db.getRecentSummariesWithSessionInfo(project, 3);
|
|
|
|
if (summaries.length === 0) {
|
|
if (useColors) {
|
|
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 `# [${project}] recent context\n\nNo previous summaries found for this project yet.`;
|
|
}
|
|
|
|
const output: string[] = [];
|
|
|
|
// 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(`# [${project}] recent context`);
|
|
output.push('');
|
|
}
|
|
|
|
// Show index of previous summaries (oldest to newest)
|
|
if (summaries.length > 1) {
|
|
if (useColors) {
|
|
output.push(`${colors.bright}${colors.dim}Previous Requests:${colors.reset}`);
|
|
output.push('');
|
|
} else {
|
|
output.push('**Previous Requests:**');
|
|
output.push('');
|
|
}
|
|
|
|
// 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.dim}• ${dateTimeStr}:${colors.reset} ${prev.request || '(no request)'}`);
|
|
} else {
|
|
output.push(`- ${dateTimeStr}: ${prev.request || '(no request)'}`);
|
|
}
|
|
}
|
|
|
|
if (useColors) {
|
|
output.push('');
|
|
output.push(`${colors.gray}${'─'.repeat(60)}${colors.reset}`);
|
|
output.push('');
|
|
} else {
|
|
output.push('');
|
|
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('');
|
|
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('');
|
|
output.push(`${colors.dim}${'─'.repeat(23)} New Session ${'─'.repeat(24)}${colors.reset}`);
|
|
output.push('');
|
|
} else {
|
|
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 {
|
|
// First summary - just add a blank line after header
|
|
if (useColors) {
|
|
output.push('');
|
|
}
|
|
}
|
|
|
|
isFirstSummary = false;
|
|
|
|
if (summary.request) {
|
|
if (useColors) {
|
|
output.push(`${colors.bright}${colors.yellow}Request:${colors.reset} ${summary.request}`);
|
|
output.push('');
|
|
} else {
|
|
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}`);
|
|
}
|
|
|
|
return output.join('\n');
|
|
} finally {
|
|
db.close();
|
|
}
|
|
} |