@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
122 lines (121 loc) • 5.01 kB
JavaScript
/**
* Abstract base class for AI assistant chat history parsers
*
* Provides a common interface for parsing chat history from various AI coding assistants
* like GitHub Copilot, Cursor, Claude Code, etc.
*/
// Simple console logger implementation
export class SimpleConsoleLogger {
error(message, ...args) {
console.error(`[ERROR] ${message}`, ...args);
}
warn(message, ...args) {
console.warn(`[WARN] ${message}`, ...args);
}
info(message, ...args) {
console.log(`[INFO] ${message}`, ...args);
}
debug(message, ...args) {
// Only log debug in development or when DEBUG env var is set
if (process.env.NODE_ENV === 'development' || process.env.DEBUG) {
console.debug(`[DEBUG] ${message}`, ...args);
}
}
}
/**
* Abstract base class for AI assistant parsers
*/
export class AIAssistantParser {
logger;
constructor(logger) {
this.logger = logger || new SimpleConsoleLogger();
}
/**
* Search for content in chat sessions
*/
searchChatContent(workspaceData, query, caseSensitive = false) {
const results = [];
const searchQuery = caseSensitive ? query : query.toLowerCase();
for (const session of workspaceData.chat_sessions) {
for (const message of session.messages) {
const content = caseSensitive ? message.content : message.content.toLowerCase();
if (content.includes(searchQuery)) {
// Find context around the match
const matchPos = content.indexOf(searchQuery);
const contextStart = Math.max(0, matchPos - 100);
const contextEnd = Math.min(content.length, matchPos + searchQuery.length + 100);
const context = content.slice(contextStart, contextEnd);
results.push({
session_id: session.session_id,
message_id: message.id,
role: message.role,
timestamp: message.timestamp.toISOString(),
match_position: matchPos,
context,
full_content: message.content,
metadata: message.metadata,
});
}
}
}
return results;
}
/**
* Get statistics about chat sessions
*/
getChatStatistics(workspaceData) {
const stats = {
total_sessions: workspaceData.chat_sessions.length,
total_messages: 0,
message_types: {},
session_types: {},
workspace_activity: {},
date_range: {
earliest: null,
latest: null,
},
agent_activity: {},
};
const allTimestamps = [];
for (const session of workspaceData.chat_sessions) {
const sessionType = session.metadata.type || 'unknown';
stats.session_types[sessionType] = (stats.session_types[sessionType] || 0) + 1;
allTimestamps.push(session.timestamp);
const agent = session.agent;
stats.agent_activity[agent] = (stats.agent_activity[agent] || 0) + 1;
// Track workspace activity
const workspace = session.workspace || 'unknown_workspace';
if (!stats.workspace_activity[workspace]) {
stats.workspace_activity[workspace] = {
sessions: 0,
messages: 0,
first_seen: session.timestamp.toISOString(),
last_seen: session.timestamp.toISOString(),
};
}
const workspaceStats = stats.workspace_activity[workspace];
workspaceStats.sessions += 1;
// Update workspace date range
if (session.timestamp.toISOString() < workspaceStats.first_seen) {
workspaceStats.first_seen = session.timestamp.toISOString();
}
if (session.timestamp.toISOString() > workspaceStats.last_seen) {
workspaceStats.last_seen = session.timestamp.toISOString();
}
for (const message of session.messages) {
stats.total_messages += 1;
workspaceStats.messages += 1;
const messageType = message.metadata.type || message.role;
stats.message_types[messageType] = (stats.message_types[messageType] || 0) + 1;
allTimestamps.push(message.timestamp);
}
}
if (allTimestamps.length > 0) {
const earliest = new Date(Math.min(...allTimestamps.map((d) => d.getTime())));
const latest = new Date(Math.max(...allTimestamps.map((d) => d.getTime())));
stats.date_range.earliest = earliest.toISOString();
stats.date_range.latest = latest.toISOString();
}
return stats;
}
}