Skip to content

Commit 9294eb8

Browse files
committed
refactored parsing scripts
1 parent 1314e72 commit 9294eb8

File tree

5 files changed

+951
-509
lines changed

5 files changed

+951
-509
lines changed

src/cli/extract-chat-metrics.js

Lines changed: 126 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,46 @@ const ChatProcessor = require('./metrics/chat-processor');
99
const MetricsCalculator = require('./metrics/metrics-calculator');
1010
const 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
1353
const CLAUDE_LOGS_DIR = '/Users/nmogil/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/tasks';
1454
const METRICS_DIR = config.metrics.dataPath;
@@ -50,13 +90,6 @@ const MODEL_ARG = argv.model;
5090
const CLIENT_ARG = argv.client;
5191
const 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
6194
if (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
*/
80113
async 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
*/
124190
async 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
*/
178247
async 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

Comments
 (0)