From c12b3295e994e1130ad2d20948cc12d859d63da4 Mon Sep 17 00:00:00 2001 From: matt Date: Thu, 12 Feb 2026 09:15:06 +0900 Subject: [PATCH] feat(01-01): refactor SubagentDetailBuilder to use FileSystemProvider - Remove dynamic fs/promises, os, and path imports from SubagentDetailBuilder - Add fsProvider and projectsDir parameters to buildSubagentDetail function - Replace fs.access() with fsProvider.exists() for file existence check - Replace os.homedir() path construction with projectsDir parameter - Update ChunkBuilder.buildSubagentDetail() to accept and pass new parameters - Update IPC subagents handler to obtain provider and projectsDir from ProjectScanner - Update initializeSubagentHandlers to accept ProjectScanner parameter - Update handlers.ts to pass ProjectScanner to subagent handler initialization Co-Authored-By: Claude Opus 4.6 --- src/main/ipc/handlers.ts | 4 ++-- src/main/ipc/subagents.ts | 21 +++++++++++++--- src/main/services/analysis/ChunkBuilder.ts | 9 +++++-- .../analysis/SubagentDetailBuilder.ts | 24 +++++++++++-------- 4 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/main/ipc/handlers.ts b/src/main/ipc/handlers.ts index 51ccb5b3..fd8b8033 100644 --- a/src/main/ipc/handlers.ts +++ b/src/main/ipc/handlers.ts @@ -72,7 +72,7 @@ export function initializeIpcHandlers( initializeProjectHandlers(scanner); initializeSessionHandlers(scanner, parser, resolver, builder, cache); initializeSearchHandlers(scanner); - initializeSubagentHandlers(builder, cache, parser, resolver); + initializeSubagentHandlers(builder, cache, parser, resolver, scanner); initializeUpdaterHandlers(updater); if (sshManager && sshModeSwitchCallback) { initializeSshHandlers(sshManager, sshModeSwitchCallback); @@ -110,7 +110,7 @@ export function reinitializeServiceHandlers( initializeProjectHandlers(scanner); initializeSessionHandlers(scanner, parser, resolver, builder, cache); initializeSearchHandlers(scanner); - initializeSubagentHandlers(builder, cache, parser, resolver); + initializeSubagentHandlers(builder, cache, parser, resolver, scanner); logger.info('Service handlers re-initialized after mode switch'); } diff --git a/src/main/ipc/subagents.ts b/src/main/ipc/subagents.ts index d010e3c6..916a0286 100644 --- a/src/main/ipc/subagents.ts +++ b/src/main/ipc/subagents.ts @@ -12,7 +12,13 @@ import { type SubagentDetail } from '../types'; import { validateProjectId, validateSessionId, validateSubagentId } from './guards'; -import type { ChunkBuilder, DataCache, SessionParser, SubagentResolver } from '../services'; +import type { + ChunkBuilder, + DataCache, + ProjectScanner, + SessionParser, + SubagentResolver, +} from '../services'; const logger = createLogger('IPC:subagents'); @@ -21,6 +27,7 @@ let chunkBuilder: ChunkBuilder; let dataCache: DataCache; let sessionParser: SessionParser; let subagentResolver: SubagentResolver; +let projectScanner: ProjectScanner; /** * Initializes subagent handlers with service instances. @@ -29,12 +36,14 @@ export function initializeSubagentHandlers( builder: ChunkBuilder, cache: DataCache, parser: SessionParser, - resolver: SubagentResolver + resolver: SubagentResolver, + scanner: ProjectScanner ): void { chunkBuilder = builder; dataCache = cache; sessionParser = parser; subagentResolver = resolver; + projectScanner = scanner; } /** @@ -97,13 +106,19 @@ async function handleGetSubagentDetail( return subagentDetail; } + // Get provider and projectsDir from projectScanner + const fsProvider = projectScanner.getFileSystemProvider(); + const projectsDir = projectScanner.getProjectsDir(); + // Build subagent detail const builtDetail = await chunkBuilder.buildSubagentDetail( safeProjectId, safeSessionId, safeSubagentId, sessionParser, - subagentResolver + subagentResolver, + fsProvider, + projectsDir ); if (!builtDetail) { diff --git a/src/main/services/analysis/ChunkBuilder.ts b/src/main/services/analysis/ChunkBuilder.ts index 072ea18d..528c3edd 100644 --- a/src/main/services/analysis/ChunkBuilder.ts +++ b/src/main/services/analysis/ChunkBuilder.ts @@ -55,6 +55,7 @@ import { buildGroups as buildConversationGroups } from './ConversationGroupBuild import { buildSubagentDetail as buildSubagentDetailFn } from './SubagentDetailBuilder'; import type { SubagentResolver } from '../discovery/SubagentResolver'; +import type { FileSystemProvider } from '../infrastructure/FileSystemProvider'; import type { SessionParser } from '../parsing/SessionParser'; export class ChunkBuilder { @@ -428,7 +429,9 @@ export class ChunkBuilder { sessionId: string, subagentId: string, sessionParser: SessionParser, - subagentResolver: SubagentResolver + subagentResolver: SubagentResolver, + fsProvider: FileSystemProvider, + projectsDir: string ): Promise { // Delegate to the extracted module, passing buildChunks as a callback return buildSubagentDetailFn( @@ -437,7 +440,9 @@ export class ChunkBuilder { subagentId, sessionParser, subagentResolver, - (messages, subagents) => this.buildChunks(messages, subagents) + (messages, subagents) => this.buildChunks(messages, subagents), + fsProvider, + projectsDir ); } } diff --git a/src/main/services/analysis/SubagentDetailBuilder.ts b/src/main/services/analysis/SubagentDetailBuilder.ts index 82fea77e..8fde9184 100644 --- a/src/main/services/analysis/SubagentDetailBuilder.ts +++ b/src/main/services/analysis/SubagentDetailBuilder.ts @@ -16,12 +16,14 @@ import { } from '@main/types'; import { countTokens } from '@main/utils/tokenizer'; import { createLogger } from '@shared/utils/logger'; +import * as path from 'path'; const logger = createLogger('Service:SubagentDetailBuilder'); import { buildSemanticStepGroups } from './SemanticStepGrouper'; import type { SubagentResolver } from '../discovery/SubagentResolver'; +import type { FileSystemProvider } from '../infrastructure/FileSystemProvider'; import type { SessionParser } from '../parsing/SessionParser'; /** @@ -34,6 +36,8 @@ import type { SessionParser } from '../parsing/SessionParser'; * @param sessionParser - SessionParser instance for parsing subagent file * @param subagentResolver - SubagentResolver instance for nested subagents * @param buildChunksFn - Function to build chunks from messages and subagents + * @param fsProvider - FileSystemProvider for file existence checks + * @param projectsDir - Projects directory path * @returns SubagentDetail or null if not found */ export async function buildSubagentDetail( @@ -42,21 +46,21 @@ export async function buildSubagentDetail( subagentId: string, sessionParser: SessionParser, subagentResolver: SubagentResolver, - buildChunksFn: (messages: ParsedMessage[], subagents: Process[]) => EnhancedChunk[] + buildChunksFn: (messages: ParsedMessage[], subagents: Process[]) => EnhancedChunk[], + fsProvider: FileSystemProvider, + projectsDir: string ): Promise { try { - const fs = await import('fs/promises'); - const path = await import('path'); - const os = await import('os'); - // Construct path to subagent JSONL file - const claudeDir = path.join(os.homedir(), '.claude', 'projects'); - const subagentPath = path.join(claudeDir, projectId, 'subagents', `agent-${subagentId}.jsonl`); + const subagentPath = path.join( + projectsDir, + projectId, + 'subagents', + `agent-${subagentId}.jsonl` + ); // Check if file exists - try { - await fs.access(subagentPath); - } catch { + if (!(await fsProvider.exists(subagentPath))) { logger.warn(`Subagent file not found: ${subagentPath}`); return null; }