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 <noreply@anthropic.com>
This commit is contained in:
matt 2026-02-12 09:15:06 +09:00
parent a3f5dafdab
commit c12b3295e9
4 changed files with 41 additions and 17 deletions

View file

@ -72,7 +72,7 @@ export function initializeIpcHandlers(
initializeProjectHandlers(scanner); initializeProjectHandlers(scanner);
initializeSessionHandlers(scanner, parser, resolver, builder, cache); initializeSessionHandlers(scanner, parser, resolver, builder, cache);
initializeSearchHandlers(scanner); initializeSearchHandlers(scanner);
initializeSubagentHandlers(builder, cache, parser, resolver); initializeSubagentHandlers(builder, cache, parser, resolver, scanner);
initializeUpdaterHandlers(updater); initializeUpdaterHandlers(updater);
if (sshManager && sshModeSwitchCallback) { if (sshManager && sshModeSwitchCallback) {
initializeSshHandlers(sshManager, sshModeSwitchCallback); initializeSshHandlers(sshManager, sshModeSwitchCallback);
@ -110,7 +110,7 @@ export function reinitializeServiceHandlers(
initializeProjectHandlers(scanner); initializeProjectHandlers(scanner);
initializeSessionHandlers(scanner, parser, resolver, builder, cache); initializeSessionHandlers(scanner, parser, resolver, builder, cache);
initializeSearchHandlers(scanner); initializeSearchHandlers(scanner);
initializeSubagentHandlers(builder, cache, parser, resolver); initializeSubagentHandlers(builder, cache, parser, resolver, scanner);
logger.info('Service handlers re-initialized after mode switch'); logger.info('Service handlers re-initialized after mode switch');
} }

View file

@ -12,7 +12,13 @@ import { type SubagentDetail } from '../types';
import { validateProjectId, validateSessionId, validateSubagentId } from './guards'; 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'); const logger = createLogger('IPC:subagents');
@ -21,6 +27,7 @@ let chunkBuilder: ChunkBuilder;
let dataCache: DataCache; let dataCache: DataCache;
let sessionParser: SessionParser; let sessionParser: SessionParser;
let subagentResolver: SubagentResolver; let subagentResolver: SubagentResolver;
let projectScanner: ProjectScanner;
/** /**
* Initializes subagent handlers with service instances. * Initializes subagent handlers with service instances.
@ -29,12 +36,14 @@ export function initializeSubagentHandlers(
builder: ChunkBuilder, builder: ChunkBuilder,
cache: DataCache, cache: DataCache,
parser: SessionParser, parser: SessionParser,
resolver: SubagentResolver resolver: SubagentResolver,
scanner: ProjectScanner
): void { ): void {
chunkBuilder = builder; chunkBuilder = builder;
dataCache = cache; dataCache = cache;
sessionParser = parser; sessionParser = parser;
subagentResolver = resolver; subagentResolver = resolver;
projectScanner = scanner;
} }
/** /**
@ -97,13 +106,19 @@ async function handleGetSubagentDetail(
return subagentDetail; return subagentDetail;
} }
// Get provider and projectsDir from projectScanner
const fsProvider = projectScanner.getFileSystemProvider();
const projectsDir = projectScanner.getProjectsDir();
// Build subagent detail // Build subagent detail
const builtDetail = await chunkBuilder.buildSubagentDetail( const builtDetail = await chunkBuilder.buildSubagentDetail(
safeProjectId, safeProjectId,
safeSessionId, safeSessionId,
safeSubagentId, safeSubagentId,
sessionParser, sessionParser,
subagentResolver subagentResolver,
fsProvider,
projectsDir
); );
if (!builtDetail) { if (!builtDetail) {

View file

@ -55,6 +55,7 @@ import { buildGroups as buildConversationGroups } from './ConversationGroupBuild
import { buildSubagentDetail as buildSubagentDetailFn } from './SubagentDetailBuilder'; import { buildSubagentDetail as buildSubagentDetailFn } from './SubagentDetailBuilder';
import type { SubagentResolver } from '../discovery/SubagentResolver'; import type { SubagentResolver } from '../discovery/SubagentResolver';
import type { FileSystemProvider } from '../infrastructure/FileSystemProvider';
import type { SessionParser } from '../parsing/SessionParser'; import type { SessionParser } from '../parsing/SessionParser';
export class ChunkBuilder { export class ChunkBuilder {
@ -428,7 +429,9 @@ export class ChunkBuilder {
sessionId: string, sessionId: string,
subagentId: string, subagentId: string,
sessionParser: SessionParser, sessionParser: SessionParser,
subagentResolver: SubagentResolver subagentResolver: SubagentResolver,
fsProvider: FileSystemProvider,
projectsDir: string
): Promise<SubagentDetail | null> { ): Promise<SubagentDetail | null> {
// Delegate to the extracted module, passing buildChunks as a callback // Delegate to the extracted module, passing buildChunks as a callback
return buildSubagentDetailFn( return buildSubagentDetailFn(
@ -437,7 +440,9 @@ export class ChunkBuilder {
subagentId, subagentId,
sessionParser, sessionParser,
subagentResolver, subagentResolver,
(messages, subagents) => this.buildChunks(messages, subagents) (messages, subagents) => this.buildChunks(messages, subagents),
fsProvider,
projectsDir
); );
} }
} }

View file

@ -16,12 +16,14 @@ import {
} from '@main/types'; } from '@main/types';
import { countTokens } from '@main/utils/tokenizer'; import { countTokens } from '@main/utils/tokenizer';
import { createLogger } from '@shared/utils/logger'; import { createLogger } from '@shared/utils/logger';
import * as path from 'path';
const logger = createLogger('Service:SubagentDetailBuilder'); const logger = createLogger('Service:SubagentDetailBuilder');
import { buildSemanticStepGroups } from './SemanticStepGrouper'; import { buildSemanticStepGroups } from './SemanticStepGrouper';
import type { SubagentResolver } from '../discovery/SubagentResolver'; import type { SubagentResolver } from '../discovery/SubagentResolver';
import type { FileSystemProvider } from '../infrastructure/FileSystemProvider';
import type { SessionParser } from '../parsing/SessionParser'; 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 sessionParser - SessionParser instance for parsing subagent file
* @param subagentResolver - SubagentResolver instance for nested subagents * @param subagentResolver - SubagentResolver instance for nested subagents
* @param buildChunksFn - Function to build chunks from messages and 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 * @returns SubagentDetail or null if not found
*/ */
export async function buildSubagentDetail( export async function buildSubagentDetail(
@ -42,21 +46,21 @@ export async function buildSubagentDetail(
subagentId: string, subagentId: string,
sessionParser: SessionParser, sessionParser: SessionParser,
subagentResolver: SubagentResolver, subagentResolver: SubagentResolver,
buildChunksFn: (messages: ParsedMessage[], subagents: Process[]) => EnhancedChunk[] buildChunksFn: (messages: ParsedMessage[], subagents: Process[]) => EnhancedChunk[],
fsProvider: FileSystemProvider,
projectsDir: string
): Promise<SubagentDetail | null> { ): Promise<SubagentDetail | null> {
try { try {
const fs = await import('fs/promises');
const path = await import('path');
const os = await import('os');
// Construct path to subagent JSONL file // Construct path to subagent JSONL file
const claudeDir = path.join(os.homedir(), '.claude', 'projects'); const subagentPath = path.join(
const subagentPath = path.join(claudeDir, projectId, 'subagents', `agent-${subagentId}.jsonl`); projectsDir,
projectId,
'subagents',
`agent-${subagentId}.jsonl`
);
// Check if file exists // Check if file exists
try { if (!(await fsProvider.exists(subagentPath))) {
await fs.access(subagentPath);
} catch {
logger.warn(`Subagent file not found: ${subagentPath}`); logger.warn(`Subagent file not found: ${subagentPath}`);
return null; return null;
} }