2b60dd2932
Persist platform_source across session creation, transcript ingestion, API query paths, and viewer state so Claude and Codex data can coexist without bleeding into each other. - add platform-source normalization helpers and persist platform_source in sdk_sessions via migration 24 with backfill and indexing - thread platformSource through CLI hooks, transcript processing, context generation, pagination, search routes, SSE payloads, and session management - expose source-aware project catalogs, viewer tabs, context preview selectors, and source badges for observations, prompts, and summaries - start the transcript watcher from the worker for transcript-based clients and preserve platform source during Codex ingestion - auto-start the worker from the MCP server for MCP-only clients and tighten stdio-driven cleanup during shutdown - keep createSDKSession backward compatible with existing custom-title callers while allowing explicit platform source forwarding
71 lines
2.8 KiB
TypeScript
71 lines
2.8 KiB
TypeScript
/**
|
|
* File Edit Handler - Cursor-specific afterFileEdit
|
|
*
|
|
* Handles file edit observations from Cursor IDE.
|
|
* Similar to observation handler but with file-specific metadata.
|
|
*/
|
|
|
|
import type { EventHandler, NormalizedHookInput, HookResult } from '../types.js';
|
|
import { ensureWorkerRunning, workerHttpRequest } from '../../shared/worker-utils.js';
|
|
import { logger } from '../../utils/logger.js';
|
|
import { HOOK_EXIT_CODES } from '../../shared/hook-constants.js';
|
|
import { normalizePlatformSource } from '../../shared/platform-source.js';
|
|
|
|
export const fileEditHandler: EventHandler = {
|
|
async execute(input: NormalizedHookInput): Promise<HookResult> {
|
|
// Ensure worker is running before any other logic
|
|
const workerReady = await ensureWorkerRunning();
|
|
if (!workerReady) {
|
|
// Worker not available - skip file edit observation gracefully
|
|
return { continue: true, suppressOutput: true, exitCode: HOOK_EXIT_CODES.SUCCESS };
|
|
}
|
|
|
|
const { sessionId, cwd, filePath, edits } = input;
|
|
const platformSource = normalizePlatformSource(input.platform);
|
|
|
|
if (!filePath) {
|
|
throw new Error('fileEditHandler requires filePath');
|
|
}
|
|
|
|
logger.dataIn('HOOK', `FileEdit: ${filePath}`, {
|
|
editCount: edits?.length ?? 0
|
|
});
|
|
|
|
// Validate required fields before sending to worker
|
|
if (!cwd) {
|
|
throw new Error(`Missing cwd in FileEdit hook input for session ${sessionId}, file ${filePath}`);
|
|
}
|
|
|
|
// Send to worker as an observation with file edit metadata
|
|
// The observation handler on the worker will process this appropriately
|
|
try {
|
|
const response = await workerHttpRequest('/api/sessions/observations', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
contentSessionId: sessionId,
|
|
platformSource,
|
|
tool_name: 'write_file',
|
|
tool_input: { filePath, edits },
|
|
tool_response: { success: true },
|
|
cwd
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
// Log but don't throw — file edit observation failure should not block editing
|
|
logger.warn('HOOK', 'File edit observation storage failed, skipping', { status: response.status, filePath });
|
|
return { continue: true, suppressOutput: true, exitCode: HOOK_EXIT_CODES.SUCCESS };
|
|
}
|
|
|
|
logger.debug('HOOK', 'File edit observation sent successfully', { filePath });
|
|
} catch (error) {
|
|
// Worker unreachable — skip file edit observation gracefully
|
|
logger.warn('HOOK', 'File edit observation fetch error, skipping', { error: error instanceof Error ? error.message : String(error) });
|
|
return { continue: true, suppressOutput: true, exitCode: HOOK_EXIT_CODES.SUCCESS };
|
|
}
|
|
|
|
return { continue: true, suppressOutput: true };
|
|
}
|
|
};
|