claude-flow 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +612 -0
  3. package/bin/claude-flow +0 -0
  4. package/bin/claude-flow-simple +0 -0
  5. package/bin/claude-flow-typecheck +0 -0
  6. package/deno.json +84 -0
  7. package/package.json +45 -0
  8. package/scripts/check-links.ts +274 -0
  9. package/scripts/check-performance-regression.ts +168 -0
  10. package/scripts/claude-sparc.sh +562 -0
  11. package/scripts/coverage-report.ts +692 -0
  12. package/scripts/demo-task-system.ts +224 -0
  13. package/scripts/install.js +72 -0
  14. package/scripts/test-batch-tasks.ts +29 -0
  15. package/scripts/test-coordination-features.ts +238 -0
  16. package/scripts/test-mcp.ts +251 -0
  17. package/scripts/test-runner.ts +571 -0
  18. package/scripts/validate-examples.ts +288 -0
  19. package/src/cli/cli-core.ts +273 -0
  20. package/src/cli/commands/agent.ts +83 -0
  21. package/src/cli/commands/config.ts +442 -0
  22. package/src/cli/commands/help.ts +765 -0
  23. package/src/cli/commands/index.ts +963 -0
  24. package/src/cli/commands/mcp.ts +191 -0
  25. package/src/cli/commands/memory.ts +74 -0
  26. package/src/cli/commands/monitor.ts +403 -0
  27. package/src/cli/commands/session.ts +595 -0
  28. package/src/cli/commands/start.ts +156 -0
  29. package/src/cli/commands/status.ts +345 -0
  30. package/src/cli/commands/task.ts +79 -0
  31. package/src/cli/commands/workflow.ts +763 -0
  32. package/src/cli/completion.ts +553 -0
  33. package/src/cli/formatter.ts +310 -0
  34. package/src/cli/index.ts +211 -0
  35. package/src/cli/main.ts +23 -0
  36. package/src/cli/repl.ts +1050 -0
  37. package/src/cli/simple-cli.js +211 -0
  38. package/src/cli/simple-cli.ts +211 -0
  39. package/src/coordination/README.md +400 -0
  40. package/src/coordination/advanced-scheduler.ts +487 -0
  41. package/src/coordination/circuit-breaker.ts +366 -0
  42. package/src/coordination/conflict-resolution.ts +490 -0
  43. package/src/coordination/dependency-graph.ts +475 -0
  44. package/src/coordination/index.ts +63 -0
  45. package/src/coordination/manager.ts +460 -0
  46. package/src/coordination/messaging.ts +290 -0
  47. package/src/coordination/metrics.ts +585 -0
  48. package/src/coordination/resources.ts +322 -0
  49. package/src/coordination/scheduler.ts +390 -0
  50. package/src/coordination/work-stealing.ts +224 -0
  51. package/src/core/config.ts +627 -0
  52. package/src/core/event-bus.ts +186 -0
  53. package/src/core/json-persistence.ts +183 -0
  54. package/src/core/logger.ts +262 -0
  55. package/src/core/orchestrator-fixed.ts +312 -0
  56. package/src/core/orchestrator.ts +1234 -0
  57. package/src/core/persistence.ts +276 -0
  58. package/src/mcp/auth.ts +438 -0
  59. package/src/mcp/claude-flow-tools.ts +1280 -0
  60. package/src/mcp/load-balancer.ts +510 -0
  61. package/src/mcp/router.ts +240 -0
  62. package/src/mcp/server.ts +548 -0
  63. package/src/mcp/session-manager.ts +418 -0
  64. package/src/mcp/tools.ts +180 -0
  65. package/src/mcp/transports/base.ts +21 -0
  66. package/src/mcp/transports/http.ts +457 -0
  67. package/src/mcp/transports/stdio.ts +254 -0
  68. package/src/memory/backends/base.ts +22 -0
  69. package/src/memory/backends/markdown.ts +283 -0
  70. package/src/memory/backends/sqlite.ts +329 -0
  71. package/src/memory/cache.ts +238 -0
  72. package/src/memory/indexer.ts +238 -0
  73. package/src/memory/manager.ts +572 -0
  74. package/src/terminal/adapters/base.ts +29 -0
  75. package/src/terminal/adapters/native.ts +504 -0
  76. package/src/terminal/adapters/vscode.ts +340 -0
  77. package/src/terminal/manager.ts +308 -0
  78. package/src/terminal/pool.ts +271 -0
  79. package/src/terminal/session.ts +250 -0
  80. package/src/terminal/vscode-bridge.ts +242 -0
  81. package/src/utils/errors.ts +231 -0
  82. package/src/utils/helpers.ts +476 -0
  83. package/src/utils/types.ts +493 -0
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Event bus implementation for Claude-Flow
3
+ */
4
+
5
+ import { SystemEvents, EventMap } from '../utils/types.ts';
6
+ import { TypedEventEmitter } from '../utils/helpers.ts';
7
+
8
+ export interface IEventBus {
9
+ emit(event: string, data?: unknown): void;
10
+ on(event: string, handler: (data: unknown) => void): void;
11
+ off(event: string, handler: (data: unknown) => void): void;
12
+ once(event: string, handler: (data: unknown) => void): void;
13
+ }
14
+
15
+ /**
16
+ * Internal typed event bus
17
+ */
18
+ class TypedEventBus extends TypedEventEmitter<EventMap> {
19
+ private eventCounts = new Map<keyof EventMap, number>();
20
+ private lastEventTimes = new Map<keyof EventMap, number>();
21
+ private debug: boolean;
22
+
23
+ constructor(debug = false) {
24
+ super();
25
+ this.debug = debug;
26
+ }
27
+
28
+ /**
29
+ * Emits an event with logging
30
+ */
31
+ override emit<K extends keyof EventMap>(event: K, data: EventMap[K]): void {
32
+ if (this.debug) {
33
+ console.debug(`[EventBus] Emitting event: ${String(event)}`, data);
34
+ }
35
+
36
+ // Track event metrics
37
+ const count = this.eventCounts.get(event) || 0;
38
+ this.eventCounts.set(event, count + 1);
39
+ this.lastEventTimes.set(event, Date.now());
40
+
41
+ super.emit(event, data);
42
+ }
43
+
44
+ /**
45
+ * Get event statistics
46
+ */
47
+ getEventStats(): { event: string; count: number; lastEmitted: Date | null }[] {
48
+ const stats: { event: string; count: number; lastEmitted: Date | null }[] = [];
49
+
50
+ for (const [event, count] of this.eventCounts.entries()) {
51
+ const lastTime = this.lastEventTimes.get(event);
52
+ stats.push({
53
+ event: String(event),
54
+ count,
55
+ lastEmitted: lastTime ? new Date(lastTime) : null,
56
+ });
57
+ }
58
+
59
+ return stats.sort((a, b) => b.count - a.count);
60
+ }
61
+
62
+ /**
63
+ * Reset event statistics
64
+ */
65
+ resetStats(): void {
66
+ this.eventCounts.clear();
67
+ this.lastEventTimes.clear();
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Global event bus for system-wide communication
73
+ */
74
+ export class EventBus implements IEventBus {
75
+ private static instance: EventBus;
76
+ private typedBus: TypedEventBus;
77
+
78
+ private constructor(debug = false) {
79
+ this.typedBus = new TypedEventBus(debug);
80
+ }
81
+
82
+ /**
83
+ * Gets the singleton instance of the event bus
84
+ */
85
+ static getInstance(debug = false): EventBus {
86
+ if (!EventBus.instance) {
87
+ EventBus.instance = new EventBus(debug);
88
+ }
89
+ return EventBus.instance;
90
+ }
91
+
92
+ /**
93
+ * Emits an event
94
+ */
95
+ emit(event: string, data?: unknown): void {
96
+ // Type-safe emission for known events
97
+ if (event in SystemEvents) {
98
+ this.typedBus.emit(event as keyof EventMap, data as any);
99
+ } else {
100
+ // For custom events, emit as-is
101
+ this.typedBus.emit(event as any, data as any);
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Registers an event handler
107
+ */
108
+ on(event: string, handler: (data: unknown) => void): void {
109
+ this.typedBus.on(event as any, handler);
110
+ }
111
+
112
+ /**
113
+ * Removes an event handler
114
+ */
115
+ off(event: string, handler: (data: unknown) => void): void {
116
+ this.typedBus.off(event as any, handler);
117
+ }
118
+
119
+ /**
120
+ * Registers a one-time event handler
121
+ */
122
+ once(event: string, handler: (data: unknown) => void): void {
123
+ this.typedBus.once(event as any, handler);
124
+ }
125
+
126
+ /**
127
+ * Waits for an event to occur
128
+ */
129
+ async waitFor(event: string, timeoutMs?: number): Promise<unknown> {
130
+ return new Promise((resolve, reject) => {
131
+ const handler = (data: unknown) => {
132
+ if (timer) clearTimeout(timer);
133
+ resolve(data);
134
+ };
135
+
136
+ let timer: number | undefined;
137
+ if (timeoutMs) {
138
+ timer = setTimeout(() => {
139
+ this.off(event, handler);
140
+ reject(new Error(`Timeout waiting for event: ${event}`));
141
+ }, timeoutMs);
142
+ }
143
+
144
+ this.once(event, handler);
145
+ });
146
+ }
147
+
148
+ /**
149
+ * Creates a filtered event listener
150
+ */
151
+ onFiltered(
152
+ event: string,
153
+ filter: (data: unknown) => boolean,
154
+ handler: (data: unknown) => void,
155
+ ): void {
156
+ this.on(event, (data) => {
157
+ if (filter(data)) {
158
+ handler(data);
159
+ }
160
+ });
161
+ }
162
+
163
+ /**
164
+ * Get event statistics
165
+ */
166
+ getEventStats(): { event: string; count: number; lastEmitted: Date | null }[] {
167
+ return this.typedBus.getEventStats();
168
+ }
169
+
170
+ /**
171
+ * Reset event statistics
172
+ */
173
+ resetStats(): void {
174
+ this.typedBus.resetStats();
175
+ }
176
+
177
+ /**
178
+ * Remove all listeners for an event
179
+ */
180
+ removeAllListeners(event?: string): void {
181
+ this.typedBus.removeAllListeners(event as any);
182
+ }
183
+ }
184
+
185
+ // Export singleton instance
186
+ export const eventBus = EventBus.getInstance();
@@ -0,0 +1,183 @@
1
+ /**
2
+ * JSON-based persistence layer for Claude-Flow
3
+ */
4
+
5
+ import { join } from "https://deno.land/std@0.224.0/path/mod.ts";
6
+ import { ensureDir, exists } from "https://deno.land/std@0.224.0/fs/mod.ts";
7
+
8
+ export interface PersistedAgent {
9
+ id: string;
10
+ type: string;
11
+ name: string;
12
+ status: string;
13
+ capabilities: string[];
14
+ systemPrompt: string;
15
+ maxConcurrentTasks: number;
16
+ priority: number;
17
+ createdAt: number;
18
+ }
19
+
20
+ export interface PersistedTask {
21
+ id: string;
22
+ type: string;
23
+ description: string;
24
+ status: string;
25
+ priority: number;
26
+ dependencies: string[];
27
+ metadata: Record<string, unknown>;
28
+ assignedAgent?: string;
29
+ progress: number;
30
+ error?: string;
31
+ createdAt: number;
32
+ completedAt?: number;
33
+ }
34
+
35
+ interface PersistenceData {
36
+ agents: PersistedAgent[];
37
+ tasks: PersistedTask[];
38
+ lastUpdated: number;
39
+ }
40
+
41
+ export class JsonPersistenceManager {
42
+ private dataPath: string;
43
+ private data: PersistenceData;
44
+
45
+ constructor(dataDir: string = "./memory") {
46
+ this.dataPath = join(dataDir, "claude-flow-data.json");
47
+ this.data = {
48
+ agents: [],
49
+ tasks: [],
50
+ lastUpdated: Date.now(),
51
+ };
52
+ }
53
+
54
+ async initialize(): Promise<void> {
55
+ // Ensure directory exists
56
+ await ensureDir(join(this.dataPath, ".."));
57
+
58
+ // Load existing data if available
59
+ if (await exists(this.dataPath)) {
60
+ try {
61
+ const content = await Deno.readTextFile(this.dataPath);
62
+ this.data = JSON.parse(content);
63
+ } catch (error) {
64
+ console.error("Failed to load persistence data:", error);
65
+ // Keep default empty data
66
+ }
67
+ }
68
+ }
69
+
70
+ private async save(): Promise<void> {
71
+ this.data.lastUpdated = Date.now();
72
+ await Deno.writeTextFile(this.dataPath, JSON.stringify(this.data, null, 2));
73
+ }
74
+
75
+ // Agent operations
76
+ async saveAgent(agent: PersistedAgent): Promise<void> {
77
+ // Remove existing agent if updating
78
+ this.data.agents = this.data.agents.filter(a => a.id !== agent.id);
79
+ this.data.agents.push(agent);
80
+ await this.save();
81
+ }
82
+
83
+ async getAgent(id: string): Promise<PersistedAgent | null> {
84
+ return this.data.agents.find(a => a.id === id) || null;
85
+ }
86
+
87
+ async getActiveAgents(): Promise<PersistedAgent[]> {
88
+ return this.data.agents.filter(a => a.status === 'active' || a.status === 'idle');
89
+ }
90
+
91
+ async getAllAgents(): Promise<PersistedAgent[]> {
92
+ return this.data.agents;
93
+ }
94
+
95
+ async updateAgentStatus(id: string, status: string): Promise<void> {
96
+ const agent = this.data.agents.find(a => a.id === id);
97
+ if (agent) {
98
+ agent.status = status;
99
+ await this.save();
100
+ }
101
+ }
102
+
103
+ // Task operations
104
+ async saveTask(task: PersistedTask): Promise<void> {
105
+ // Remove existing task if updating
106
+ this.data.tasks = this.data.tasks.filter(t => t.id !== task.id);
107
+ this.data.tasks.push(task);
108
+ await this.save();
109
+ }
110
+
111
+ async getTask(id: string): Promise<PersistedTask | null> {
112
+ return this.data.tasks.find(t => t.id === id) || null;
113
+ }
114
+
115
+ async getActiveTasks(): Promise<PersistedTask[]> {
116
+ return this.data.tasks.filter(t =>
117
+ t.status === 'pending' ||
118
+ t.status === 'in_progress' ||
119
+ t.status === 'assigned'
120
+ );
121
+ }
122
+
123
+ async getAllTasks(): Promise<PersistedTask[]> {
124
+ return this.data.tasks;
125
+ }
126
+
127
+ async updateTaskStatus(id: string, status: string, assignedAgent?: string): Promise<void> {
128
+ const task = this.data.tasks.find(t => t.id === id);
129
+ if (task) {
130
+ task.status = status;
131
+ if (assignedAgent !== undefined) {
132
+ task.assignedAgent = assignedAgent;
133
+ }
134
+ if (status === 'completed') {
135
+ task.completedAt = Date.now();
136
+ }
137
+ await this.save();
138
+ }
139
+ }
140
+
141
+ async updateTaskProgress(id: string, progress: number): Promise<void> {
142
+ const task = this.data.tasks.find(t => t.id === id);
143
+ if (task) {
144
+ task.progress = progress;
145
+ await this.save();
146
+ }
147
+ }
148
+
149
+ // Statistics
150
+ async getStats(): Promise<{
151
+ totalAgents: number;
152
+ activeAgents: number;
153
+ totalTasks: number;
154
+ pendingTasks: number;
155
+ completedTasks: number;
156
+ }> {
157
+ const activeAgents = this.data.agents.filter(a =>
158
+ a.status === 'active' || a.status === 'idle'
159
+ ).length;
160
+
161
+ const pendingTasks = this.data.tasks.filter(t =>
162
+ t.status === 'pending' ||
163
+ t.status === 'in_progress' ||
164
+ t.status === 'assigned'
165
+ ).length;
166
+
167
+ const completedTasks = this.data.tasks.filter(t =>
168
+ t.status === 'completed'
169
+ ).length;
170
+
171
+ return {
172
+ totalAgents: this.data.agents.length,
173
+ activeAgents,
174
+ totalTasks: this.data.tasks.length,
175
+ pendingTasks,
176
+ completedTasks,
177
+ };
178
+ }
179
+
180
+ close(): void {
181
+ // No-op for JSON persistence
182
+ }
183
+ }
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Logging infrastructure for Claude-Flow
3
+ */
4
+
5
+ import { LoggingConfig } from '../utils/types.ts';
6
+ import { formatBytes } from '../utils/helpers.ts';
7
+
8
+ export interface ILogger {
9
+ debug(message: string, meta?: unknown): void;
10
+ info(message: string, meta?: unknown): void;
11
+ warn(message: string, meta?: unknown): void;
12
+ error(message: string, error?: unknown): void;
13
+ configure(config: LoggingConfig): Promise<void>;
14
+ }
15
+
16
+ export enum LogLevel {
17
+ DEBUG = 0,
18
+ INFO = 1,
19
+ WARN = 2,
20
+ ERROR = 3,
21
+ }
22
+
23
+ interface LogEntry {
24
+ timestamp: string;
25
+ level: string;
26
+ message: string;
27
+ context: Record<string, unknown>;
28
+ data?: unknown;
29
+ error?: unknown;
30
+ }
31
+
32
+ /**
33
+ * Logger implementation with context support
34
+ */
35
+ export class Logger implements ILogger {
36
+ private static instance: Logger;
37
+ private config: LoggingConfig;
38
+ private context: Record<string, unknown>;
39
+ private fileHandle?: Deno.FsFile;
40
+ private currentFileSize = 0;
41
+ private currentFileIndex = 0;
42
+
43
+ constructor(
44
+ config: LoggingConfig = {
45
+ level: 'info',
46
+ format: 'json',
47
+ destination: 'console',
48
+ },
49
+ context: Record<string, unknown> = {},
50
+ ) {
51
+ this.config = config;
52
+ this.context = context;
53
+ }
54
+
55
+ /**
56
+ * Gets the singleton instance of the logger
57
+ */
58
+ static getInstance(config?: LoggingConfig): Logger {
59
+ if (!Logger.instance) {
60
+ Logger.instance = new Logger(config);
61
+ }
62
+ return Logger.instance;
63
+ }
64
+
65
+ /**
66
+ * Updates logger configuration
67
+ */
68
+ async configure(config: LoggingConfig): Promise<void> {
69
+ this.config = config;
70
+
71
+ // Reset file handle if destination changed
72
+ if (this.fileHandle && config.destination !== 'file' && config.destination !== 'both') {
73
+ await this.fileHandle.close();
74
+ delete this.fileHandle;
75
+ }
76
+ }
77
+
78
+ debug(message: string, meta?: unknown): void {
79
+ this.log(LogLevel.DEBUG, message, meta);
80
+ }
81
+
82
+ info(message: string, meta?: unknown): void {
83
+ this.log(LogLevel.INFO, message, meta);
84
+ }
85
+
86
+ warn(message: string, meta?: unknown): void {
87
+ this.log(LogLevel.WARN, message, meta);
88
+ }
89
+
90
+ error(message: string, error?: unknown): void {
91
+ this.log(LogLevel.ERROR, message, undefined, error);
92
+ }
93
+
94
+ /**
95
+ * Creates a child logger with additional context
96
+ */
97
+ child(context: Record<string, unknown>): Logger {
98
+ return new Logger(this.config, { ...this.context, ...context });
99
+ }
100
+
101
+ private log(level: LogLevel, message: string, data?: unknown, error?: unknown): void {
102
+ if (!this.shouldLog(level)) {
103
+ return;
104
+ }
105
+
106
+ const entry: LogEntry = {
107
+ timestamp: new Date().toISOString(),
108
+ level: LogLevel[level],
109
+ message,
110
+ context: this.context,
111
+ data,
112
+ error,
113
+ };
114
+
115
+ const formatted = this.format(entry);
116
+
117
+ if (this.config.destination === 'console' || this.config.destination === 'both') {
118
+ this.writeToConsole(level, formatted);
119
+ }
120
+
121
+ if (this.config.destination === 'file' || this.config.destination === 'both') {
122
+ this.writeToFile(formatted);
123
+ }
124
+ }
125
+
126
+ private shouldLog(level: LogLevel): boolean {
127
+ const configLevel = LogLevel[this.config.level.toUpperCase() as keyof typeof LogLevel];
128
+ return level >= configLevel;
129
+ }
130
+
131
+ private format(entry: LogEntry): string {
132
+ if (this.config.format === 'json') {
133
+ return JSON.stringify(entry);
134
+ }
135
+
136
+ // Text format
137
+ const contextStr = Object.keys(entry.context).length > 0
138
+ ? ` ${JSON.stringify(entry.context)}`
139
+ : '';
140
+ const dataStr = entry.data !== undefined
141
+ ? ` ${JSON.stringify(entry.data)}`
142
+ : '';
143
+ const errorStr = entry.error !== undefined
144
+ ? entry.error instanceof Error
145
+ ? `\n Error: ${entry.error.message}\n Stack: ${entry.error.stack}`
146
+ : ` Error: ${JSON.stringify(entry.error)}`
147
+ : '';
148
+
149
+ return `[${entry.timestamp}] ${entry.level} ${entry.message}${contextStr}${dataStr}${errorStr}`;
150
+ }
151
+
152
+ private writeToConsole(level: LogLevel, message: string): void {
153
+ switch (level) {
154
+ case LogLevel.DEBUG:
155
+ console.debug(message);
156
+ break;
157
+ case LogLevel.INFO:
158
+ console.info(message);
159
+ break;
160
+ case LogLevel.WARN:
161
+ console.warn(message);
162
+ break;
163
+ case LogLevel.ERROR:
164
+ console.error(message);
165
+ break;
166
+ }
167
+ }
168
+
169
+ private async writeToFile(message: string): Promise<void> {
170
+ if (!this.config.filePath) {
171
+ return;
172
+ }
173
+
174
+ try {
175
+ // Check if we need to rotate the log file
176
+ if (await this.shouldRotate()) {
177
+ await this.rotate();
178
+ }
179
+
180
+ // Open file handle if not already open
181
+ if (!this.fileHandle) {
182
+ this.fileHandle = await Deno.open(this.config.filePath, {
183
+ create: true,
184
+ append: true,
185
+ write: true,
186
+ });
187
+ }
188
+
189
+ // Write the message
190
+ const encoder = new TextEncoder();
191
+ const data = encoder.encode(message + '\n');
192
+ await this.fileHandle.write(data);
193
+ this.currentFileSize += data.length;
194
+ } catch (error) {
195
+ console.error('Failed to write to log file:', error);
196
+ }
197
+ }
198
+
199
+ private async shouldRotate(): Promise<boolean> {
200
+ if (!this.config.maxFileSize || !this.fileHandle) {
201
+ return false;
202
+ }
203
+
204
+ try {
205
+ const stat = await this.fileHandle.stat();
206
+ return stat.size >= this.config.maxFileSize;
207
+ } catch {
208
+ return false;
209
+ }
210
+ }
211
+
212
+ private async rotate(): Promise<void> {
213
+ if (!this.config.filePath || !this.config.maxFiles) {
214
+ return;
215
+ }
216
+
217
+ // Close current file
218
+ if (this.fileHandle) {
219
+ await this.fileHandle.close();
220
+ delete this.fileHandle;
221
+ }
222
+
223
+ // Rename current file
224
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
225
+ const rotatedPath = `${this.config.filePath}.${timestamp}`;
226
+ await Deno.rename(this.config.filePath, rotatedPath);
227
+
228
+ // Clean up old files
229
+ await this.cleanupOldFiles();
230
+
231
+ // Reset file size
232
+ this.currentFileSize = 0;
233
+ }
234
+
235
+ private async cleanupOldFiles(): Promise<void> {
236
+ if (!this.config.filePath || !this.config.maxFiles) {
237
+ return;
238
+ }
239
+
240
+ const dir = this.config.filePath.substring(0, this.config.filePath.lastIndexOf('/'));
241
+ const baseFileName = this.config.filePath.substring(this.config.filePath.lastIndexOf('/') + 1);
242
+
243
+ const files: string[] = [];
244
+ for await (const entry of Deno.readDir(dir)) {
245
+ if (entry.isFile && entry.name.startsWith(baseFileName + '.')) {
246
+ files.push(entry.name);
247
+ }
248
+ }
249
+
250
+ // Sort files by timestamp (newest first)
251
+ files.sort().reverse();
252
+
253
+ // Remove old files
254
+ const filesToRemove = files.slice(this.config.maxFiles - 1);
255
+ for (const file of filesToRemove) {
256
+ await Deno.remove(`${dir}/${file}`);
257
+ }
258
+ }
259
+ }
260
+
261
+ // Export singleton instance
262
+ export const logger = Logger.getInstance();