diff --git a/.cursor/skills/continuous-learning/evaluate-session.sh b/.cursor/skills/continuous-learning/evaluate-session.sh index b6c008f..a5946fc 100755 --- a/.cursor/skills/continuous-learning/evaluate-session.sh +++ b/.cursor/skills/continuous-learning/evaluate-session.sh @@ -43,8 +43,13 @@ fi # Ensure learned skills directory exists mkdir -p "$LEARNED_SKILLS_PATH" -# Get transcript path from environment (set by Claude Code) -transcript_path="${CLAUDE_TRANSCRIPT_PATH:-}" +# Get transcript path from stdin JSON (Claude Code hook input) +# Falls back to env var for backwards compatibility +stdin_data=$(cat) +transcript_path=$(echo "$stdin_data" | grep -o '"transcript_path":"[^"]*"' | head -1 | cut -d'"' -f4) +if [ -z "$transcript_path" ]; then + transcript_path="${CLAUDE_TRANSCRIPT_PATH:-}" +fi if [ -z "$transcript_path" ] || [ ! -f "$transcript_path" ]; then exit 0 diff --git a/.cursor/skills/strategic-compact/suggest-compact.js b/.cursor/skills/strategic-compact/suggest-compact.js index d17068d..71f5da5 100644 --- a/.cursor/skills/strategic-compact/suggest-compact.js +++ b/.cursor/skills/strategic-compact/suggest-compact.js @@ -28,7 +28,9 @@ async function main() { const sessionId = process.env.CLAUDE_SESSION_ID || String(process.ppid) || 'default'; const counterFile = path.join(getTempDir(), `claude-tool-count-${sessionId}`); const rawThreshold = parseInt(process.env.COMPACT_THRESHOLD || '50', 10); - const threshold = Number.isFinite(rawThreshold) ? rawThreshold : 50; + const threshold = Number.isFinite(rawThreshold) && rawThreshold > 0 && rawThreshold <= 10000 + ? rawThreshold + : 50; let count = 1; diff --git a/scripts/hooks/evaluate-session.js b/scripts/hooks/evaluate-session.js index 38a65bb..0dcfd37 100644 --- a/scripts/hooks/evaluate-session.js +++ b/scripts/hooks/evaluate-session.js @@ -4,7 +4,8 @@ * * Cross-platform (Windows, macOS, Linux) * - * Runs on Stop hook to extract reusable patterns from Claude Code sessions + * 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) @@ -21,7 +22,34 @@ const { 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'); @@ -49,9 +77,6 @@ async function main() { // Ensure learned skills directory exists ensureDir(learnedSkillsPath); - // Get transcript path from environment (set by Claude Code) - const transcriptPath = process.env.CLAUDE_TRANSCRIPT_PATH; - if (!transcriptPath || !fs.existsSync(transcriptPath)) { process.exit(0); } @@ -71,8 +96,3 @@ async function main() { process.exit(0); } - -main().catch(err => { - console.error('[ContinuousLearning] Error:', err.message); - process.exit(0); -}); diff --git a/scripts/hooks/session-end.js b/scripts/hooks/session-end.js index cc4abd1..632c5f0 100644 --- a/scripts/hooks/session-end.js +++ b/scripts/hooks/session-end.js @@ -5,7 +5,7 @@ * Cross-platform (Windows, macOS, Linux) * * Runs when Claude session ends. Extracts a meaningful summary from - * the session transcript (via CLAUDE_TRANSCRIPT_PATH) and saves it + * the session transcript (via stdin JSON transcript_path) and saves it * to a session file for cross-session continuity. */ @@ -85,7 +85,38 @@ function extractSessionSummary(transcriptPath) { }; } +// 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', () => { + runMain(); +}); + +function runMain() { + main().catch(err => { + console.error('[SessionEnd] 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; + } + const sessionsDir = getSessionsDir(); const today = getDateString(); const shortId = getSessionIdShort(); @@ -96,7 +127,6 @@ async function main() { const currentTime = getTimeString(); // Try to extract summary from transcript - const transcriptPath = process.env.CLAUDE_TRANSCRIPT_PATH; let summary = null; if (transcriptPath) { @@ -183,7 +213,3 @@ function buildSummarySection(summary) { return section; } -main().catch(err => { - console.error('[SessionEnd] Error:', err.message); - process.exit(0); -}); diff --git a/scripts/hooks/suggest-compact.js b/scripts/hooks/suggest-compact.js index d17068d..71f5da5 100644 --- a/scripts/hooks/suggest-compact.js +++ b/scripts/hooks/suggest-compact.js @@ -28,7 +28,9 @@ async function main() { const sessionId = process.env.CLAUDE_SESSION_ID || String(process.ppid) || 'default'; const counterFile = path.join(getTempDir(), `claude-tool-count-${sessionId}`); const rawThreshold = parseInt(process.env.COMPACT_THRESHOLD || '50', 10); - const threshold = Number.isFinite(rawThreshold) ? rawThreshold : 50; + const threshold = Number.isFinite(rawThreshold) && rawThreshold > 0 && rawThreshold <= 10000 + ? rawThreshold + : 50; let count = 1; diff --git a/skills/continuous-learning/evaluate-session.sh b/skills/continuous-learning/evaluate-session.sh index b6c008f..a5946fc 100755 --- a/skills/continuous-learning/evaluate-session.sh +++ b/skills/continuous-learning/evaluate-session.sh @@ -43,8 +43,13 @@ fi # Ensure learned skills directory exists mkdir -p "$LEARNED_SKILLS_PATH" -# Get transcript path from environment (set by Claude Code) -transcript_path="${CLAUDE_TRANSCRIPT_PATH:-}" +# Get transcript path from stdin JSON (Claude Code hook input) +# Falls back to env var for backwards compatibility +stdin_data=$(cat) +transcript_path=$(echo "$stdin_data" | grep -o '"transcript_path":"[^"]*"' | head -1 | cut -d'"' -f4) +if [ -z "$transcript_path" ]; then + transcript_path="${CLAUDE_TRANSCRIPT_PATH:-}" +fi if [ -z "$transcript_path" ] || [ ! -f "$transcript_path" ]; then exit 0 diff --git a/tests/hooks/hooks.test.js b/tests/hooks/hooks.test.js index 8994d40..7da7eb4 100644 --- a/tests/hooks/hooks.test.js +++ b/tests/hooks/hooks.test.js @@ -232,9 +232,8 @@ async function runTests() { const transcript = Array(5).fill('{"type":"user","content":"test"}\n').join(''); fs.writeFileSync(transcriptPath, transcript); - const result = await runScript(path.join(scriptsDir, 'evaluate-session.js'), '', { - CLAUDE_TRANSCRIPT_PATH: transcriptPath - }); + const stdinJson = JSON.stringify({ transcript_path: transcriptPath }); + const result = await runScript(path.join(scriptsDir, 'evaluate-session.js'), stdinJson); assert.ok( result.stderr.includes('Session too short'), @@ -252,9 +251,8 @@ async function runTests() { const transcript = Array(15).fill('{"type":"user","content":"test"}\n').join(''); fs.writeFileSync(transcriptPath, transcript); - const result = await runScript(path.join(scriptsDir, 'evaluate-session.js'), '', { - CLAUDE_TRANSCRIPT_PATH: transcriptPath - }); + const stdinJson = JSON.stringify({ transcript_path: transcriptPath }); + const result = await runScript(path.join(scriptsDir, 'evaluate-session.js'), stdinJson); assert.ok( result.stderr.includes('15 messages'), diff --git a/tests/run-all.js b/tests/run-all.js index 5ffcf9e..2abb299 100644 --- a/tests/run-all.js +++ b/tests/run-all.js @@ -13,7 +13,8 @@ const testsDir = __dirname; const testFiles = [ 'lib/utils.test.js', 'lib/package-manager.test.js', - 'hooks/hooks.test.js' + 'hooks/hooks.test.js', + 'integration/hooks.test.js' ]; console.log('╔══════════════════════════════════════════════════════════╗');