@codervisor/devlog-ai
Version:
AI Chat History Extractor & Docker-based Automation - TypeScript implementation for GitHub Copilot and other AI coding assistants with automated testing capabilities
203 lines (202 loc) • 6.41 kB
JavaScript
/**
* Real-time Copilot Interaction Capture Parser
*
* Captures and parses Copilot interactions in real-time during automation
*/
import { EventEmitter } from 'events';
export class RealTimeCaptureParser extends EventEmitter {
isCapturing = false;
interactions = [];
startTime;
/**
* Start capturing Copilot interactions
*/
startCapture() {
if (this.isCapturing) {
throw new Error('Capture is already in progress');
}
this.isCapturing = true;
this.startTime = new Date();
this.interactions = [];
this.emit('captureStarted');
}
/**
* Stop capturing and return collected interactions
*/
async stopCapture() {
if (!this.isCapturing) {
throw new Error('No capture in progress');
}
this.isCapturing = false;
const capturedInteractions = [...this.interactions];
this.emit('captureStopped', capturedInteractions);
return capturedInteractions;
}
/**
* Record a Copilot interaction
*/
recordInteraction(interaction) {
if (!this.isCapturing) {
return;
}
this.interactions.push(interaction);
this.emit('interactionRecorded', interaction);
}
/**
* Create interaction from VS Code telemetry data
*/
createInteractionFromTelemetry(telemetryData) {
return {
timestamp: new Date(telemetryData.timestamp || Date.now()),
trigger: this.mapTriggerType(telemetryData.trigger || 'unknown'),
context: {
fileName: telemetryData.fileName || 'unknown',
fileContent: telemetryData.fileContent || '',
cursorPosition: {
line: telemetryData.line || 0,
character: telemetryData.character || 0,
},
precedingText: telemetryData.precedingText || '',
followingText: telemetryData.followingText || '',
},
suggestion: telemetryData.suggestion
? {
text: telemetryData.suggestion.text,
confidence: telemetryData.suggestion.confidence,
accepted: telemetryData.suggestion.accepted || false,
alternativeCount: telemetryData.suggestion.alternatives?.length || 0,
}
: undefined,
metadata: {
responseTime: telemetryData.responseTime,
completionType: telemetryData.completionType,
...telemetryData.metadata,
},
};
}
/**
* Parse VS Code logs for Copilot interactions
*/
async parseVSCodeLogs(logContent) {
const interactions = [];
const logLines = logContent.split('\n');
for (const line of logLines) {
const interaction = this.parseLogLine(line);
if (interaction) {
interactions.push(interaction);
}
}
return interactions;
}
/**
* Parse a single log line for Copilot data
*/
parseLogLine(line) {
// Look for Copilot-related log entries
const copilotPatterns = [
/\[copilot\].*completion.*requested/i,
/\[copilot\].*suggestion.*shown/i,
/\[copilot\].*suggestion.*accepted/i,
/\[copilot\].*suggestion.*dismissed/i,
];
for (const pattern of copilotPatterns) {
if (pattern.test(line)) {
return this.extractInteractionFromLogLine(line);
}
}
return null;
}
/**
* Extract interaction data from log line
*/
extractInteractionFromLogLine(line) {
// Basic parsing - would need enhancement for real VS Code logs
const timestamp = this.extractTimestamp(line) || new Date();
const trigger = this.extractTrigger(line);
return {
timestamp,
trigger,
context: {
fileName: this.extractFileName(line) || 'unknown',
fileContent: '',
cursorPosition: { line: 0, character: 0 },
precedingText: '',
followingText: '',
},
suggestion: {
text: this.extractSuggestionText(line) || '',
accepted: line.includes('accepted'),
},
metadata: {
logLine: line,
},
};
}
/**
* Extract timestamp from log line
*/
extractTimestamp(line) {
const timestampMatch = line.match(/(\d{4}-\d{2}-\d{2}[T\s]\d{2}:\d{2}:\d{2})/);
return timestampMatch ? new Date(timestampMatch[1]) : null;
}
/**
* Extract trigger type from log line
*/
extractTrigger(line) {
if (line.includes('keystroke') || line.includes('typing')) {
return 'keystroke';
}
if (line.includes('tab') || line.includes('accept')) {
return 'tab';
}
return 'manual';
}
/**
* Extract filename from log line
*/
extractFileName(line) {
const fileMatch = line.match(/file[:\s]+([^,\s]+)/i);
return fileMatch ? fileMatch[1] : null;
}
/**
* Extract suggestion text from log line
*/
extractSuggestionText(line) {
const suggestionMatch = line.match(/suggestion[:\s]+"([^"]+)"/i);
return suggestionMatch ? suggestionMatch[1] : null;
}
/**
* Map telemetry trigger to interaction trigger
*/
mapTriggerType(trigger) {
switch (trigger?.toLowerCase()) {
case 'keystroke':
case 'typing':
return 'keystroke';
case 'tab':
case 'accept':
return 'tab';
default:
return 'manual';
}
}
/**
* Get capture statistics
*/
getCaptureStats() {
const duration = this.startTime ? Date.now() - this.startTime.getTime() : 0;
return {
isCapturing: this.isCapturing,
duration,
interactionCount: this.interactions.length,
startTime: this.startTime,
};
}
/**
* Clear captured interactions
*/
clearCapture() {
this.interactions = [];
this.startTime = undefined;
}
}