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