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);
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');
}

View file

@ -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) {

View file

@ -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<SubagentDetail | null> {
// 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
);
}
}

View file

@ -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<SubagentDetail | null> {
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;
}