trace.ai-cli 1.0.3 → 1.0.5

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.
package/cli/traceAI.js ADDED
@@ -0,0 +1,486 @@
1
+ const readline = require('readline');
2
+ const figlet = require('figlet');
3
+ const chalk = require('chalk');
4
+ const path = require('path');
5
+ const fs = require('fs').promises;
6
+ const { analyzeFile } = require('../services/fileAnalyzer');
7
+ const { analyzeFolderStructure } = require('../services/folderAnalyzer');
8
+ const { extractTextFromImage } = require('../services/imageService');
9
+ const { processWithAI } = require('../services/aiService');
10
+
11
+ class TraceAI {
12
+ constructor() {
13
+ this.rl = readline.createInterface({
14
+ input: process.stdin,
15
+ output: process.stdout,
16
+ prompt: ''
17
+ });
18
+ this.contexts = [];
19
+ this.sessionStartTime = new Date();
20
+ this.queryCount = 0;
21
+ }
22
+
23
+ // Enhanced startup with animated loading
24
+ async start() {
25
+ // Clear screen for clean start
26
+ console.clear();
27
+
28
+ // Animated startup sequence
29
+ await this.showLoadingAnimation();
30
+
31
+ // Main header with enhanced ASCII art
32
+ await this.displayHeader();
33
+
34
+ // Show welcome message and commands
35
+ this.displayWelcomeMessage();
36
+
37
+ // Start the interactive session
38
+ this.promptUser();
39
+ }
40
+
41
+ async showLoadingAnimation() {
42
+ const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
43
+ const messages = [
44
+ 'Initializing Trace.AI...',
45
+ 'Loading AI modules...',
46
+ 'Preparing analysis engines...',
47
+ 'Ready to trace!'
48
+ ];
49
+
50
+ for (let i = 0; i < messages.length; i++) {
51
+ for (let j = 0; j < 10; j++) {
52
+ process.stdout.write(`\r${chalk.blueBright(frames[j % frames.length])} ${chalk.white(messages[i])}`);
53
+ await new Promise(resolve => setTimeout(resolve, 80));
54
+ }
55
+ }
56
+ console.log(`\r${chalk.green('✓')} ${chalk.white('Trace.Ai initialized successfully!')}\n`);
57
+ await new Promise(resolve => setTimeout(resolve, 500));
58
+ }
59
+
60
+ async displayHeader() {
61
+ // Create enhanced ASCII art
62
+ const asciiArt = figlet.textSync('TRACE.AI', {
63
+ font: 'ANSI Shadow',
64
+ horizontalLayout: 'fitted',
65
+ verticalLayout: 'default'
66
+ });
67
+
68
+ // Enhanced header with gradient effect
69
+ console.log(chalk.bold.blueBright(asciiArt));
70
+
71
+ // Subtitle and version info
72
+ console.log(chalk.gray.bold(' ') + chalk.white.bold('AI powered CLI Platform'));
73
+ console.log(chalk.gray(' ') + chalk.gray('v1.0.4 | Powered by Mixkey'));
74
+
75
+ // Dynamic separator
76
+ const width = process.stdout.columns || 80;
77
+ console.log(chalk.blueBright('═'.repeat(Math.min(width, 80))));
78
+ }
79
+
80
+ displayWelcomeMessage() {
81
+ // Welcome section with better formatting
82
+ console.log(chalk.bold.green('\nWelcome to Trace.AI CLI'));
83
+ console.log();
84
+ // Enhanced command documentation
85
+ console.log(chalk.bold.cyan('📋 AVAILABLE COMMANDS'));
86
+ console.log(chalk.gray('━'.repeat(50)));
87
+
88
+ const commands = [
89
+ {
90
+ cmd: '/file <path> [question]',
91
+ desc: 'Analyze any code file with AI insights',
92
+ example: '/file "src/app.js" explain the main function',
93
+ icon: '📄'
94
+ },
95
+ {
96
+ cmd: '/folder <path> [question]',
97
+ desc: 'Analyze entire project structure',
98
+ example: '/folder "src/" what is the architecture?',
99
+ icon: '📁'
100
+ },
101
+ {
102
+ cmd: '/image <path> [question]',
103
+ desc: 'Analyze image content',
104
+ example: '/image "diagram.png" explain this flowchart',
105
+ icon: '🖼️'
106
+ },
107
+ {
108
+ cmd: '/context <text>',
109
+ desc: 'Add contextual information',
110
+ example: '/context This is a React project using TypeScript',
111
+ icon: '💭'
112
+ },
113
+ {
114
+ cmd: '/context-file <path>',
115
+ desc: 'Add file content as context',
116
+ example: '/context-file "README.md"',
117
+ icon: '📋'
118
+ },
119
+ {
120
+ cmd: '/view-context',
121
+ desc: 'Display all active contexts',
122
+ example: '/view-context',
123
+ icon: '👁️'
124
+ },
125
+ {
126
+ cmd: '/clear [type]',
127
+ desc: 'Clear contexts or screen',
128
+ example: '/clear or /clear context',
129
+ icon: '🧹'
130
+ },
131
+ {
132
+ cmd: '/help',
133
+ desc: 'Show detailed help information',
134
+ example: '/help',
135
+ icon: '❓'
136
+ },
137
+ {
138
+ cmd: '/stats',
139
+ desc: 'Show session statistics',
140
+ example: '/stats',
141
+ icon: '📊'
142
+ },
143
+ {
144
+ cmd: '/exit',
145
+ desc: 'Exit the application',
146
+ example: '/exit',
147
+ icon: '👋'
148
+ }
149
+ ];
150
+
151
+ commands.forEach(({ cmd, desc, icon }) => {
152
+ console.log(`${chalk.blueBright(icon)} ${chalk.bold.magenta(cmd.padEnd(25))} ${chalk.gray(desc)}`);
153
+ });
154
+
155
+ console.log(chalk.gray('\n' + '━'.repeat(50)));
156
+ // Quick start section
157
+ console.log(chalk.bold.cyan('⚡ QUICK START'));
158
+ console.log(chalk.gray('━'.repeat(20)));
159
+ console.log(chalk.white('• Type ') + chalk.cyan('/help') + chalk.white(' for detailed guidance'));
160
+ console.log(chalk.white('• Use ') + chalk.cyan('Tab') + chalk.white(' for auto-completion (when available)'));
161
+ console.log(chalk.white('• Start with ') + chalk.cyan('/file "your-file.js"') + chalk.white(' to analyze code'));
162
+ console.log(chalk.white('• Or just ask any question directly!'));
163
+
164
+ // Status bar
165
+ this.displayStatusBar();
166
+
167
+ console.log(chalk.gray('\n' + '─'.repeat(60)));
168
+
169
+ }
170
+
171
+ displayStatusBar() {
172
+ const uptime = Math.floor((new Date() - this.sessionStartTime) / 1000);
173
+ const contextCount = this.contexts.length;
174
+
175
+ console.log(chalk.gray('\n┌─ SESSION INFO ') + chalk.gray('─'.repeat(34)));
176
+ console.log(chalk.gray('│ ') + chalk.white('Uptime: ') + chalk.green(`${uptime}s`) +
177
+ chalk.gray(' │ ') + chalk.white('Contexts: ') + chalk.cyan(contextCount) +
178
+ chalk.gray(' │ ') + chalk.white('Queries: ') + chalk.blueBright(this.queryCount));
179
+ console.log(chalk.gray('└─') + chalk.gray('─'.repeat(48)));
180
+ }
181
+
182
+ promptUser() {
183
+ // Enhanced prompt with status indicators
184
+ const contextIndicator = this.contexts.length > 0 ? chalk.cyan(`[${this.contexts.length}]`) : '';
185
+ const prompt = `${chalk.bold.blueBright('Trace.Ai')} ${contextIndicator}${chalk.gray('>')} `;
186
+
187
+ this.rl.question(prompt, async (input) => {
188
+ try {
189
+ if (input.trim()) {
190
+ this.queryCount++;
191
+ await this.handleInput(input);
192
+ }
193
+ } catch (error) {
194
+ this.displayError(error.message);
195
+ }
196
+ this.promptUser();
197
+ });
198
+ }
199
+
200
+ async handleInput(input) {
201
+ const trimmedInput = input.trim();
202
+
203
+ if (!trimmedInput) return;
204
+
205
+ // Command routing with enhanced feedback
206
+ const commands = {
207
+ '/file': () => this.handleFileCommand(trimmedInput),
208
+ '/folder': () => this.handleFolderCommand(trimmedInput),
209
+ '/image': () => this.handleImageCommand(trimmedInput),
210
+ '/context ': () => this.handleContextCommand(trimmedInput),
211
+ '/context-file': () => this.handleContextFileCommand(trimmedInput),
212
+ '/view-context': () => this.displayContexts(),
213
+ '/clear': () => this.handleClearCommand(trimmedInput),
214
+ '/help': () => this.displayHelp(),
215
+ '/stats': () => this.displayStats(),
216
+ '/exit': () => this.close()
217
+ };
218
+
219
+ // Find and execute command
220
+ const commandKey = Object.keys(commands).find(cmd => trimmedInput.startsWith(cmd));
221
+
222
+ if (commandKey) {
223
+ await commands[commandKey]();
224
+ } else {
225
+ await this.handleTextQuery(trimmedInput);
226
+ }
227
+ }
228
+
229
+ async handleFileCommand(input) {
230
+ const parts = this.parseCommand(input);
231
+ if (!this.validateCommand(parts, 2, 'Usage: /file <file_path> [query]')) return;
232
+
233
+ const filePath = this.cleanPath(parts[1]);
234
+ const query = parts.slice(2).join(' ').replace(/^"|"$/g, '') || 'Analyze this file';
235
+
236
+ await this.executeWithSpinner(
237
+ `Analyzing file: ${path.basename(filePath)}`,
238
+ async () => {
239
+ const result = await analyzeFile(path.resolve(filePath), query);
240
+ this.displayResult('File Analysis', result.text, filePath);
241
+ }
242
+ );
243
+ }
244
+
245
+ async handleFolderCommand(input) {
246
+ const parts = this.parseCommand(input);
247
+ if (!this.validateCommand(parts, 2, 'Usage: /folder <folder_path> [query]')) return;
248
+
249
+ const folderPath = this.cleanPath(parts[1]);
250
+ const query = parts.slice(2).join(' ').replace(/^"|"$/g, '') || 'Analyze this folder structure';
251
+
252
+ await this.executeWithSpinner(
253
+ `Analyzing folder: ${path.basename(folderPath)}`,
254
+ async () => {
255
+ const result = await analyzeFolderStructure(path.resolve(folderPath), query);
256
+ this.displayResult('Folder Analysis', result.text, folderPath);
257
+ }
258
+ );
259
+ }
260
+
261
+ async handleImageCommand(input) {
262
+ const parts = this.parseCommand(input);
263
+ if (!this.validateCommand(parts, 2, 'Usage: /image <image_path> [query]')) return;
264
+
265
+ const imagePath = this.cleanPath(parts[1]);
266
+ const query = parts.slice(2).join(' ').replace(/^"|"$/g, '') || 'Describe this image';
267
+
268
+ await this.executeWithSpinner(
269
+ `Analyzing image: ${path.basename(imagePath)}`,
270
+ async () => {
271
+ const result = await extractTextFromImage(path.resolve(imagePath), query);
272
+ this.displayResult('Image Analysis', result, imagePath);
273
+ }
274
+ );
275
+ }
276
+
277
+ handleContextCommand(input) {
278
+ const context = input.substring('/context '.length).trim();
279
+ if (context) {
280
+ this.contexts.push({
281
+ type: 'text',
282
+ content: context,
283
+ timestamp: new Date().toLocaleTimeString()
284
+ });
285
+ console.log(chalk.green('✓ Context added successfully'));
286
+ } else {
287
+ console.log(chalk.cyan('⚠️ No context provided'));
288
+ }
289
+ }
290
+
291
+ async handleContextFileCommand(input) {
292
+ const filePath = input.substring('/context-file'.length).trim().replace(/^"|"$/g, '');
293
+ if (!filePath) {
294
+ console.log(chalk.cyan('⚠️ No file path provided'));
295
+ return;
296
+ }
297
+
298
+ try {
299
+ const resolvedPath = path.resolve(filePath);
300
+ const content = await fs.readFile(resolvedPath, 'utf8');
301
+ this.contexts.push({
302
+ type: 'file',
303
+ content: `File ${path.basename(resolvedPath)}:\n${content}`,
304
+ filename: path.basename(resolvedPath),
305
+ timestamp: new Date().toLocaleTimeString()
306
+ });
307
+ console.log(chalk.green(`✓ File ${path.basename(resolvedPath)} added to context`));
308
+ } catch (error) {
309
+ this.displayError(`Error reading file: ${error.message}`);
310
+ }
311
+ }
312
+
313
+ displayContexts() {
314
+ if (this.contexts.length === 0) {
315
+ console.log(chalk.cyan('📭 No active contexts'));
316
+ return;
317
+ }
318
+
319
+ console.log(chalk.bold.green('\n📋 ACTIVE CONTEXTS'));
320
+ console.log(chalk.gray('━'.repeat(50)));
321
+
322
+ this.contexts.forEach((ctx, index) => {
323
+ const icon = ctx.type === 'file' ? '📄' : '💭';
324
+ const preview = ctx.content.substring(0, 80).replace(/\n/g, ' ');
325
+ const truncated = ctx.content.length > 80 ? '...' : '';
326
+
327
+ console.log(`${chalk.blueBright(icon)} ${chalk.bold(`[${index + 1}]`)} ${chalk.gray(ctx.timestamp)}`);
328
+ console.log(` ${chalk.white(preview)}${chalk.gray(truncated)}`);
329
+ if (index < this.contexts.length - 1) console.log();
330
+ });
331
+ console.log(chalk.gray('━'.repeat(50)));
332
+ }
333
+
334
+ handleClearCommand(input) {
335
+ if (input === '/clear' || input === '/clear all') {
336
+ this.contexts = [];
337
+ console.log(chalk.green('✓ All contexts cleared'));
338
+ } else if (input === '/clear screen') {
339
+ console.clear();
340
+ this.displayHeader();
341
+ } else if (input === '/clear context') {
342
+ this.contexts = [];
343
+ console.log(chalk.green('✓ Context cleared'));
344
+ } else {
345
+ console.log(chalk.cyan('Usage: /clear [all|context|screen]'));
346
+ }
347
+ }
348
+
349
+ displayHelp() {
350
+
351
+ console.log(chalk.bold.green('\n📖 DETAILED HELP GUIDE'));
352
+ console.log(chalk.gray('━'.repeat(60)));
353
+
354
+ console.log(chalk.bold.cyan('\n🎯 GETTING STARTED'));
355
+ console.log('Trace.AI is an intelligent CLI tool that helps you understand and analyze files,');
356
+ console.log('folder structures, and images using advanced AI capabilities.\n');
357
+
358
+ console.log(chalk.bold.cyan('💡 TIPS FOR BETTER RESULTS'));
359
+ console.log('• Be specific in your questions');
360
+ console.log('• Use context to provide background information');
361
+ console.log('• Combine multiple commands for comprehensive analysis');
362
+ console.log('• File paths with spaces should be quoted\n');
363
+
364
+ console.log(chalk.bold.cyan('🔧 TROUBLESHOOTING'));
365
+ console.log('• Ensure file paths are correct and accessible');
366
+ console.log('• Check file permissions for read access');
367
+ console.log('• Use /stats to monitor your session');
368
+ console.log('• Use /clear screen to refresh the interface\n');
369
+
370
+ console.log(chalk.gray('Press any key to continue...'));
371
+
372
+ }
373
+
374
+ displayStats() {
375
+ const uptime = Math.floor((new Date() - this.sessionStartTime) / 1000);
376
+ const minutes = Math.floor(uptime / 60);
377
+ const seconds = uptime % 60;
378
+
379
+ console.log(chalk.bold.green('\n📊 SESSION STATISTICS'));
380
+ console.log(chalk.gray('━'.repeat(40)));
381
+ console.log(`${chalk.blueBright('🕐')} Session Duration: ${chalk.white(`${minutes}m ${seconds}s`)}`);
382
+ console.log(`${chalk.blueBright('💬')} Total Queries: ${chalk.white(this.queryCount)}`);
383
+ console.log(`${chalk.blueBright('📋')} Active Contexts: ${chalk.white(this.contexts.length)}`);
384
+ console.log(`${chalk.blueBright('🖥️')} Terminal Width: ${chalk.white(process.stdout.columns || 'Unknown')}`);
385
+ console.log(`${chalk.blueBright('📅')} Started: ${chalk.white(this.sessionStartTime.toLocaleString())}`);
386
+ console.log(chalk.gray('━'.repeat(40)));
387
+ }
388
+
389
+ async handleTextQuery(input) {
390
+ await this.executeWithSpinner(
391
+ 'Processing your query',
392
+ async () => {
393
+ const context = this.contexts.map(ctx => ctx.content).join('\n\n');
394
+ const result = await processWithAI(input, context);
395
+ this.displayResult('Trace.Ai Response', result);
396
+ }
397
+ );
398
+ }
399
+
400
+ // Utility methods
401
+ parseCommand(input) {
402
+ return input.match(/(?:[^\s"]+|"[^"]*")+/g) || [];
403
+ }
404
+
405
+ cleanPath(path) {
406
+ // First remove quotes from beginning and end
407
+ let cleanedPath = path.replace(/^"|"$/g, '');
408
+ // Then handle escaped spaces and other escaped characters
409
+ cleanedPath = cleanedPath.replace(/\\([ \(\)\[\]\{\}\&\^\%\$\#\@\!\,\.\;\:\'\"])/g, '$1');
410
+ return cleanedPath;
411
+ }
412
+
413
+ validateCommand(parts, minLength, usage) {
414
+ if (!parts || parts.length < minLength) {
415
+ console.log(chalk.cyan(`⚠️ ${usage}`));
416
+ return false;
417
+ }
418
+ return true;
419
+ }
420
+
421
+ async executeWithSpinner(message, task) {
422
+ const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
423
+ let frameIndex = 0;
424
+
425
+ const spinner = setInterval(() => {
426
+ process.stdout.write(`\r${chalk.blueBright(frames[frameIndex])} ${chalk.white(message)}`);
427
+ frameIndex = (frameIndex + 1) % frames.length;
428
+ }, 100);
429
+
430
+ try {
431
+ await task();
432
+ clearInterval(spinner);
433
+ process.stdout.write(`\r${chalk.green('✓')} ${chalk.white(message)} ${chalk.gray('- Complete!')}\n`);
434
+ } catch (error) {
435
+ clearInterval(spinner);
436
+ process.stdout.write(`\r${chalk.red('✗')} ${chalk.white(message)} ${chalk.gray('- Failed!')}\n`);
437
+ this.displayError(error.message);
438
+ }
439
+ }
440
+
441
+ displayResult(title, content, filePath = null) {
442
+ console.log(chalk.bold.blueBright(`\n${title.toUpperCase()}`));
443
+ if (filePath) {
444
+ console.log(chalk.gray(`📍 Source: ${filePath}`));
445
+ }
446
+ console.log(chalk.blueBright('═'.repeat(60)));
447
+ console.log(chalk.white(content));
448
+ console.log(chalk.blueBright('═'.repeat(60)));
449
+ console.log();
450
+ }
451
+
452
+ displayError(message) {
453
+ console.log(chalk.red(`\n❌ Error: ${message}\n`));
454
+ }
455
+
456
+ close() {
457
+
458
+ // Farewell message
459
+ const farewell = figlet.textSync('Goodbye!', {
460
+ font: 'Small',
461
+ horizontalLayout: 'fitted'
462
+ });
463
+
464
+ console.log(chalk.blueBright(farewell));
465
+ console.log(chalk.gray('━'.repeat(50)));
466
+ console.log(chalk.bold.green('Thank you for using Trace.AI!'));
467
+
468
+ // Session summary
469
+ const uptime = Math.floor((new Date() - this.sessionStartTime) / 1000);
470
+ const minutes = Math.floor(uptime / 60);
471
+ const seconds = uptime % 60;
472
+
473
+ console.log(chalk.gray(`Session Duration: ${minutes}m ${seconds}s`));
474
+ console.log(chalk.gray(`Total Queries: ${this.queryCount}`));
475
+ console.log(chalk.gray(`Contexts Used: ${this.contexts.length}`));
476
+
477
+ console.log(chalk.gray('━'.repeat(50)));
478
+ console.log(chalk.cyan('Visit us at: https://traceai.netlify.app'));
479
+ console.log(chalk.gray('Have a great day! 👋\n'));
480
+
481
+ this.rl.close();
482
+ process.exit(0);
483
+ }
484
+ }
485
+
486
+ module.exports = TraceAI;