52d2f72a82
* Enhance error logging in hooks
- Added detailed error logging in context-hook, new-hook, save-hook, and summary-hook to capture status, project, port, and relevant session information on failures.
- Improved error messages thrown in save-hook and summary-hook to include specific context about the failure.
* Refactor migration logging to use console.log instead of console.error
- Updated SessionSearch and SessionStore classes to replace console.error with console.log for migration-related messages.
- Added notes in the documentation to clarify the use of console.log for migration messages due to the unavailability of the structured logger during constructor execution.
* Refactor SDKAgent and silent-debug utility to simplify error handling
- Updated SDKAgent to use direct defaults instead of happy_path_error__with_fallback for non-critical fields such as last_user_message, last_assistant_message, title, filesRead, filesModified, concepts, and summary.request.
- Enhanced silent-debug documentation to clarify appropriate use cases for happy_path_error__with_fallback, emphasizing its role in handling unexpected null/undefined values while discouraging its use for nullable fields with valid defaults.
* fix: correct happy_path_error__with_fallback usage to prevent false errors
Fixes false "Missing cwd" and "Missing transcript_path" errors that were
flooding silent.log even when values were present.
Root cause: happy_path_error__with_fallback was being called unconditionally
instead of only when the value was actually missing.
Pattern changed from:
value: happy_path_error__with_fallback('Missing', {}, value || '')
To correct usage:
value: value || happy_path_error__with_fallback('Missing', {}, '')
Fixed in:
- src/hooks/save-hook.ts (PostToolUse hook)
- src/hooks/summary-hook.ts (Stop hook)
- src/services/worker/http/routes/SessionRoutes.ts (2 instances)
Impact: Eliminates false error noise, making actual errors visible.
Addresses issue #260 - users were seeing "Missing cwd" errors despite
Claude Code correctly passing all required fields.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* Enhance error logging and handling across services
- Improved error messages in SessionStore to include project context when fetching boundary observations and timestamps.
- Updated ChromaSync error handling to provide more informative messages regarding client initialization failures, including the project context.
- Enhanced error logging in WorkerService to include the package path when reading version fails.
- Added detailed error logging in worker-utils to capture expected and running versions during health checks.
- Extended WorkerErrorMessageOptions to include actualError for more informative restart instructions.
* Refactor error handling in hooks to use standardized fetch error handler
- Introduced a new error handler `handleFetchError` in `shared/error-handler.ts` to standardize logging and user-facing error messages for fetch failures across hooks.
- Updated `context-hook.ts`, `new-hook.ts`, `save-hook.ts`, and `summary-hook.ts` to utilize the new error handler, improving consistency and maintainability.
- Removed redundant imports and error handling logic related to worker restart instructions from the hooks.
* feat: add comprehensive error handling tests for hooks and ChromaSync client
---------
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
111 lines
3.5 KiB
TypeScript
111 lines
3.5 KiB
TypeScript
/**
|
|
* Summary Hook - Stop
|
|
*
|
|
* Pure HTTP client - sends data to worker, worker handles all database operations
|
|
* including privacy checks. This allows the hook to run under any runtime
|
|
* (Node.js or Bun) since it has no native module dependencies.
|
|
*
|
|
* Transcript parsing stays in the hook because only the hook has access to
|
|
* the transcript file path.
|
|
*/
|
|
|
|
import { stdin } from 'process';
|
|
import { createHookResponse } from './hook-response.js';
|
|
import { logger } from '../utils/logger.js';
|
|
import { ensureWorkerRunning, getWorkerPort } from '../shared/worker-utils.js';
|
|
import { HOOK_TIMEOUTS } from '../shared/hook-constants.js';
|
|
import { happy_path_error__with_fallback } from '../utils/silent-debug.js';
|
|
import { handleWorkerError } from '../shared/hook-error-handler.js';
|
|
import { handleFetchError } from './shared/error-handler.js';
|
|
import { extractLastMessage } from '../shared/transcript-parser.js';
|
|
|
|
export interface StopInput {
|
|
session_id: string;
|
|
cwd: string;
|
|
transcript_path: string;
|
|
}
|
|
|
|
/**
|
|
* Summary Hook Main Logic - Fire-and-forget HTTP client
|
|
*/
|
|
async function summaryHook(input?: StopInput): Promise<void> {
|
|
// Ensure worker is running before any other logic
|
|
await ensureWorkerRunning();
|
|
|
|
if (!input) {
|
|
throw new Error('summaryHook requires input');
|
|
}
|
|
|
|
const { session_id } = input;
|
|
|
|
const port = getWorkerPort();
|
|
|
|
// Extract last user AND assistant messages from transcript
|
|
const transcriptPath = input.transcript_path || happy_path_error__with_fallback(
|
|
'Missing transcript_path in Stop hook input',
|
|
{ session_id },
|
|
''
|
|
);
|
|
const lastUserMessage = extractLastMessage(transcriptPath, 'user');
|
|
const lastAssistantMessage = extractLastMessage(transcriptPath, 'assistant', true);
|
|
|
|
logger.dataIn('HOOK', 'Stop: Requesting summary', {
|
|
workerPort: port,
|
|
hasLastUserMessage: !!lastUserMessage,
|
|
hasLastAssistantMessage: !!lastAssistantMessage
|
|
});
|
|
|
|
try {
|
|
// Send to worker - worker handles privacy check and database operations
|
|
const response = await fetch(`http://127.0.0.1:${port}/api/sessions/summarize`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
claudeSessionId: session_id,
|
|
last_user_message: lastUserMessage,
|
|
last_assistant_message: lastAssistantMessage
|
|
}),
|
|
signal: AbortSignal.timeout(HOOK_TIMEOUTS.DEFAULT)
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
handleFetchError(response, errorText, {
|
|
hookName: 'summary',
|
|
operation: 'Summary generation',
|
|
sessionId: session_id,
|
|
port
|
|
});
|
|
}
|
|
|
|
logger.debug('HOOK', 'Summary request sent successfully');
|
|
} catch (error: any) {
|
|
handleWorkerError(error);
|
|
} finally {
|
|
// Stop processing spinner
|
|
try {
|
|
const spinnerResponse = await fetch(`http://127.0.0.1:${port}/api/processing`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ isProcessing: false }),
|
|
signal: AbortSignal.timeout(2000)
|
|
});
|
|
if (!spinnerResponse.ok) {
|
|
logger.warn('HOOK', 'Failed to stop spinner', { status: spinnerResponse.status });
|
|
}
|
|
} catch (error: any) {
|
|
logger.warn('HOOK', 'Could not stop spinner', { error: error.message });
|
|
}
|
|
}
|
|
|
|
console.log(createHookResponse('Stop', true));
|
|
}
|
|
|
|
// Entry Point
|
|
let input = '';
|
|
stdin.on('data', (chunk) => input += chunk);
|
|
stdin.on('end', async () => {
|
|
const parsed = input ? JSON.parse(input) : undefined;
|
|
await summaryHook(parsed);
|
|
});
|