create-ai-chat-context-experimental
Version:
Phase 2: TypeScript rewrite - AI Chat Context & Memory System with conversation extraction and AICF format support (powered by aicf-core v2.1.0).
244 lines • 7.97 kB
JavaScript
/**
* This file is part of create-ai-chat-context-experimental.
* Licensed under the GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later).
* See LICENSE file for details.
*/
/**
* Watcher Logger
* Phase 3.3: Watcher Integration - October 2025
*
* Structured logging for watcher operations
* Integrated with aicf-core for AICF format output
*/
import chalk from 'chalk';
import { AICFWriter } from 'aicf-core';
/**
* Structured logger for watcher operations
* Integrates with aicf-core for AICF format output
*/
export class WatcherLogger {
verbose;
logLevel;
maxEntries;
entries = [];
levelPriority = {
debug: 0,
info: 1,
success: 2,
warning: 3,
error: 4,
};
aicfWriter;
constructor(options = {}) {
this.verbose = options.verbose || false;
this.logLevel = options.logLevel || 'info';
this.maxEntries = options.maxEntries || 1000;
this.aicfWriter = new AICFWriter(options.aicfDir || '.aicf');
}
/**
* Log debug message
*/
debug(message, context) {
this.log('debug', message, context);
}
/**
* Log info message
*/
info(message, context) {
this.log('info', message, context);
}
/**
* Log success message
*/
success(message, context) {
this.log('success', message, context);
}
/**
* Log warning message
*/
warning(message, context) {
this.log('warning', message, context);
}
/**
* Log error message
*/
error(message, context) {
this.log('error', message, context);
}
/**
* Internal log method
*/
log(level, message, context) {
// Check if this level should be logged
if (this.levelPriority[level] < this.levelPriority[this.logLevel]) {
return;
}
const entry = {
timestamp: new Date(),
level,
message,
context,
};
// Add to entries
this.entries.push(entry);
// Trim entries if exceeds max
if (this.entries.length > this.maxEntries) {
this.entries = this.entries.slice(-this.maxEntries);
}
// Print to console if verbose
if (this.verbose) {
this.printEntry(entry);
}
}
/**
* Print log entry to console
*/
printEntry(entry) {
const timestamp = entry.timestamp.toISOString();
const levelStr = entry.level.toUpperCase().padEnd(7);
const colorMap = {
debug: chalk.gray,
info: chalk.blue,
success: chalk.green,
warning: chalk.yellow,
error: chalk.red,
};
const colorFn = colorMap[entry.level];
const prefix = colorFn(`[${timestamp}] [${levelStr}]`);
let output = `${prefix} ${entry.message}`;
if (entry.context && Object.keys(entry.context).length > 0) {
output += ` ${JSON.stringify(entry.context)}`;
}
console.log(output);
}
/**
* Get all log entries
*/
getEntries(level) {
if (!level) {
return [...this.entries];
}
return this.entries.filter((entry) => entry.level === level);
}
/**
* Get recent log entries
*/
getRecent(count = 10, level) {
let entries = this.entries;
if (level) {
entries = entries.filter((entry) => entry.level === level);
}
return entries.slice(-count);
}
/**
* Get log entries since timestamp
*/
getSince(timestamp, level) {
let entries = this.entries.filter((entry) => entry.timestamp >= timestamp);
if (level) {
entries = entries.filter((entry) => entry.level === level);
}
return entries;
}
/**
* Get log statistics
*/
getStats() {
const stats = {
debug: 0,
info: 0,
success: 0,
warning: 0,
error: 0,
};
for (const entry of this.entries) {
stats[entry.level]++;
}
return stats;
}
/**
* Clear all log entries
*/
clear() {
this.entries = [];
}
/**
* Set verbose mode
*/
setVerbose(verbose) {
this.verbose = verbose;
}
/**
* Set log level
*/
setLogLevel(level) {
this.logLevel = level;
}
/**
* Format entries as string (plain text format)
*/
format(entries) {
const entriesToFormat = entries || this.entries;
return entriesToFormat
.map((entry) => {
const timestamp = entry.timestamp.toISOString();
const level = entry.level.toUpperCase().padEnd(7);
let line = `[${timestamp}] [${level}] ${entry.message}`;
if (entry.context && Object.keys(entry.context).length > 0) {
line += ` ${JSON.stringify(entry.context)}`;
}
return line;
})
.join('\n');
}
/**
* Write entries to AICF file using aicf-core
* Delegates to AICFWriter for professional AICF format output
*/
async writeToAICF(entries) {
const entriesToWrite = entries || this.entries;
if (entriesToWrite.length === 0) {
return { ok: true };
}
try {
// Group entries by type
const checkEntries = entriesToWrite.filter((e) => e.message.includes('checkpoint') || e.message.includes('Checking'));
const parseEntries = entriesToWrite.filter((e) => e.message.includes('parse') || e.message.includes('platform'));
const writeEntries = entriesToWrite.filter((e) => e.message.includes('write') || e.message.includes('Processed'));
const errorEntries = entriesToWrite.filter((e) => e.level === 'error');
// Write checkpoint events
for (const entry of checkEntries) {
await this.aicfWriter.appendLine('.watcher-events.aicf', `@WATCHER_CHECK|timestamp=${entry.timestamp.toISOString()}|event=checkpoint_check|status=${entry.level === 'error' ? 'error' : 'success'}|message=${entry.message}`);
}
// Write parse events
for (const entry of parseEntries) {
await this.aicfWriter.appendLine('.watcher-events.aicf', `@WATCHER_PARSE|timestamp=${entry.timestamp.toISOString()}|event=conversation_parse|status=${entry.level === 'error' ? 'error' : 'success'}|message=${entry.message}`);
}
// Write file write events
for (const entry of writeEntries) {
await this.aicfWriter.appendLine('.watcher-events.aicf', `@WATCHER_WRITE|timestamp=${entry.timestamp.toISOString()}|event=files_written|status=${entry.level === 'error' ? 'error' : 'success'}|message=${entry.message}`);
}
// Write error events
for (const entry of errorEntries) {
await this.aicfWriter.appendLine('.watcher-events.aicf', `@WATCHER_ERROR|timestamp=${entry.timestamp.toISOString()}|event=watcher_error|status=error|message=${entry.message}`);
}
// Write summary
const stats = this.getStats();
await this.aicfWriter.appendLine('.watcher-events.aicf', `@WATCHER_SUMMARY|timestamp=${new Date().toISOString()}|event=cycle_summary|total_entries=${entriesToWrite.length}|info=${stats.info}|success=${stats.success}|warning=${stats.warning}|error=${stats.error}|debug=${stats.debug}`);
return { ok: true };
}
catch (error) {
return {
ok: false,
error: error instanceof Error ? error.message : String(error),
};
}
}
/**
* Get total entry count
*/
getCount() {
return this.entries.length;
}
}
//# sourceMappingURL=WatcherLogger.js.map