feat: add functionality to extract and display prior session messages
- Implemented `cwdToDashed` helper function to format current working directory for transcript file paths. - Added `extractPriorMessages` function to read and parse the last user and assistant messages from a transcript file. - Enhanced `contextHook` to retrieve and display prior session messages if enabled in the configuration. - Updated output formatting to include a "Previously" section showing last messages from the prior session.
This commit is contained in:
@@ -219,6 +219,78 @@ function renderSummaryField(label: string, value: string | null, color: string,
|
||||
return [`**${label}**: ${value}`, ''];
|
||||
}
|
||||
|
||||
// Helper: Convert cwd path to dashed format for transcript directory name
|
||||
function cwdToDashed(cwd: string): string {
|
||||
// Remove leading slash and convert remaining slashes to dashes
|
||||
return cwd.replace(/^\//, '').replace(/\//g, '-');
|
||||
}
|
||||
|
||||
// Helper: Extract last user and assistant messages from transcript file
|
||||
function extractPriorMessages(transcriptPath: string): { userMessage: string; assistantMessage: string } {
|
||||
try {
|
||||
if (!existsSync(transcriptPath)) {
|
||||
return { userMessage: '', assistantMessage: '' };
|
||||
}
|
||||
|
||||
const content = readFileSync(transcriptPath, 'utf-8').trim();
|
||||
if (!content) {
|
||||
return { userMessage: '', assistantMessage: '' };
|
||||
}
|
||||
|
||||
const lines = content.split('\n').filter(line => line.trim());
|
||||
let lastUserMessage = '';
|
||||
let lastAssistantMessage = '';
|
||||
|
||||
// Parse JSONL backwards to find last user and assistant messages
|
||||
for (let i = lines.length - 1; i >= 0; i--) {
|
||||
try {
|
||||
const entry = JSON.parse(lines[i]);
|
||||
|
||||
// Find last assistant message
|
||||
if (!lastAssistantMessage && entry.type === 'assistant' && entry.message?.content) {
|
||||
let text = '';
|
||||
for (const block of entry.message.content) {
|
||||
if (block.type === 'text') {
|
||||
text += block.text;
|
||||
}
|
||||
}
|
||||
// Remove system-reminder tags
|
||||
text = text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, '').trim();
|
||||
if (text) {
|
||||
lastAssistantMessage = text;
|
||||
}
|
||||
}
|
||||
|
||||
// Find last user message
|
||||
if (!lastUserMessage && entry.type === 'user' && entry.message?.content) {
|
||||
let text = '';
|
||||
for (const block of entry.message.content) {
|
||||
if (block.type === 'text') {
|
||||
text += block.text;
|
||||
}
|
||||
}
|
||||
if (text) {
|
||||
lastUserMessage = text;
|
||||
}
|
||||
}
|
||||
|
||||
// Stop once we have both
|
||||
if (lastUserMessage && lastAssistantMessage) {
|
||||
break;
|
||||
}
|
||||
} catch (parseError) {
|
||||
// Skip malformed lines
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return { userMessage: lastUserMessage, assistantMessage: lastAssistantMessage };
|
||||
} catch (error) {
|
||||
logger.debug('HOOK', `Failed to extract prior messages from ${transcriptPath}:`, {}, error as Error);
|
||||
return { userMessage: '', assistantMessage: '' };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Context Hook Main Logic
|
||||
*/
|
||||
@@ -286,6 +358,33 @@ async function contextHook(input?: SessionStartInput, useColors: boolean = false
|
||||
LIMIT ?
|
||||
`).all(project, config.sessionCount + SUMMARY_LOOKAHEAD) as SessionSummary[];
|
||||
|
||||
// Retrieve prior session messages if enabled
|
||||
let priorUserMessage = '';
|
||||
let priorAssistantMessage = '';
|
||||
if (config.showLastMessage && observations.length > 0) {
|
||||
try {
|
||||
const currentSessionId = input?.session_id;
|
||||
|
||||
// Find the first observation from a different session (the prior session)
|
||||
const priorSessionObs = observations.find(obs => obs.sdk_session_id !== currentSessionId);
|
||||
|
||||
if (priorSessionObs) {
|
||||
const priorSessionId = priorSessionObs.sdk_session_id;
|
||||
|
||||
// Construct transcript path: ~/.claude/projects/{dashed-cwd}/{session_id}.jsonl
|
||||
const dashedCwd = cwdToDashed(cwd);
|
||||
const transcriptPath = path.join(homedir(), '.claude', 'projects', dashedCwd, `${priorSessionId}.jsonl`);
|
||||
|
||||
// Extract messages from transcript
|
||||
const messages = extractPriorMessages(transcriptPath);
|
||||
priorUserMessage = messages.userMessage;
|
||||
priorAssistantMessage = messages.assistantMessage;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.debug('HOOK', 'Failed to retrieve prior session messages:', {}, error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
// If we have neither observations nor summaries, show empty state
|
||||
if (observations.length === 0 && recentSummaries.length === 0) {
|
||||
db.close();
|
||||
@@ -314,6 +413,37 @@ async function contextHook(input?: SessionStartInput, useColors: boolean = false
|
||||
output.push('');
|
||||
}
|
||||
|
||||
// Previously section (last messages from prior session)
|
||||
if (priorUserMessage || priorAssistantMessage) {
|
||||
if (useColors) {
|
||||
output.push(`${colors.bright}${colors.magenta}📋 Previously${colors.reset}`);
|
||||
output.push('');
|
||||
if (priorUserMessage) {
|
||||
output.push(`${colors.dim}User: ${priorUserMessage}${colors.reset}`);
|
||||
output.push('');
|
||||
}
|
||||
if (priorAssistantMessage) {
|
||||
output.push(`${colors.dim}Assistant: ${priorAssistantMessage}${colors.reset}`);
|
||||
output.push('');
|
||||
}
|
||||
output.push(`${colors.gray}${'─'.repeat(60)}${colors.reset}`);
|
||||
output.push('');
|
||||
} else {
|
||||
output.push(`**📋 Previously**`);
|
||||
output.push('');
|
||||
if (priorUserMessage) {
|
||||
output.push(`User: ${priorUserMessage}`);
|
||||
output.push('');
|
||||
}
|
||||
if (priorAssistantMessage) {
|
||||
output.push(`Assistant: ${priorAssistantMessage}`);
|
||||
output.push('');
|
||||
}
|
||||
output.push('---');
|
||||
output.push('');
|
||||
}
|
||||
}
|
||||
|
||||
// Chronological Timeline
|
||||
if (timelineObs.length > 0) {
|
||||
// Legend/Key
|
||||
|
||||
Reference in New Issue
Block a user