mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-02-15 19:03:22 +08:00
- Migrate session-end.js and evaluate-session.js from CLAUDE_TRANSCRIPT_PATH env var to stdin JSON transcript_path (correct hook input mechanism) - Remove duplicate main() calls that ran before stdin was read, causing session files to be created with empty data - Add range validation (1-10000) on COMPACT_THRESHOLD in suggest-compact.js to prevent negative or absurdly large thresholds - Add integration/hooks.test.js to tests/run-all.js so CI runs all 97 tests - Update evaluate-session.sh to parse transcript_path from stdin JSON - Update hooks.test.js to pass transcript_path via stdin instead of env var - Sync .cursor/ copies
99 lines
2.8 KiB
JavaScript
99 lines
2.8 KiB
JavaScript
#!/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);
|
|
}
|