auto-log.js•4.46 kB
#!/usr/bin/env node
import { execSync } from 'child_process';
import { readFileSync, existsSync } from 'fs';
import { join, basename } from 'path';
/**
* Auto-log conversation script for Claude Code hooks
* This script runs after each conversation ends to automatically log the interaction
*/
function logConversation(projectName, userInput, aiResponse, actions, platform) {
// Truncate long content
if (userInput.length > 1000) {
userInput = userInput.substring(0, 1000) + '...';
}
if (aiResponse.length > 2000) {
aiResponse = aiResponse.substring(0, 2000) + '...';
}
// Call the MCP tool to log the conversation
const logCommand = `claude mcp call conversation-logger log_conversation '{
"project": "${projectName}",
"userInput": ${JSON.stringify(userInput)},
"aiResponse": ${JSON.stringify(aiResponse)},
"platform": "${platform}",
"actions": ${JSON.stringify(actions)},
"tags": ["auto-logged"]
}'`;
console.log('Executing auto-log command...');
const result = execSync(logCommand, { encoding: 'utf8', stdio: 'pipe' });
console.log('Auto-log result:', result);
}
function main() {
try {
const projectDir = process.argv[2] || process.cwd();
const projectName = basename(projectDir);
console.log(`Auto-logging conversation for project: ${projectName}`);
// Check if transcript file exists
const transcriptPath = join(projectDir, 'transcript.jsonl');
if (!existsSync(transcriptPath)) {
console.log('No transcript file found, trying alternative approach...');
// Alternative: use environment variables or CLI history if available
const userInput = process.env.CLAUDE_LAST_USER_INPUT || 'Auto-logged conversation';
const aiResponse = process.env.CLAUDE_LAST_AI_RESPONSE || 'Conversation auto-logged via hook';
if (userInput === 'Auto-logged conversation') {
console.log('No conversation data available, skipping auto-log');
return;
}
logConversation(projectName, userInput, aiResponse, [], 'claude-code');
return;
}
// Read the last few lines of transcript to get recent conversation
const transcript = readFileSync(transcriptPath, 'utf8');
const lines = transcript.trim().split('\n').filter(line => line.trim());
if (lines.length === 0) {
console.log('Empty transcript, skipping auto-log');
return;
}
// Get the last conversation exchange
const lastLines = lines.slice(-10); // Get last 10 lines to capture full exchange
let userInput = '';
let aiResponse = '';
let actions = [];
// Parse the transcript lines to extract user input and AI response
for (const line of lastLines) {
try {
const entry = JSON.parse(line);
if (entry.role === 'user' && entry.content) {
// Extract text content from user message
if (typeof entry.content === 'string') {
userInput = entry.content;
} else if (Array.isArray(entry.content)) {
const textContent = entry.content.find(c => c.type === 'text');
if (textContent) {
userInput = textContent.text;
}
}
} else if (entry.role === 'assistant' && entry.content) {
// Extract AI response and actions
if (typeof entry.content === 'string') {
aiResponse = entry.content;
} else if (Array.isArray(entry.content)) {
const textContent = entry.content.find(c => c.type === 'text');
if (textContent) {
aiResponse = textContent.text;
}
// Extract tool use actions
const toolUses = entry.content.filter(c => c.type === 'tool_use');
actions = toolUses.map(tool => `${tool.name}: ${tool.input ? JSON.stringify(tool.input) : ''}`);
}
}
} catch (e) {
// Skip invalid JSON lines
continue;
}
}
// Only log if we have meaningful content
if (!userInput.trim() || !aiResponse.trim()) {
console.log('No meaningful conversation content found, skipping auto-log');
return;
}
logConversation(projectName, userInput, aiResponse, actions, 'claude-code');
} catch (error) {
console.error('Auto-log failed:', error.message);
// Don't throw error to avoid disrupting Claude Code workflow
}
}
main();