@@ -9,6 +9,46 @@ const ChatProcessor = require('./metrics/chat-processor');
99const MetricsCalculator = require ( './metrics/metrics-calculator' ) ;
1010const SummaryGenerator = require ( './metrics/summary-generator' ) ;
1111
12+ /**
13+ * Result class for consistent error handling
14+ */
15+ class ExtractionResult {
16+ /**
17+ * Create a new ExtractionResult
18+ * @param {boolean } success Whether the operation was successful
19+ * @param {string } message A message describing the result
20+ * @param {Array } metrics The extracted metrics
21+ * @param {Array } errors Any errors encountered
22+ */
23+ constructor ( success , message , metrics = [ ] , errors = [ ] ) {
24+ this . success = success ;
25+ this . message = message ;
26+ this . metrics = metrics ;
27+ this . errors = errors ;
28+ }
29+
30+ /**
31+ * Create a success result
32+ * @param {string } message Success message
33+ * @param {Array } metrics Extracted metrics
34+ * @returns {ExtractionResult } A success result
35+ */
36+ static success ( message , metrics = [ ] ) {
37+ return new ExtractionResult ( true , message , metrics ) ;
38+ }
39+
40+ /**
41+ * Create an error result
42+ * @param {string } message Error message
43+ * @param {Array } metrics Any metrics that were successfully extracted
44+ * @param {Array } errors Any errors encountered
45+ * @returns {ExtractionResult } An error result
46+ */
47+ static error ( message , metrics = [ ] , errors = [ ] ) {
48+ return new ExtractionResult ( false , message , metrics , errors ) ;
49+ }
50+ }
51+
1252// Constants
1353const CLAUDE_LOGS_DIR = '/Users/nmogil/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/tasks' ;
1454const METRICS_DIR = config . metrics . dataPath ;
@@ -50,13 +90,6 @@ const MODEL_ARG = argv.model;
5090const CLIENT_ARG = argv . client ;
5191const SERVER_ARG = argv . server ;
5292
53- // Show help message if requested (yargs handles this automatically, but good practice)
54- // Note: yargs automatically exits after showing help, so this block might not be strictly needed
55- if ( argv . help ) {
56- // yargs handles printing help, we just need to ensure the script exits cleanly if needed.
57- // process.exit(0); // yargs usually handles exit
58- }
59-
6093// Log argument usage
6194if ( VERBOSE ) {
6295 logger . debug ( 'Verbose mode enabled - will show detailed logging' ) ;
@@ -78,60 +111,95 @@ if (SERVER_ARG) {
78111 * Main function to extract metrics from Claude chat logs
79112 */
80113async function extractChatMetrics ( ) {
114+ // Ensure metrics directory exists
81115 try {
82- // Ensure metrics directory exists
83- try {
84- await fs . access ( METRICS_DIR ) ;
85- } catch ( error ) {
86- throw new Error ( `Metrics directory ${ METRICS_DIR } does not exist. Please create it before running this script.` ) ;
87- }
116+ await fs . access ( METRICS_DIR ) ;
117+ } catch ( error ) {
118+ logger . error ( `Metrics directory ${ METRICS_DIR } does not exist. Please create it before running this script.` ) ;
119+ return ExtractionResult . error ( `Metrics directory ${ METRICS_DIR } does not exist` ) ;
120+ }
88121
122+ try {
89123 // Get all chat directories
90- const chatDirs = await getChatDirectories ( ) ;
124+ const chatDirsResult = await getChatDirectories ( ) ;
125+ if ( ! chatDirsResult . success ) {
126+ return chatDirsResult ;
127+ }
128+
129+ const chatDirs = chatDirsResult . directories ;
91130 logger . info ( `Found ${ chatDirs . length } chat directories` ) ;
92131
93132 // Process each chat directory in parallel, passing arguments
94- const allTaskMetrics = await Promise . all (
133+ const processingResults = await Promise . all (
95134 chatDirs . map ( dir => processDirectory ( dir , MODEL_ARG , CLIENT_ARG , SERVER_ARG ) )
96135 ) ;
97136
98- // Flatten and filter out null results
99- const validMetrics = allTaskMetrics
100- . flat ( )
101- . filter ( metric => metric !== null ) ;
137+ // Collect all metrics and errors
138+ const allMetrics = [ ] ;
139+ const allErrors = [ ] ;
140+
141+ for ( const result of processingResults ) {
142+ if ( result . metrics ?. length ) {
143+ allMetrics . push ( ...result . metrics ) ;
144+ }
145+ if ( result . error ) {
146+ allErrors . push ( result . error ) ;
147+ }
148+ }
102149
103150 // Create a summary generator
104151 const summaryGenerator = new SummaryGenerator ( METRICS_DIR ) ;
105152
106153 // If we have new metrics, write individual metric files
107- if ( validMetrics . length > 0 ) {
108- await summaryGenerator . writeIndividualMetricFiles ( validMetrics ) ;
154+ if ( allMetrics . length > 0 ) {
155+ const writeResult = await summaryGenerator . writeIndividualMetricFiles ( allMetrics ) ;
156+ if ( ! writeResult . success ) {
157+ logger . warn ( `Some metrics files could not be written: ${ writeResult . message } ` ) ;
158+ }
109159 } else {
110160 logger . warn ( 'No new task metrics found in chat logs' ) ;
111161 }
112162
113163 // Always generate summary from all available metric files
114164 // This ensures the summary is updated even if individual files were manually edited
115- await summaryGenerator . generateSummaryFromFiles ( ) ;
165+ const summaryResult = await summaryGenerator . generateSummaryFromFiles ( ) ;
166+
167+ if ( ! summaryResult . success ) {
168+ logger . warn ( `Summary generation had issues: ${ summaryResult . message } ` ) ;
169+ return ExtractionResult . error (
170+ 'Completed with some errors' ,
171+ allMetrics ,
172+ [ ...allErrors , summaryResult . message ]
173+ ) ;
174+ }
175+
176+ return ExtractionResult . success (
177+ `Successfully processed ${ allMetrics . length } metrics` ,
178+ allMetrics
179+ ) ;
116180 } catch ( error ) {
117181 logger . error ( 'Error extracting metrics:' , error ) ;
182+ return ExtractionResult . error ( `Extraction failed: ${ error . message } ` ) ;
118183 }
119184}
120185
121186/**
122187 * Get all chat directories
188+ * @returns {Promise<{success: boolean, directories: Array, error: string}> } Result with directories
123189 */
124190async function getChatDirectories ( ) {
191+ // Check if the root directory exists
192+ try {
193+ await fs . access ( CLAUDE_LOGS_DIR ) ;
194+ } catch ( error ) {
195+ const errorMsg = `Claude logs directory ${ CLAUDE_LOGS_DIR } does not exist. Please ensure the Claude extension is installed and has generated logs.` ;
196+ logger . error ( errorMsg ) ;
197+ return { success : false , directories : [ ] , error : errorMsg } ;
198+ }
199+
125200 try {
126201 const chatDirs = [ ] ;
127202
128- // Check if the root directory exists
129- try {
130- await fs . access ( CLAUDE_LOGS_DIR ) ;
131- } catch ( error ) {
132- throw new Error ( `Claude logs directory ${ CLAUDE_LOGS_DIR } does not exist. Please ensure the Claude extension is installed and has generated logs.` ) ;
133- }
134-
135203 // Get all subdirectories
136204 const items = await fs . readdir ( CLAUDE_LOGS_DIR , { withFileTypes : true } ) ;
137205
@@ -161,10 +229,10 @@ async function getChatDirectories() {
161229 }
162230 }
163231
164- return chatDirs ;
232+ return { success : true , directories : chatDirs } ;
165233 } catch ( error ) {
166234 logger . error ( 'Error getting chat directories:' , error ) ;
167- return [ ] ;
235+ return { success : false , directories : [ ] , error : error . message } ;
168236 }
169237}
170238
@@ -174,56 +242,59 @@ async function getChatDirectories() {
174242 * @param {string|undefined } modelArg - The model name provided via CLI argument
175243 * @param {string|undefined } clientArg - The client name provided via CLI argument
176244 * @param {string|undefined } serverArg - The server name provided via CLI argument
245+ * @returns {Promise<{metrics: Array, error: string|undefined}> } Processing result
177246 */
178247async function processDirectory ( dir , modelArg , clientArg , serverArg ) {
179248 try {
180249 const chatProcessor = new ChatProcessor ( dir ) ;
181- const initialized = await chatProcessor . initialize ( ) ;
182- if ( ! initialized ) {
183- return [ ] ;
184- }
185-
186- const testType = chatProcessor . identifyTestType ( ) ;
187- if ( ! testType ) {
188- logger . debug ( `Skipping directory ${ dir } - not a test chat` ) ;
189- return [ ] ;
250+
251+ // Use the new process method that centralizes error handling
252+ const taskSegments = await chatProcessor . process ( ) ;
253+
254+ if ( ! taskSegments . length ) {
255+ return { metrics : [ ] , error : undefined } ;
190256 }
191257
192- logger . info ( `Processing ${ testType } test in directory: ${ path . basename ( dir ) } ` ) ;
193-
194- // Extract task segments
195- const taskSegments = await chatProcessor . extractTaskSegments ( testType ) ;
258+ logger . info ( `Processing test in directory: ${ path . basename ( dir ) } ` ) ;
196259 logger [ VERBOSE ? 'debug' : 'info' ] ( `Found ${ taskSegments . length } task segments` ) ;
197260
198- // Return early if no task segments found
199- if ( taskSegments . length === 0 ) {
200- return [ ] ;
201- }
202-
203261 // Process only the first task segment found for this directory
204262 // This ensures we don't create multiple metrics for the same directory
205263 const taskSegment = taskSegments [ 0 ] ;
206-
207- // Get the directory ID
264+ const testType = taskSegment . testType ;
208265 const directoryId = path . basename ( dir ) ;
209266
210267 // Check if metrics for this task already exist (unless force regenerate is enabled)
211268 if ( ! FORCE_REGENERATE ) {
212269 const summaryGenerator = new SummaryGenerator ( METRICS_DIR ) ;
213270 if ( await summaryGenerator . metricFileExists ( testType , taskSegment . taskNumber , directoryId ) ) {
214271 logger . info ( `Skipping task ${ taskSegment . taskNumber } (${ testType } ) - metrics file already exists for directory ${ directoryId } ` ) ;
215- return [ ] ;
272+ return { metrics : [ ] , error : undefined } ;
216273 }
217274 }
275+
218276 // Pass model, client, and server args to the calculator
219277 const calculator = new MetricsCalculator ( taskSegment , testType , directoryId , modelArg , clientArg , serverArg ) ;
220278 const metrics = await calculator . calculate ( ) ;
221- return metrics ? [ metrics ] : [ ] ;
279+
280+ return {
281+ metrics : metrics ? [ metrics ] : [ ] ,
282+ error : metrics ? undefined : `Failed to calculate metrics for ${ directoryId } `
283+ } ;
222284 } catch ( error ) {
223285 logger . error ( `Error processing directory ${ dir } :` , error ) ;
224- return [ ] ;
286+ return { metrics : [ ] , error : `Error processing ${ path . basename ( dir ) } : ${ error . message } ` } ;
225287 }
226288}
227289
228290// Run the extraction
229- extractChatMetrics ( ) ;
291+ extractChatMetrics ( ) . then ( result => {
292+ if ( result . success ) {
293+ logger . info ( `Extraction completed successfully: ${ result . message } ` ) ;
294+ } else {
295+ logger . error ( `Extraction completed with errors: ${ result . message } ` ) ;
296+ if ( result . errors . length > 0 ) {
297+ logger . error ( `Encountered ${ result . errors . length } errors during extraction` ) ;
298+ }
299+ }
300+ } ) ;
0 commit comments