210 lines
6.2 KiB
JavaScript
Executable File
210 lines
6.2 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Session Finalizer Hook - Stop hook
|
|
* Finalizes session, creates documentation, and saves state
|
|
*/
|
|
|
|
const fs = require('fs-extra');
|
|
const path = require('path');
|
|
|
|
// Add lib directory to require path
|
|
const libPath = path.join(__dirname, '..', 'lib');
|
|
const { SessionStateManager } = require(path.join(libPath, 'session-state'));
|
|
const { ShadowLearner } = require(path.join(libPath, 'shadow-learner'));
|
|
const { ContextMonitor } = require(path.join(libPath, 'context-monitor'));
|
|
|
|
async function createRecoveryInfo(sessionSummary, contextMonitor) {
|
|
/**
|
|
* Create recovery information if needed
|
|
*/
|
|
try {
|
|
const contextUsage = contextMonitor.getContextUsageRatio();
|
|
|
|
// If context was high when session ended, create recovery guide
|
|
if (contextUsage > 0.8) {
|
|
let recoveryContent = `# Session Recovery Information
|
|
|
|
## Context Status
|
|
- **Context Usage**: ${(contextUsage * 100).toFixed(1)}% when session ended
|
|
- **Reason**: Session ended with high context usage
|
|
|
|
## What This Means
|
|
Your Claude session ended while using a significant amount of context. This could mean:
|
|
1. You were working on a complex task
|
|
2. Context limits were approaching
|
|
3. Session was interrupted
|
|
|
|
## Recovery Steps
|
|
|
|
### 1. Check Your Progress
|
|
Review these recently modified files:
|
|
`;
|
|
|
|
for (const filePath of sessionSummary.modifiedFiles || sessionSummary.modified_files || []) {
|
|
recoveryContent += `- ${filePath}\n`;
|
|
}
|
|
|
|
recoveryContent += `
|
|
### 2. Review Last Actions
|
|
Recent commands executed:
|
|
`;
|
|
|
|
const recentCommands = (sessionSummary.commandsExecuted || sessionSummary.commands_executed || []).slice(-5);
|
|
for (const cmdInfo of recentCommands) {
|
|
recoveryContent += `- \`${cmdInfo.command}\`\n`;
|
|
}
|
|
|
|
recoveryContent += `
|
|
### 3. Continue Your Work
|
|
1. Check \`ACTIVE_TODOS.md\` for pending tasks
|
|
2. Review \`LAST_SESSION.md\` for complete session history
|
|
3. Use \`git status\` to see current file changes
|
|
4. Consider committing your progress: \`git add -A && git commit -m "Work in progress"\`
|
|
|
|
### 4. Available Backups
|
|
`;
|
|
|
|
for (const backup of sessionSummary.backupHistory || sessionSummary.backup_history || []) {
|
|
const status = backup.success ? '✅' : '❌';
|
|
recoveryContent += `- ${status} ${backup.backup_id || backup.backupId} - ${backup.reason}\n`;
|
|
}
|
|
|
|
recoveryContent += `
|
|
## Quick Recovery Commands
|
|
\`\`\`bash
|
|
# Check current status
|
|
git status
|
|
|
|
# View recent changes
|
|
git diff
|
|
|
|
# List available backups
|
|
ls .claude_hooks/backups/
|
|
|
|
# View active todos
|
|
cat ACTIVE_TODOS.md
|
|
|
|
# View last session summary
|
|
cat LAST_SESSION.md
|
|
\`\`\`
|
|
|
|
*This recovery guide was created because your session ended with ${(contextUsage * 100).toFixed(1)}% context usage.*
|
|
`;
|
|
|
|
await fs.writeFile('RECOVERY_GUIDE.md', recoveryContent);
|
|
}
|
|
|
|
} catch (error) {
|
|
// Don't let recovery guide creation break session finalization
|
|
}
|
|
}
|
|
|
|
async function logSessionCompletion(sessionSummary) {
|
|
/**
|
|
* Log session completion for analysis
|
|
*/
|
|
try {
|
|
const logDir = path.join('.claude_hooks', 'logs');
|
|
await fs.ensureDir(logDir);
|
|
|
|
const completionLog = {
|
|
timestamp: new Date().toISOString(),
|
|
type: 'session_completion',
|
|
session_id: sessionSummary.sessionId || sessionSummary.session_id || 'unknown',
|
|
duration_minutes: (sessionSummary.sessionStats || sessionSummary.session_stats || {}).duration_minutes || 0,
|
|
total_tools: (sessionSummary.sessionStats || sessionSummary.session_stats || {}).total_tool_calls || 0,
|
|
files_modified: (sessionSummary.modifiedFiles || sessionSummary.modified_files || []).length,
|
|
commands_executed: (sessionSummary.sessionStats || sessionSummary.session_stats || {}).total_commands || 0,
|
|
backups_created: (sessionSummary.backupHistory || sessionSummary.backup_history || []).length
|
|
};
|
|
|
|
const logFile = path.join(logDir, 'session_completions.jsonl');
|
|
|
|
await fs.appendFile(logFile, JSON.stringify(completionLog) + '\n');
|
|
|
|
} catch (error) {
|
|
// Don't let logging errors break finalization
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
try {
|
|
let inputData = {};
|
|
|
|
// Handle stdin input
|
|
if (!process.stdin.isTTY) {
|
|
try {
|
|
let input = '';
|
|
process.stdin.setEncoding('utf8');
|
|
|
|
for await (const chunk of process.stdin) {
|
|
input += chunk;
|
|
}
|
|
|
|
if (input.trim()) {
|
|
inputData = JSON.parse(input);
|
|
}
|
|
} catch (error) {
|
|
// If input parsing fails, use empty object
|
|
inputData = {};
|
|
}
|
|
}
|
|
|
|
// Initialize components
|
|
const sessionManager = new SessionStateManager();
|
|
const shadowLearner = new ShadowLearner();
|
|
const contextMonitor = new ContextMonitor();
|
|
|
|
// Create session documentation
|
|
await sessionManager.createContinuationDocs();
|
|
|
|
// Save all learned patterns
|
|
await shadowLearner.saveDatabase();
|
|
|
|
// Get session summary for logging
|
|
const sessionSummary = await sessionManager.getSessionSummary();
|
|
|
|
// Create recovery guide if session was interrupted
|
|
await createRecoveryInfo(sessionSummary, contextMonitor);
|
|
|
|
// Clean up session
|
|
await sessionManager.cleanupSession();
|
|
|
|
// Log session completion
|
|
await logSessionCompletion(sessionSummary);
|
|
|
|
const modifiedFiles = sessionSummary.modifiedFiles || sessionSummary.modified_files || [];
|
|
const totalTools = (sessionSummary.sessionStats || sessionSummary.session_stats || {}).total_tool_calls || 0;
|
|
|
|
// Always allow - this is a cleanup hook
|
|
const response = {
|
|
allow: true,
|
|
message: `Session finalized. Modified ${modifiedFiles.length} files, used ${totalTools} tools.`
|
|
};
|
|
|
|
console.log(JSON.stringify(response));
|
|
process.exit(0);
|
|
|
|
} catch (error) {
|
|
// Session finalization should never block
|
|
const response = {
|
|
allow: true,
|
|
message: `Session finalization error: ${error.message}`
|
|
};
|
|
console.log(JSON.stringify(response));
|
|
process.exit(0);
|
|
}
|
|
}
|
|
|
|
// Handle unhandled promise rejections
|
|
process.on('unhandledRejection', (error) => {
|
|
const response = {
|
|
allow: true,
|
|
message: `Session finalization error: ${error.message}`
|
|
};
|
|
console.log(JSON.stringify(response));
|
|
process.exit(0);
|
|
});
|
|
|
|
main(); |