trace.ai-cli 1.1.9 → 1.2.1
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 +40 -40
- package/package.json +4 -4
- package/services/aiService.js +188 -213
- package/services/fileAnalyzer.js +135 -60
- package/utils/fileUtils.js +105 -100
package/services/fileAnalyzer.js
CHANGED
|
@@ -1,60 +1,135 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
const {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
};
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs').promises;
|
|
3
|
+
const pdfParse = require('pdf-parse');
|
|
4
|
+
const { getFileType, readFileContent } = require('../utils/fileUtils');
|
|
5
|
+
const { processWithAI } = require('./aiService');
|
|
6
|
+
const { extractTextFromImage } = require('./imageService');
|
|
7
|
+
|
|
8
|
+
async function analyzeFile(filePath, query = '', mode = 2) {
|
|
9
|
+
try {
|
|
10
|
+
const fileType = getFileType(filePath);
|
|
11
|
+
|
|
12
|
+
// Handle images - use vision API only for images
|
|
13
|
+
if (fileType === 'image') {
|
|
14
|
+
const result = await extractTextFromImage(filePath, query, mode);
|
|
15
|
+
return { text: result };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Handle documents (PDFs, Word, etc.)
|
|
19
|
+
if (fileType === 'document') {
|
|
20
|
+
const fileName = path.basename(filePath);
|
|
21
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
22
|
+
|
|
23
|
+
// Extract text from PDF
|
|
24
|
+
if (ext === '.pdf') {
|
|
25
|
+
try {
|
|
26
|
+
const dataBuffer = await fs.readFile(filePath);
|
|
27
|
+
const pdfData = await pdfParse(dataBuffer);
|
|
28
|
+
const content = pdfData.text;
|
|
29
|
+
|
|
30
|
+
if (!content || content.trim().length === 0) {
|
|
31
|
+
return {
|
|
32
|
+
text: `❌ **Unable to Extract Text from PDF**\n\n` +
|
|
33
|
+
`The PDF "${fileName}" appears to be empty or contains only images/scanned content.\n\n` +
|
|
34
|
+
`**Try using the /image command for scanned PDFs:**\n` +
|
|
35
|
+
`\`/image "${filePath}" ${query || 'analyze this document'}\``
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Build prompt for AI analysis
|
|
40
|
+
let prompt;
|
|
41
|
+
if (query) {
|
|
42
|
+
prompt = `PDF Document: ${fileName}
|
|
43
|
+
Content:
|
|
44
|
+
\`\`\`
|
|
45
|
+
${content}
|
|
46
|
+
\`\`\`
|
|
47
|
+
|
|
48
|
+
User Question: ${query}`;
|
|
49
|
+
} else {
|
|
50
|
+
prompt = `Analyze this PDF document (${fileName}):
|
|
51
|
+
|
|
52
|
+
\`\`\`
|
|
53
|
+
${content}
|
|
54
|
+
\`\`\`
|
|
55
|
+
|
|
56
|
+
Please provide a comprehensive summary and analysis of the document.`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const result = await processWithAI(prompt, '', mode);
|
|
60
|
+
return { text: result || 'No response generated' };
|
|
61
|
+
|
|
62
|
+
} catch (pdfError) {
|
|
63
|
+
return {
|
|
64
|
+
text: `❌ **Error Reading PDF**\n\n` +
|
|
65
|
+
`Failed to extract text from "${fileName}": ${pdfError.message}\n\n` +
|
|
66
|
+
`**Possible reasons:**\n` +
|
|
67
|
+
`- The PDF is corrupted or password-protected\n` +
|
|
68
|
+
`- The PDF contains only images (scanned document)\n\n` +
|
|
69
|
+
`**Try using /image command for scanned PDFs:**\n` +
|
|
70
|
+
`\`/image "${filePath}" ${query || 'analyze this document'}\``
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// For other document types (Word, Excel, etc.)
|
|
76
|
+
return {
|
|
77
|
+
text: `❌ **Document Type Not Supported**\n\n` +
|
|
78
|
+
`The file "${fileName}" is a ${ext.toUpperCase()} document.\n\n` +
|
|
79
|
+
`**Supported formats:**\n` +
|
|
80
|
+
`- PDF files (.pdf) - Text extraction supported\n` +
|
|
81
|
+
`- Text files (.txt, .md, etc.)\n` +
|
|
82
|
+
`- Code files (.js, .py, .java, etc.)\n\n` +
|
|
83
|
+
`**For ${ext.toUpperCase()} files:**\n` +
|
|
84
|
+
`Please convert to PDF or text format first.`
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Handle text-based files
|
|
89
|
+
const content = await readFileContent(filePath);
|
|
90
|
+
const fileName = path.basename(filePath);
|
|
91
|
+
|
|
92
|
+
let prompt;
|
|
93
|
+
if (query) {
|
|
94
|
+
prompt = `File: ${fileName} (${fileType})
|
|
95
|
+
Content:
|
|
96
|
+
\`\`\`
|
|
97
|
+
${content}
|
|
98
|
+
\`\`\`
|
|
99
|
+
|
|
100
|
+
User Question: ${query}`;
|
|
101
|
+
} else {
|
|
102
|
+
if (fileType !== 'Unknown' && fileType !== 'Text' && fileType !== 'Markdown') {
|
|
103
|
+
prompt = `Analyze this ${fileType} code file (${fileName}):
|
|
104
|
+
|
|
105
|
+
\`\`\`
|
|
106
|
+
${content}
|
|
107
|
+
\`\`\`
|
|
108
|
+
|
|
109
|
+
Please provide:
|
|
110
|
+
1. Code overview and purpose
|
|
111
|
+
2. Key functions/components
|
|
112
|
+
3. Potential issues or improvements
|
|
113
|
+
4. Code quality assessment
|
|
114
|
+
5. Suggestions for optimization`;
|
|
115
|
+
} else {
|
|
116
|
+
prompt = `Analyze this file content (${fileName}):
|
|
117
|
+
|
|
118
|
+
\`\`\`
|
|
119
|
+
${content}
|
|
120
|
+
\`\`\`
|
|
121
|
+
|
|
122
|
+
Please provide a summary and analysis of the content.`;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const result = await processWithAI(prompt, '', mode);
|
|
127
|
+
return { text: result || 'No response generated' };
|
|
128
|
+
} catch (error) {
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
module.exports = {
|
|
134
|
+
analyzeFile
|
|
135
|
+
};
|
package/utils/fileUtils.js
CHANGED
|
@@ -1,101 +1,106 @@
|
|
|
1
|
-
const fs = require('fs').promises;
|
|
2
|
-
const path = require('path');
|
|
3
|
-
|
|
4
|
-
// File type detection
|
|
5
|
-
function getFileType(filePath) {
|
|
6
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
7
|
-
const codeExtensions = {
|
|
8
|
-
'.js': 'JavaScript',
|
|
9
|
-
'.jsx': 'React/JavaScript',
|
|
10
|
-
'.ts': 'TypeScript',
|
|
11
|
-
'.tsx': 'React/TypeScript',
|
|
12
|
-
'.py': 'Python',
|
|
13
|
-
'.java': 'Java',
|
|
14
|
-
'.cpp': 'C++',
|
|
15
|
-
'.c': 'C',
|
|
16
|
-
'.cs': 'C#',
|
|
17
|
-
'.php': 'PHP',
|
|
18
|
-
'.rb': 'Ruby',
|
|
19
|
-
'.go': 'Go',
|
|
20
|
-
'.rs': 'Rust',
|
|
21
|
-
'.swift': 'Swift',
|
|
22
|
-
'.kt': 'Kotlin',
|
|
23
|
-
'.scala': 'Scala',
|
|
24
|
-
'.html': 'HTML',
|
|
25
|
-
'.css': 'CSS',
|
|
26
|
-
'.scss': 'SCSS',
|
|
27
|
-
'.sass': 'SASS',
|
|
28
|
-
'.less': 'LESS',
|
|
29
|
-
'.vue': 'Vue.js',
|
|
30
|
-
'.svelte': 'Svelte',
|
|
31
|
-
'.json': 'JSON',
|
|
32
|
-
'.xml': 'XML',
|
|
33
|
-
'.yaml': 'YAML',
|
|
34
|
-
'.yml': 'YAML',
|
|
35
|
-
'.toml': 'TOML',
|
|
36
|
-
'.ini': 'INI',
|
|
37
|
-
'.conf': 'Config',
|
|
38
|
-
'.md': 'Markdown',
|
|
39
|
-
'.txt': 'Text',
|
|
40
|
-
'.sql': 'SQL',
|
|
41
|
-
'.sh': 'Shell Script',
|
|
42
|
-
'.bash': 'Bash Script',
|
|
43
|
-
'.zsh': 'Zsh Script',
|
|
44
|
-
'.ps1': 'PowerShell',
|
|
45
|
-
'.
|
|
46
|
-
'.
|
|
47
|
-
'.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
if (imageExtensions.includes(ext)) {
|
|
54
|
-
return 'image';
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
'.
|
|
68
|
-
'.
|
|
69
|
-
'.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (stats.
|
|
84
|
-
throw new Error('
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
1
|
+
const fs = require('fs').promises;
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
// File type detection
|
|
5
|
+
function getFileType(filePath) {
|
|
6
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
7
|
+
const codeExtensions = {
|
|
8
|
+
'.js': 'JavaScript',
|
|
9
|
+
'.jsx': 'React/JavaScript',
|
|
10
|
+
'.ts': 'TypeScript',
|
|
11
|
+
'.tsx': 'React/TypeScript',
|
|
12
|
+
'.py': 'Python',
|
|
13
|
+
'.java': 'Java',
|
|
14
|
+
'.cpp': 'C++',
|
|
15
|
+
'.c': 'C',
|
|
16
|
+
'.cs': 'C#',
|
|
17
|
+
'.php': 'PHP',
|
|
18
|
+
'.rb': 'Ruby',
|
|
19
|
+
'.go': 'Go',
|
|
20
|
+
'.rs': 'Rust',
|
|
21
|
+
'.swift': 'Swift',
|
|
22
|
+
'.kt': 'Kotlin',
|
|
23
|
+
'.scala': 'Scala',
|
|
24
|
+
'.html': 'HTML',
|
|
25
|
+
'.css': 'CSS',
|
|
26
|
+
'.scss': 'SCSS',
|
|
27
|
+
'.sass': 'SASS',
|
|
28
|
+
'.less': 'LESS',
|
|
29
|
+
'.vue': 'Vue.js',
|
|
30
|
+
'.svelte': 'Svelte',
|
|
31
|
+
'.json': 'JSON',
|
|
32
|
+
'.xml': 'XML',
|
|
33
|
+
'.yaml': 'YAML',
|
|
34
|
+
'.yml': 'YAML',
|
|
35
|
+
'.toml': 'TOML',
|
|
36
|
+
'.ini': 'INI',
|
|
37
|
+
'.conf': 'Config',
|
|
38
|
+
'.md': 'Markdown',
|
|
39
|
+
'.txt': 'Text',
|
|
40
|
+
'.sql': 'SQL',
|
|
41
|
+
'.sh': 'Shell Script',
|
|
42
|
+
'.bash': 'Bash Script',
|
|
43
|
+
'.zsh': 'Zsh Script',
|
|
44
|
+
'.ps1': 'PowerShell',
|
|
45
|
+
'.dockerfile': 'Dockerfile',
|
|
46
|
+
'.gitignore': 'Git Ignore',
|
|
47
|
+
'.env': 'Environment Variables'
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg'];
|
|
51
|
+
const documentExtensions = ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx'];
|
|
52
|
+
|
|
53
|
+
if (imageExtensions.includes(ext)) {
|
|
54
|
+
return 'image';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (documentExtensions.includes(ext)) {
|
|
58
|
+
return 'document';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return codeExtensions[ext] || 'Unknown';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function getMimeType(filePath) {
|
|
65
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
66
|
+
const mimeTypes = {
|
|
67
|
+
'.jpg': 'image/jpeg',
|
|
68
|
+
'.jpeg': 'image/jpeg',
|
|
69
|
+
'.png': 'image/png',
|
|
70
|
+
'.gif': 'image/gif',
|
|
71
|
+
'.webp': 'image/webp',
|
|
72
|
+
'.bmp': 'image/bmp',
|
|
73
|
+
'.svg': 'image/svg+xml',
|
|
74
|
+
'.pdf': 'application/pdf'
|
|
75
|
+
};
|
|
76
|
+
return mimeTypes[ext] || 'image/jpeg';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// File reading and processing
|
|
80
|
+
async function readFileContent(filePath) {
|
|
81
|
+
try {
|
|
82
|
+
const stats = await fs.stat(filePath);
|
|
83
|
+
if (stats.isDirectory()) {
|
|
84
|
+
throw new Error('Path is a directory, not a file');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Check file size (limit to 1MB for safety)
|
|
88
|
+
if (stats.size > 1024 * 1024) {
|
|
89
|
+
throw new Error('File too large. Maximum size is 1MB');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
93
|
+
return content;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
if (error.code === 'ENOENT') {
|
|
96
|
+
throw new Error(`File not found: ${filePath}`);
|
|
97
|
+
}
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = {
|
|
103
|
+
getFileType,
|
|
104
|
+
getMimeType,
|
|
105
|
+
readFileContent
|
|
101
106
|
};
|