trace.ai-cli 1.0.3 → 1.0.4

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/index.js CHANGED
@@ -1,644 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const fs = require('fs').promises;
4
- const path = require('path');
5
- const readline = require('readline');
6
- const fetch = require('node-fetch');
7
- const figlet = require('figlet');
8
- const chalk = require('chalk');
9
-
10
- // Core encryption/decryption logic from background.js
11
- const ENCRYPTION_KEY = 'AlzaSyCVKlzUxK';
12
-
13
- function encodeUnicode(str) {
14
- const utf8Bytes = new TextEncoder().encode(str);
15
- const base64 = Buffer.from(utf8Bytes).toString('base64');
16
- return base64;
17
- }
18
-
19
- function decodeUnicode(str) {
20
- const bytes = Buffer.from(str, 'base64');
21
- return new TextDecoder().decode(bytes);
22
- }
23
-
24
- function encryptData(data) {
25
- const jsonStr = JSON.stringify(data);
26
- const encoded = encodeUnicode(jsonStr);
27
- return encoded.split('').reverse().join('');
28
- }
29
-
30
- function decryptData(encoded) {
31
- const reversed = encoded.split('').reverse().join('');
32
- const decoded = decodeUnicode(reversed);
33
- return JSON.parse(decoded);
34
- }
35
-
36
- // File type detection
37
- function getFileType(filePath) {
38
- const ext = path.extname(filePath).toLowerCase();
39
- const codeExtensions = {
40
- '.js': 'JavaScript',
41
- '.jsx': 'React/JavaScript',
42
- '.ts': 'TypeScript',
43
- '.tsx': 'React/TypeScript',
44
- '.py': 'Python',
45
- '.java': 'Java',
46
- '.cpp': 'C++',
47
- '.c': 'C',
48
- '.cs': 'C#',
49
- '.php': 'PHP',
50
- '.rb': 'Ruby',
51
- '.go': 'Go',
52
- '.rs': 'Rust',
53
- '.swift': 'Swift',
54
- '.kt': 'Kotlin',
55
- '.scala': 'Scala',
56
- '.html': 'HTML',
57
- '.css': 'CSS',
58
- '.scss': 'SCSS',
59
- '.sass': 'SASS',
60
- '.less': 'LESS',
61
- '.vue': 'Vue.js',
62
- '.svelte': 'Svelte',
63
- '.json': 'JSON',
64
- '.xml': 'XML',
65
- '.yaml': 'YAML',
66
- '.yml': 'YAML',
67
- '.toml': 'TOML',
68
- '.ini': 'INI',
69
- '.conf': 'Config',
70
- '.md': 'Markdown',
71
- '.txt': 'Text',
72
- '.sql': 'SQL',
73
- '.sh': 'Shell Script',
74
- '.bash': 'Bash Script',
75
- '.zsh': 'Zsh Script',
76
- '.ps1': 'PowerShell',
77
- '.bat': 'Batch Script',
78
- '.dockerfile': 'Dockerfile',
79
- '.gitignore': 'Git Ignore',
80
- '.env': 'Environment Variables'
81
- };
82
-
83
- const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg'];
84
-
85
- if (imageExtensions.includes(ext)) {
86
- return 'image';
87
- }
88
-
89
- return codeExtensions[ext] || 'Unknown';
90
- }
91
-
92
- function getMimeType(filePath) {
93
- const ext = path.extname(filePath).toLowerCase();
94
- const mimeTypes = {
95
- '.jpg': 'image/jpeg',
96
- '.jpeg': 'image/jpeg',
97
- '.png': 'image/png',
98
- '.gif': 'image/gif',
99
- '.webp': 'image/webp',
100
- '.bmp': 'image/bmp',
101
- '.svg': 'image/svg+xml'
102
- };
103
- return mimeTypes[ext] || 'image/jpeg';
104
- }
105
-
106
- // File reading and processing
107
- async function readFileContent(filePath) {
108
- try {
109
- const stats = await fs.stat(filePath);
110
- if (stats.isDirectory()) {
111
- throw new Error('Path is a directory, not a file');
112
- }
113
-
114
- // Check file size (limit to 1MB for safety)
115
- if (stats.size > 1024 * 1024) {
116
- throw new Error('File too large. Maximum size is 1MB');
117
- }
118
-
119
- const content = await fs.readFile(filePath, 'utf8');
120
- return content;
121
- } catch (error) {
122
- if (error.code === 'ENOENT') {
123
- throw new Error(`File not found: ${filePath}`);
124
- }
125
- throw error;
126
- }
127
- }
128
-
129
- function buildSimpleFileTree(folderPath, items, statsCache, depth = 0) {
130
- let tree = '';
131
- const indent = ' '.repeat(depth);
132
-
133
- for (const item of items) {
134
- const itemPath = path.join(folderPath, item);
135
- const stats = statsCache.get(itemPath);
136
- tree += `${indent}${item}${stats.isDirectory() ? '/' : ''}\n`;
137
-
138
- if (stats.isDirectory()) {
139
- try {
140
- const subItems = fs.readdirSync(itemPath);
141
- const subStatsCache = new Map();
142
- for (const subItem of subItems) {
143
- const subItemPath = path.join(itemPath, subItem);
144
- subStatsCache.set(subItemPath, fs.statSync(subItemPath));
145
- }
146
- tree += buildSimpleFileTree(itemPath, subItems, subStatsCache, depth + 1);
147
- } catch (error) {
148
- tree += `${indent} (error accessing directory)\n`;
149
- }
150
- }
151
- }
152
-
153
- return tree;
154
- }
155
-
156
- async function analyzeFolder(folderPath, maxDepth = 2, currentDepth = 0) {
157
- try {
158
- const stats = await fs.stat(folderPath);
159
- if (!stats.isDirectory()) {
160
- throw new Error('Path is not a directory');
161
- }
162
-
163
- const analysis = {
164
- path: folderPath,
165
- files: [],
166
- folders: [],
167
- fileTree: '', // Store simple file tree
168
- summary: {
169
- totalFiles: 0,
170
- totalFolders: 0,
171
- fileTypes: {},
172
- languages: {},
173
- size: 0
174
- }
175
- };
176
-
177
- const items = await fs.readdir(folderPath);
178
- const statsCache = new Map();
179
-
180
- // Cache stats for all items
181
- for (const item of items) {
182
- const itemPath = path.join(folderPath, item);
183
- statsCache.set(itemPath, await fs.stat(itemPath));
184
- }
185
-
186
- // Build simple file tree
187
- analysis.fileTree = buildSimpleFileTree(folderPath, items, statsCache);
188
-
189
- for (const item of items) {
190
- const itemPath = path.join(folderPath, item);
191
- const itemStats = statsCache.get(itemPath);
192
-
193
- if (itemStats.isDirectory()) {
194
- analysis.folders.push(item);
195
- analysis.summary.totalFolders++;
196
-
197
- if (currentDepth < maxDepth) {
198
- try {
199
- const subAnalysis = await analyzeFolder(itemPath, maxDepth, currentDepth + 1);
200
- analysis.summary.totalFiles += subAnalysis.summary.totalFiles;
201
- analysis.summary.totalFolders += subAnalysis.summary.totalFolders;
202
- analysis.summary.size += subAnalysis.summary.size;
203
-
204
- Object.keys(subAnalysis.summary.fileTypes).forEach(type => {
205
- analysis.summary.fileTypes[type] = (analysis.summary.fileTypes[type] || 0) + subAnalysis.summary.fileTypes[type];
206
- });
207
- Object.keys(subAnalysis.summary.languages).forEach(lang => {
208
- analysis.summary.languages[lang] = (analysis.summary.languages[lang] || 0) + subAnalysis.summary.languages[lang];
209
- });
210
- } catch (error) {
211
- console.warn(`Warning: Cannot access folder ${itemPath}`);
212
- }
213
- }
214
- } else {
215
- const fileType = getFileType(itemPath);
216
- analysis.files.push({
217
- name: item,
218
- type: fileType,
219
- size: itemStats.size,
220
- modified: itemStats.mtime
221
- });
222
-
223
- analysis.summary.totalFiles++;
224
- analysis.summary.size += itemStats.size;
225
- analysis.summary.fileTypes[fileType] = (analysis.summary.fileTypes[fileType] || 0) + 1;
226
-
227
- if (fileType !== 'image' && fileType !== 'Unknown') {
228
- analysis.summary.languages[fileType] = (analysis.summary.languages[fileType] || 0) + 1;
229
- }
230
- }
231
- }
232
-
233
- return analysis;
234
- } catch (error) {
235
- if (error.code === 'ENOENT') {
236
- throw new Error(`Folder not found: ${folderPath}`);
237
- }
238
- if (error.code === 'EACCES') {
239
- throw new Error(`Access denied: ${folderPath}`);
240
- }
241
- throw error;
242
- }
243
- }
244
-
245
- // Image processing functions
246
- async function convertImageToBase64(imagePath) {
247
- try {
248
- const imageBuffer = await fs.readFile(imagePath);
249
- const base64 = imageBuffer.toString('base64');
250
- const mimeType = getMimeType(imagePath);
251
- return { base64, mimeType };
252
- } catch (error) {
253
- throw new Error(`Failed to read image: ${error.message}`);
254
- }
255
- }
256
-
257
- async function extractTextFromImage(imagePath, question = '') {
258
- try {
259
- console.log('Processing...');
260
- const { base64, mimeType } = await convertImageToBase64(imagePath);
261
- const visionModels = ['kimi', 'mvrk', 'gma3', 'qvl72', 'llama-vision'];
262
-
263
- const modelRequests = visionModels.map(model =>
264
- fetch('https://traceai.dukeindustries7.workers.dev/', {
265
- method: 'POST',
266
- headers: { 'Content-Type': 'application/json' },
267
- body: encryptData({
268
- a: model,
269
- q: 'Extract and return only the text from this image. Format it naturally.',
270
- r: [],
271
- i: [{ inlineData: { data: base64, mimeType } }],
272
- c: ''
273
- })
274
- })
275
- .then(res => res.text())
276
- .then(decryptData)
277
- );
278
-
279
- const responses = await Promise.all(modelRequests);
280
- const responseTexts = responses.map(r => r.text);
281
-
282
- const finalResponse = await fetch('https://traceai.dukeindustries7.workers.dev/', {
283
- method: 'POST',
284
- headers: { 'Content-Type': 'application/json' },
285
- body: encryptData({
286
- a: 'gfinal',
287
- q: question ?
288
- `First extract and combine the text from this image, then answer this question about the extracted text: ${question}` :
289
- 'Combine these extracted texts from an image into a single, coherent, and naturally formatted text. Remove duplicates and ensure clarity.',
290
- r: responseTexts,
291
- i: [{ inlineData: { data: base64, mimeType } }],
292
- c: ''
293
- })
294
- });
295
-
296
- const encryptedResult = await finalResponse.text();
297
- const decryptedResult = decryptData(encryptedResult);
298
- return decryptedResult.text || 'No text extracted from image'
299
- } catch (error) {
300
- console.error('❌ Error extracting text from image:', error);
301
- throw error;
302
- }
303
- }
304
-
305
- async function processWithAI(prompt, context = '') {
306
- try {
307
- console.log('Processing...');
308
- const models = ['kimi', 'mvrk', 'gma3', 'dsv3', 'qw32b', 'ms24b', 'll70b', 'qw3', 'mp4', 'nlm3'];
309
-
310
- const modelRequests = models.map(model =>
311
- fetch('https://traceai.dukeindustries7.workers.dev/', {
312
- method: 'POST',
313
- headers: { 'Content-Type': 'application/json' },
314
- body: encryptData({
315
- a: model,
316
- q: prompt,
317
- r: [],
318
- i: [],
319
- c: context
320
- })
321
- })
322
- .then(res => res.text())
323
- .then(decryptData)
324
- );
325
-
326
- const responses = await Promise.all(modelRequests);
327
- const responseTexts = responses.map(r => r.text);
328
-
329
- const finalResponse = await fetch('https://traceai.dukeindustries7.workers.dev/', {
330
- method: 'POST',
331
- headers: { 'Content-Type': 'application/json' },
332
- body: encryptData({
333
- a: 'gfinal',
334
- q: prompt,
335
- r: responseTexts.filter(text => text && typeof text === 'string'),
336
- i: [],
337
- c: context
338
- })
339
- });
340
-
341
- const encryptedResult = await finalResponse.text();
342
- const decryptedResult = decryptData(encryptedResult);
343
- return decryptedResult.text || 'No response generated';
344
- } catch (error) {
345
- console.error('❌ Processing error:', error.message);
346
- throw error;
347
- }
348
- }
349
-
350
- // File and folder analysis functions
351
- async function analyzeFile(filePath, query = '') {
352
- try {
353
- const fileType = getFileType(filePath);
354
-
355
- if (fileType === 'image') {
356
- return await extractTextFromImage(filePath);
357
- }
358
-
359
- const content = await readFileContent(filePath);
360
- const fileName = path.basename(filePath);
361
-
362
- let prompt;
363
- if (query) {
364
- prompt = `File: ${fileName} (${fileType})
365
- Content:
366
- \`\`\`
367
- ${content}
368
- \`\`\`
369
-
370
- User Question: ${query}`;
371
- } else {
372
- if (fileType !== 'Unknown' && fileType !== 'Text' && fileType !== 'Markdown') {
373
- prompt = `Analyze this ${fileType} code file (${fileName}):
374
-
375
- \`\`\`
376
- ${content}
377
- \`\`\`
378
-
379
- Please provide:
380
- 1. Code overview and purpose
381
- 2. Key functions/components
382
- 3. Potential issues or improvements
383
- 4. Code quality assessment
384
- 5. Suggestions for optimization`;
385
- } else {
386
- prompt = `Analyze this file content (${fileName}):
387
-
388
- \`\`\`
389
- ${content}
390
- \`\`\`
391
-
392
- Please provide a summary and analysis of the content.`;
393
- }
394
- }
395
-
396
- const result = await processWithAI(prompt);
397
- return { text: result || 'No response generated' }; // Ensure consistent return format
398
- } catch (error) {
399
- throw error;
400
- }
401
- }
402
-
403
- async function analyzeFolderStructure(folderPath, query = '') {
404
- try {
405
- const analysis = await analyzeFolder(folderPath);
406
-
407
- const structureText = `
408
- Folder: ${analysis.path}
409
- Files: ${analysis.summary.totalFiles}
410
- Folders: ${analysis.summary.totalFolders}
411
- Total Size: ${(analysis.summary.size / 1024).toFixed(2)} KB
412
-
413
- Files and Directories:
414
- ${analysis.fileTree}
415
-
416
- File Types:
417
- ${Object.entries(analysis.summary.fileTypes).map(([type, count]) => `- ${type}: ${count}`).join('\n')}
418
-
419
- Languages/Technologies:
420
- ${Object.entries(analysis.summary.languages).map(([lang, count]) => `- ${lang}: ${count} files`).join('\n')}
421
- `;
422
-
423
- let prompt;
424
- if (query) {
425
- prompt = `Project Structure:
426
- ${structureText}
427
-
428
- User Question: ${query}`;
429
- } else {
430
- prompt = `Analyze this project structure:
431
- ${structureText}
432
-
433
- Provide a brief overview of the project based on its file structure.`;
434
- }
435
-
436
- const result = await processWithAI(prompt);
437
- return { text: result || 'No response generated' }; // Ensure text property is always defined
438
- } catch (error) {
439
- throw error;
440
- }
441
- }
442
- // CLI Interface
443
- class TraceAI {
444
- constructor() {
445
- this.rl = readline.createInterface({
446
- input: process.stdin,
447
- output: process.stdout
448
- });
449
- this.contexts = {
450
- text: '',
451
- files: new Map()
452
- };
453
- }
454
-
455
- async start() {
456
- // Create ASCII art for "Trace.A"
457
- const asciiArt = figlet.textSync('Trace.Ai', {
458
- font: 'Big',
459
- horizontalLayout: 'default',
460
- verticalLayout: 'default'
461
- });
462
-
463
- console.log(chalk.cyan(asciiArt));
464
- console.log(chalk.gray('═'.repeat(60)));
465
- console.log(chalk.bold.green('Welcome to Trace.Ai CLI - AI powered CLI tool!'));
466
- console.log(chalk.gray('═'.repeat(60)));
467
-
468
- console.log(chalk.bold.yellow('\n📋 Commands:'));
469
- console.log(chalk.magenta(' /file "path" question ') + chalk.gray('- Analyze a file with optional question'));
470
- console.log(chalk.magenta(' /folder "path" question ') + chalk.gray('- Analyze a folder structure with optional question'));
471
- console.log(chalk.magenta(' /image "path" question ') + chalk.gray('- Analyze an image'));
472
- console.log(chalk.magenta(' /context <text> ') + chalk.gray('- Set text context for conversations'));
473
- console.log(chalk.magenta(' /context-file "path" ') + chalk.gray('- Add file context'));
474
- console.log(chalk.magenta(' /view-context ') + chalk.gray('- View all contexts'));
475
- console.log(chalk.magenta(' /clear [text|file "path"] ') + chalk.gray('- Clear specific or all contexts'));
476
- console.log(chalk.magenta(' /exit ') + chalk.gray('- Exit the application'));
477
- console.log(chalk.magenta(' Or just type your question directly'));
478
-
479
- console.log(chalk.bold.blue('\n💡 Examples:'));
480
- console.log(chalk.magenta(' /file "path/app.js" ') + chalk.italic('explain this code in simple terms'));
481
- console.log(chalk.magenta(' /image "path/image.png" ') + chalk.italic('what is this image about?'));
482
- console.log(chalk.magenta(' /folder "path/src" ') + chalk.italic('what is the purpose of this project?'));
483
- console.log(chalk.gray('\n' + '─'.repeat(60)));
484
- console.log(chalk.green('Ready to assist! Type your command or question below:'));
485
- console.log();
486
-
487
- this.promptUser();
488
- }
489
-
490
- promptUser() {
491
- this.rl.question('Trace.Ai> ', async (input) => {
492
- await this.handleInput(input.trim());
493
- this.promptUser();
494
- });
495
- }
496
-
497
- async handleInput(input) {
498
- if (!input) return;
499
-
500
- try {
501
- if (input.startsWith('/file ')) {
502
- await this.handleFileCommand(input.substring(6).trim());
503
- } else if (input.startsWith('/folder ')) {
504
- await this.handleFolderCommand(input.substring(8).trim());
505
- } else if (input.startsWith('/image ')) {
506
- const parts = input.substring(7).trim().match(/(?:"[^"]*"|[^\s"]+)+/g) || [];
507
- const imagePath = parts[0]?.replace(/\\/g, '').replace(/^"|"$/g, '');
508
- const question = parts.slice(1).join(' ').replace(/^"|"$/g, '');
509
- await this.handleImageCommand(imagePath, question);
510
- } else if (input.startsWith('/context ')) {
511
- const context = input.substring(9).trim();
512
- this.contexts.text = context;
513
- console.log('✅ Text context set successfully');
514
- } else if (input.startsWith('/context-file ')) {
515
- const filePath = input.substring(13).trim().replace(/\\/g, '').replace(/^"|"$/g, '');
516
- try {
517
- const fileContent = await fs.readFile(filePath, 'utf8');
518
- this.contexts.files.set(filePath, fileContent);
519
- console.log(`✅ File context added: ${filePath}`);
520
- } catch (error) {
521
- console.error(`❌ Error reading file: ${error.message}`);
522
- }
523
- } else if (input === '/view-context') {
524
- console.log('\nCurrent Contexts:');
525
- console.log('Text Context:', this.contexts.text || '(none)');
526
- console.log('\nFile Contexts:');
527
- if (this.contexts.files.size === 0) {
528
- console.log('(none)');
529
- } else {
530
- for (const [path] of this.contexts.files) {
531
- console.log(`- ${path}`);
532
- }
533
- }
534
- } else if (input.startsWith('/clear')) {
535
- const parts = input.match(/(?:"[^"]*"|[^\s"]+)+/g) || [];
536
- if (parts.length === 1) {
537
- this.contexts.text = '';
538
- this.contexts.files.clear();
539
- console.log('✅ All contexts cleared');
540
- } else if (parts[1] === 'text') {
541
- this.contexts.text = '';
542
- console.log('✅ Text context cleared');
543
- } else if (parts[1] === 'file' && parts[2]) {
544
- const filePath = parts[2].replace(/\\/g, '').replace(/^"|"$/g, '');
545
- if (this.contexts.files.delete(filePath)) {
546
- console.log(`✅ File context cleared: ${filePath}`);
547
- } else {
548
- console.log(`❌ No such file context: ${filePath}`);
549
- }
550
- } else {
551
- console.log('❌ Invalid clear command. Use: /clear [text|file <path>]');
552
- }
553
- } else if (input === '/exit') {
554
- console.log('👋 Goodbye!');
555
- process.exit(0);
556
- } else {
557
- await this.handleTextQuery(input);
558
- }
559
- } catch (error) {
560
- console.error('❌ Error:', error.message);
561
- }
562
- }
563
-
564
- async handleFileCommand(input) {
565
- // Use regex to split input, preserving quoted strings
566
- const parts = input.match(/(?:"[^"]*"|[^\s"]+)+/g) || [];
567
- const filePath = parts[0]?.replace(/\\/g, '').replace(/^"|"$/g, '');
568
- const query = parts.slice(1).join(' ').replace(/^"|"$/g, '');
569
-
570
- if (!filePath) {
571
- console.error('❌ Error: No file path provided');
572
- return;
573
- }
574
-
575
- try {
576
- const result = await analyzeFile(filePath, query);
577
- console.log('\n📄 File Analysis:');
578
- console.log('=' .repeat(50));
579
- console.log(result.text || result); // Handle both object and string responses
580
- console.log('=' .repeat(50) + '\n');
581
- } catch (error) {
582
- console.error('❌ Error analyzing file:', error.message);
583
- }
584
- }
585
-
586
- async handleFolderCommand(input) {
587
- const parts = input.split(/\s+(?=(?:[^"]*"[^"]*")*[^"]*$)/);
588
- const folderPath = parts[0].replace(/\\/g, '').replace(/^"|"$/g, '');
589
- const query = parts.slice(1).join(' ').replace(/^"|"$/g, '');
590
-
591
- try {
592
- const result = await analyzeFolderStructure(folderPath, query);
593
- console.log('\n📁 Folder Analysis:');
594
- console.log('=' .repeat(50));
595
- console.log(result.text);
596
- console.log('=' .repeat(50) + '\n');
597
- } catch (error) {
598
- console.error('❌ Error analyzing folder:', error.message);
599
- }
600
- }
601
-
602
- async handleImageCommand(imagePath, question = '') {
603
- try {
604
- await fs.access(imagePath);
605
- const result = await extractTextFromImage(imagePath, question);
606
- console.log('\n🖼️ Image Analysis:');
607
- console.log('=' .repeat(50));
608
- console.log(result);
609
- console.log('=' .repeat(50) + '\n');
610
- } catch (error) {
611
- if (error.code === 'ENOENT') {
612
- console.error('❌ Image file not found:', imagePath);
613
- } else {
614
- console.error('❌ Error processing image:', error.message);
615
- }
616
- }
617
- }
618
-
619
- async handleTextQuery(query) {
620
- try {
621
- let combinedContext = this.contexts.text;
622
- if (this.contexts.files.size > 0) {
623
- combinedContext += '\n\nFile Contexts:\n';
624
- for (const [path, content] of this.contexts.files) {
625
- combinedContext += `\nFile: ${path}\n${content}\n`;
626
- }
627
- }
628
- const result = await processWithAI(query, combinedContext);
629
- console.log('\nTrace.Ai Response:');
630
- console.log('=' .repeat(50));
631
- console.log(result);
632
- console.log('=' .repeat(50) + '\n');
633
- } catch (error) {
634
- console.error('❌ Error processing query:', error.message);
635
- }
636
- }
637
-
638
- close() {
639
- this.rl.close();
640
- }
641
- }
3
+ const TraceAI = require('./cli/traceAI');
4
+ const { analyzeFile } = require('./services/fileAnalyzer');
5
+ const { analyzeFolderStructure } = require('./services/folderAnalyzer');
6
+ const { extractTextFromImage } = require('./services/imageService');
7
+ const { processWithAI } = require('./services/aiService');
642
8
 
643
9
  // Handle CLI arguments
644
10
  async function handleCliArgs() {
@@ -661,7 +27,7 @@ async function handleCliArgs() {
661
27
  const result = await analyzeFile(filePath, query);
662
28
  console.log('\n📄 File Analysis:');
663
29
  console.log('=' .repeat(50));
664
- console.log(result);
30
+ console.log(result.text || result);
665
31
  console.log('=' .repeat(50));
666
32
  } catch (error) {
667
33
  console.error('❌ Error:', error.message);
@@ -676,7 +42,7 @@ async function handleCliArgs() {
676
42
  const result = await analyzeFolderStructure(folderPath, query);
677
43
  console.log('\n📁 Folder Analysis:');
678
44
  console.log('=' .repeat(50));
679
- console.log(result);
45
+ console.log(result.text);
680
46
  console.log('=' .repeat(50));
681
47
  } catch (error) {
682
48
  console.error('❌ Error:', error.message);
@@ -751,4 +117,10 @@ if (require.main === module) {
751
117
  handleCliArgs().catch(console.error);
752
118
  }
753
119
 
754
- module.exports = { TraceAI, processWithAI, extractTextFromImage, analyzeFile, analyzeFolderStructure };
120
+ module.exports = {
121
+ TraceAI,
122
+ processWithAI,
123
+ extractTextFromImage,
124
+ analyzeFile,
125
+ analyzeFolderStructure
126
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trace.ai-cli",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "A powerful AI-powered CLI tool",
5
5
  "main": "index.js",
6
6
  "bin": {