Skip to main content
Glama
report-scan.ts7.24 kB
/** * Report Scan Handler - Project Setup Tool * PRD #177 - Scope-based workflow refactoring * * Step 2 of workflow: Return ALL questions for selected scope */ import { ErrorHandler, Logger } from '../../core/error-handling'; import { GenericSessionManager } from '../../core/generic-session-manager'; import { ReportScanResponse, ProjectSetupSessionData, ErrorResponse } from './types'; /** * Handle reportScan stage - Step 2 of project setup workflow * * Returns all questions for the selected scope and list of files to generate */ export async function handleReportScan( sessionId: string, existingFiles: string[] | undefined, selectedScopes: string[] | undefined, logger: Logger, requestId: string ): Promise<ReportScanResponse | ErrorResponse> { return await ErrorHandler.withErrorHandling( async () => { logger.debug('Starting report scan analysis', { requestId, sessionId }); // Initialize session manager const sessionManager = new GenericSessionManager<ProjectSetupSessionData>('proj'); // Load session const session = sessionManager.getSession(sessionId); if (!session) { return { success: false, error: { message: `Session ${sessionId} not found`, details: 'Please start a new session with step: "discover"' } } as ErrorResponse; } // Validate session state if (!session.data.allScopes || !session.data.filesToCheck) { return { success: false, error: { message: 'Invalid session state', details: 'Session does not contain scope configuration data' } } as ErrorResponse; } const allScopes = session.data.allScopes; // Store or retrieve existingFiles let filesToUse: string[]; if (existingFiles !== undefined) { filesToUse = existingFiles; session.data.existingFiles = existingFiles; sessionManager.updateSession(sessionId, session.data); logger.debug('Stored existingFiles in session', { requestId, sessionId, count: existingFiles.length }); } else if (session.data.existingFiles !== undefined) { filesToUse = session.data.existingFiles; logger.debug('Reusing existingFiles from session', { requestId, sessionId, count: filesToUse.length }); } else { return { success: false, error: { message: 'existingFiles is required for first reportScan call', details: 'Please provide an array of files that exist in the repository' } } as ErrorResponse; } // If no scopes selected, return analysis report if (!selectedScopes || selectedScopes.length === 0) { // Analyze scope completeness const scopeStatus: Record<string, { complete: boolean; missingFiles: string[] }> = {}; for (const [scopeName, scopeConfig] of Object.entries(allScopes)) { const missingFiles = scopeConfig.files.filter(file => !filesToUse.includes(file)); scopeStatus[scopeName] = { complete: missingFiles.length === 0, missingFiles }; } const incompleteScopes = Object.entries(scopeStatus) .filter(([_, status]) => !status.complete) .map(([scopeName, _]) => scopeName); // Generate report for user to review const report = generateReport(scopeStatus, allScopes); logger.info('Generated scope analysis report', { requestId, sessionId, totalScopes: Object.keys(allScopes).length, incompleteScopes: incompleteScopes.length }); const incompleteScopeNames = incompleteScopes.join('", "'); return { success: true, sessionId, nextStep: 'generateScope', scope: '', questions: [], filesToGenerate: [], instructions: `${report}\n\nIncomplete scopes: ${incompleteScopes.join(', ')}\n\n**IMPORTANT**: Present each scope individually to the user. Do NOT combine or group scopes. Use exact scope names.\n\nTo proceed, call projectSetup tool again with:\n- step: "reportScan"\n- sessionId: "${sessionId}"\n- selectedScopes: ["${incompleteScopeNames}"] (Use exact scope names from the list above)` } as ReportScanResponse; } // User selected scope(s) - take first scope to generate const selectedScope = selectedScopes[0]; const scopeConfig = allScopes[selectedScope]; if (!scopeConfig) { return { success: false, error: { message: `Invalid scope: ${selectedScope}`, details: `Available scopes: ${Object.keys(allScopes).join(', ')}` } } as ErrorResponse; } // Calculate which files need to be generated const filesToGenerate = scopeConfig.files.filter(file => !filesToUse.includes(file)); // Store selected scopes in session session.data.selectedScopes = selectedScopes; session.data.currentStep = 'generateScope'; sessionManager.updateSession(sessionId, session.data); logger.info('Ready to generate scope', { requestId, sessionId, scope: selectedScope, filesToGenerate: filesToGenerate.length, questions: scopeConfig.questions.length }); // Return ALL questions for this scope return { success: true, sessionId, nextStep: 'generateScope', scope: selectedScope, questions: scopeConfig.questions, filesToGenerate, instructions: `Scope: ${selectedScope}\n\nFiles to generate (${filesToGenerate.length}):\n${filesToGenerate.map(f => `- ${f}`).join('\n')}\n\nQuestions (${scopeConfig.questions.length}):\n${scopeConfig.questions.map((q, i) => `${i + 1}. ${q.question} (ID: ${q.id}${q.required ? ', required' : ''})`).join('\n')}\n\nAnalyze the repository to determine answers for these questions. Present your suggested answers as a numbered list. Once finalized, call projectSetup tool with:\n- step: "generateScope"\n- sessionId: "${sessionId}"\n- scope: "${selectedScope}"\n- answers: {${scopeConfig.questions.slice(0, 2).map(q => `"${q.id}": "value"`).join(', ')}, ...}` }; }, { operation: 'project_setup_report_scan', component: 'ProjectSetupTool', requestId } ); } /** * Generate human-readable report of scope analysis */ function generateReport( scopeStatus: Record<string, { complete: boolean; missingFiles: string[] }>, allScopes: Record<string, any> ): string { const lines: string[] = ['Repository Analysis:', '']; for (const [scopeName, status] of Object.entries(scopeStatus)) { const scopeConfig = allScopes[scopeName]; const totalFiles = scopeConfig.files.length; const missingCount = status.missingFiles.length; const existingCount = totalFiles - missingCount; const statusIcon = status.complete ? '✓' : '○'; lines.push(`${statusIcon} ${scopeName}: ${existingCount}/${totalFiles} files exist`); if (!status.complete) { lines.push(` Missing: ${status.missingFiles.join(', ')}`); } } return lines.join('\n'); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/vfarcic/dot-ai'

If you have feedback or need assistance with the MCP directory API, please join our Discord server