#!/usr/bin/env node /** * Continuous Learning - Session Evaluator * * Cross-platform (Windows, macOS, Linux) * * Runs on Stop hook to extract reusable patterns from Claude Code sessions. * Reads transcript_path from stdin JSON (Claude Code hook input). * * Why Stop hook instead of UserPromptSubmit: * - Stop runs once at session end (lightweight) * - UserPromptSubmit runs every message (heavy, adds latency) */ const path = require('path'); const fs = require('fs'); const { getLearnedSkillsDir, ensureDir, readFile, countInFile, log } = require('../lib/utils'); // Read hook input from stdin (Claude Code provides transcript_path via stdin JSON) const MAX_STDIN = 1024 * 1024; let stdinData = ''; process.stdin.on('data', chunk => { if (stdinData.length < MAX_STDIN) { stdinData += chunk; } }); process.stdin.on('end', () => { main().catch(err => { console.error('[ContinuousLearning] Error:', err.message); process.exit(0); }); }); async function main() { // Parse stdin JSON to get transcript_path let transcriptPath = null; try { const input = JSON.parse(stdinData); transcriptPath = input.transcript_path; } catch { // Fallback: try env var for backwards compatibility transcriptPath = process.env.CLAUDE_TRANSCRIPT_PATH; } // Get script directory to find config const scriptDir = __dirname; const configFile = path.join(scriptDir, '..', '..', 'skills', 'continuous-learning', 'config.json'); // Default configuration let minSessionLength = 10; let learnedSkillsPath = getLearnedSkillsDir(); // Load config if exists const configContent = readFile(configFile); if (configContent) { try { const config = JSON.parse(configContent); minSessionLength = config.min_session_length || 10; if (config.learned_skills_path) { // Handle ~ in path learnedSkillsPath = config.learned_skills_path.replace(/^~/, require('os').homedir()); } } catch (err) { log(`[ContinuousLearning] Failed to parse config: ${err.message}, using defaults`); } } // Ensure learned skills directory exists ensureDir(learnedSkillsPath); if (!transcriptPath || !fs.existsSync(transcriptPath)) { process.exit(0); } // Count user messages in session const messageCount = countInFile(transcriptPath, /"type":"user"/g); // Skip short sessions if (messageCount < minSessionLength) { log(`[ContinuousLearning] Session too short (${messageCount} messages), skipping`); process.exit(0); } // Signal to Claude that session should be evaluated for extractable patterns log(`[ContinuousLearning] Session has ${messageCount} messages - evaluate for extractable patterns`); log(`[ContinuousLearning] Save learned skills to: ${learnedSkillsPath}`); process.exit(0); }