mirror of
https://github.com/thedotmack/claude-mem
synced 2026-04-25 17:15:04 +02:00
# Conflicts: # plugin/scripts/context-generator.cjs # plugin/scripts/mcp-server.cjs # plugin/scripts/worker-service.cjs # plugin/ui/viewer-bundle.js # src/cli/handlers/context.ts # src/services/sqlite/SessionStore.ts # src/services/sqlite/migrations/runner.ts # src/services/worker-service.ts # src/shared/SettingsDefaultsManager.ts
99 lines
4.2 KiB
TypeScript
99 lines
4.2 KiB
TypeScript
/**
|
|
* Context Handler - SessionStart
|
|
*
|
|
* Extracted from context-hook.ts - calls worker to generate context.
|
|
* Returns context as hookSpecificOutput for Claude Code to inject.
|
|
*/
|
|
|
|
import type { EventHandler, NormalizedHookInput, HookResult } from '../types.js';
|
|
import { ensureWorkerRunning, getWorkerPort, workerHttpRequest } from '../../shared/worker-utils.js';
|
|
import { getProjectContext } from '../../utils/project-name.js';
|
|
import { HOOK_EXIT_CODES } from '../../shared/hook-constants.js';
|
|
import { logger } from '../../utils/logger.js';
|
|
import { SettingsDefaultsManager } from '../../shared/SettingsDefaultsManager.js';
|
|
import { USER_SETTINGS_PATH } from '../../shared/paths.js';
|
|
import { normalizePlatformSource } from '../../shared/platform-source.js';
|
|
|
|
export const contextHandler: EventHandler = {
|
|
async execute(input: NormalizedHookInput): Promise<HookResult> {
|
|
// Ensure worker is running before any other logic
|
|
const workerReady = await ensureWorkerRunning();
|
|
if (!workerReady) {
|
|
// Worker not available - return empty context gracefully
|
|
return {
|
|
hookSpecificOutput: {
|
|
hookEventName: 'SessionStart',
|
|
additionalContext: ''
|
|
},
|
|
exitCode: HOOK_EXIT_CODES.SUCCESS
|
|
};
|
|
}
|
|
|
|
const cwd = input.cwd ?? process.cwd();
|
|
const context = getProjectContext(cwd);
|
|
const port = getWorkerPort();
|
|
const platformSource = normalizePlatformSource(input.platform);
|
|
|
|
// Check if terminal output should be shown (load settings early)
|
|
const settings = SettingsDefaultsManager.loadFromFile(USER_SETTINGS_PATH);
|
|
const showTerminalOutput = settings.CLAUDE_MEM_CONTEXT_SHOW_TERMINAL_OUTPUT === 'true';
|
|
|
|
// Pass all projects (parent + worktree if applicable) for unified timeline
|
|
const projectsParam = context.allProjects.join(',');
|
|
const apiPath = `/api/context/inject?projects=${encodeURIComponent(projectsParam)}&platformSource=${encodeURIComponent(platformSource)}`;
|
|
const colorApiPath = input.platform === 'claude-code' ? `${apiPath}&colors=true` : apiPath;
|
|
|
|
// Note: Removed AbortSignal.timeout due to Windows Bun cleanup issue (libuv assertion)
|
|
// Worker service has its own timeouts, so client-side timeout is redundant
|
|
try {
|
|
// Fetch markdown (for Claude context) and optionally colored (for user display)
|
|
const [response, colorResponse] = await Promise.all([
|
|
workerHttpRequest(apiPath),
|
|
showTerminalOutput ? workerHttpRequest(colorApiPath).catch(() => null) : Promise.resolve(null)
|
|
]);
|
|
|
|
if (!response.ok) {
|
|
// Log but don't throw — context fetch failure should not block session start
|
|
logger.warn('HOOK', 'Context generation failed, returning empty', { status: response.status });
|
|
return {
|
|
hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: '' },
|
|
exitCode: HOOK_EXIT_CODES.SUCCESS
|
|
};
|
|
}
|
|
|
|
const [contextResult, colorResult] = await Promise.all([
|
|
response.text(),
|
|
colorResponse?.ok ? colorResponse.text() : Promise.resolve('')
|
|
]);
|
|
|
|
const additionalContext = contextResult.trim();
|
|
const coloredTimeline = colorResult.trim();
|
|
const platform = input.platform;
|
|
|
|
// Use colored timeline for display if available, otherwise fall back to
|
|
// plain markdown context (especially useful for platforms like Gemini
|
|
// where we want to ensure visibility even if colors aren't fetched).
|
|
const displayContent = coloredTimeline || (platform === 'gemini-cli' || platform === 'gemini' ? additionalContext : '');
|
|
|
|
const systemMessage = showTerminalOutput && displayContent
|
|
? `${displayContent}\n\nView Observations Live @ http://localhost:${port}`
|
|
: undefined;
|
|
|
|
return {
|
|
hookSpecificOutput: {
|
|
hookEventName: 'SessionStart',
|
|
additionalContext
|
|
},
|
|
systemMessage
|
|
};
|
|
} catch (error) {
|
|
// Worker unreachable — return empty context gracefully
|
|
logger.warn('HOOK', 'Context fetch error, returning empty', { error: error instanceof Error ? error.message : String(error) });
|
|
return {
|
|
hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: '' },
|
|
exitCode: HOOK_EXIT_CODES.SUCCESS
|
|
};
|
|
}
|
|
}
|
|
};
|