mirror of
https://fastgit.cc/github.com/Yeachan-Heo/oh-my-claudecode
synced 2026-04-23 10:20:59 +08:00
The installed SessionStart hook runs in its own Node process, so the existing project-memory registration path never reached the startup additionalContext consumed by Claude Code. Load and, when needed, refresh persisted project memory directly from the plugin runtime inside the session-start script, then append the formatted summary without relying on process-local collector state. Keep the legacy helper aligned with plugin-root-aware imports. Constraint: The installed SessionStart runtime is `scripts/session-start.mjs`, not the in-process bridge path Constraint: Session-start hooks must remain failure-tolerant and continue on missing dist/runtime artifacts Rejected: Refactor all session-start assembly into shared bridge/script runtime | broader change than needed for the bug Rejected: Preserve collector-only registration across processes | process isolation makes that design ineffective here Confidence: high Scope-risk: narrow Directive: Do not rely on in-memory contextCollector state for subprocess hook injection paths without a persisted or returned handoff Tested: node --check scripts/session-start.mjs scripts/project-memory-session.mjs Tested: Manual A/B subprocess reproduction against pre-fix session-start script vs patched script with persisted project-memory.json Tested: Added focused regression test in src/__tests__/session-start-script-context.test.ts Not-tested: Full Vitest run in this worktree (local shared node_modules does not expose a runnable repo-local vitest binary) Related: issue #1779
81 lines
2.8 KiB
JavaScript
Executable File
81 lines
2.8 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
/**
|
|
* SessionStart Hook: Project Memory Detection
|
|
* Auto-detects project environment and injects context
|
|
*/
|
|
|
|
import { dirname, join } from 'path';
|
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
|
|
function getRuntimeBaseDir() {
|
|
return process.env.CLAUDE_PLUGIN_ROOT || join(__dirname, '..');
|
|
}
|
|
|
|
// Import timeout-protected stdin reader (prevents hangs on Linux/Windows, see issue #240, #524)
|
|
let readStdin;
|
|
try {
|
|
const mod = await import(pathToFileURL(join(__dirname, 'lib', 'stdin.mjs')).href);
|
|
readStdin = mod.readStdin;
|
|
} catch {
|
|
// Fallback: inline timeout-protected readStdin if lib module is missing
|
|
readStdin = (timeoutMs = 5000) => new Promise((resolve) => {
|
|
const chunks = [];
|
|
let settled = false;
|
|
const timeout = setTimeout(() => {
|
|
if (!settled) { settled = true; process.stdin.removeAllListeners(); process.stdin.destroy(); resolve(Buffer.concat(chunks).toString('utf-8')); }
|
|
}, timeoutMs);
|
|
process.stdin.on('data', (chunk) => { chunks.push(chunk); });
|
|
process.stdin.on('end', () => { if (!settled) { settled = true; clearTimeout(timeout); resolve(Buffer.concat(chunks).toString('utf-8')); } });
|
|
process.stdin.on('error', () => { if (!settled) { settled = true; clearTimeout(timeout); resolve(''); } });
|
|
if (process.stdin.readableEnded) { if (!settled) { settled = true; clearTimeout(timeout); resolve(Buffer.concat(chunks).toString('utf-8')); } }
|
|
});
|
|
}
|
|
|
|
// Dynamic import of project memory module (prevents crash if dist is missing, see issue #362)
|
|
let registerProjectMemoryContext;
|
|
try {
|
|
const mod = await import(pathToFileURL(join(getRuntimeBaseDir(), 'dist', 'hooks', 'project-memory', 'index.js')).href);
|
|
registerProjectMemoryContext = mod.registerProjectMemoryContext;
|
|
} catch {
|
|
// dist not built or missing - skip project memory detection silently
|
|
registerProjectMemoryContext = null;
|
|
}
|
|
|
|
/**
|
|
* Main hook execution
|
|
*/
|
|
async function main() {
|
|
try {
|
|
const input = await readStdin();
|
|
let data = {};
|
|
try { data = JSON.parse(input); } catch {}
|
|
|
|
// Extract directory and session ID
|
|
const directory = data.cwd || data.directory || process.cwd();
|
|
const sessionId = data.session_id || data.sessionId || '';
|
|
|
|
// Register project memory context (skip if module unavailable)
|
|
if (registerProjectMemoryContext) {
|
|
await registerProjectMemoryContext(sessionId, directory);
|
|
}
|
|
|
|
// Return success (context registered via contextCollector, not returned here)
|
|
console.log(JSON.stringify({
|
|
continue: true,
|
|
suppressOutput: true
|
|
}));
|
|
} catch (error) {
|
|
// Always continue on error
|
|
console.log(JSON.stringify({
|
|
continue: true,
|
|
suppressOutput: true
|
|
}));
|
|
}
|
|
}
|
|
|
|
main();
|