@goldensheepai/toknxr-cli 0.2.2 → 0.4.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/lib/cli.js +899 -216
- package/lib/commands/hallucination-commands.js +453 -0
- package/lib/enhanced-hallucination-detector.js +622 -0
- package/lib/execution-based-detector.js +538 -0
- package/lib/execution-sandbox.js +602 -0
- package/lib/hallucination-database-service.js +447 -0
- package/lib/hallucination-patterns.js +490 -0
- package/lib/types/database-types.js +5 -0
- package/lib/types/hallucination-types.js +74 -0
- package/lib/types/index.js +8 -0
- package/lib/ui.js +84 -20
- package/lib/utils.js +117 -0
- package/package.json +1 -1
- package/lib/auth.js +0 -73
- package/lib/cli.test.js +0 -49
- package/lib/code-review.js +0 -319
- package/lib/config.js +0 -7
- package/lib/sync.js +0 -117
package/lib/cli.js
CHANGED
@@ -1,10 +1,13 @@
|
|
1
|
+
#!/usr/bin/env node
|
1
2
|
import 'dotenv/config';
|
2
3
|
import { Command } from 'commander';
|
3
4
|
import chalk from 'chalk';
|
4
|
-
import
|
5
|
+
import inquirer from 'inquirer';
|
5
6
|
import { startProxyServer } from './proxy.js';
|
7
|
+
import { testConnection, generateSampleInteraction } from './utils.js';
|
6
8
|
import * as fs from 'node:fs';
|
7
9
|
import * as path from 'node:path';
|
10
|
+
import os from 'os';
|
8
11
|
import open from 'open';
|
9
12
|
import { createStatsOverview, createProviderTable, createQualityBreakdown, createOperationProgress, createInteractiveMenu, createBox, createCostChart, createPaginatedDisplay, createFilterInterface, createSearchInterface, InteractiveDataExplorer, CliStateManager, filterAndSearchInteractions, } from './ui.js';
|
10
13
|
// Gracefully handle broken pipe (e.g., piping output to `head`)
|
@@ -17,29 +20,37 @@ process.stderr.on('error', (err) => {
|
|
17
20
|
process.exit(0);
|
18
21
|
});
|
19
22
|
const program = new Command();
|
23
|
+
// Simple rainbow color function
|
24
|
+
function applyRainbow(text) {
|
25
|
+
const colors = [chalk.red, chalk.yellow, chalk.green, chalk.cyan, chalk.blue, chalk.magenta];
|
26
|
+
return text.split('').map((char, i) => colors[i % colors.length](char)).join('');
|
27
|
+
}
|
20
28
|
// ASCII Art Welcome Screen with gradient colors
|
21
|
-
const
|
22
|
-
${chalk.
|
23
|
-
|
24
|
-
${chalk.
|
25
|
-
${chalk.blue(' ██║ ')}${chalk.hex('#6B5BED')(' ██║ ██║')}${chalk.hex('#9B5BED')(' ██╔═██╗ ')}${chalk.hex('#CB5BED')(' ██║╚██╗██║')}${chalk.hex('#ED5B9B')(' ██╔██╗ ')}${chalk.hex('#ED5B6B')(' ██╔══██╗')}
|
26
|
-
${chalk.blue(' ██║ ')}${chalk.hex('#6B5BED')(' ╚██████╔╝')}${chalk.hex('#9B5BED')(' ██║ ██╗')}${chalk.hex('#CB5BED')(' ██║ ╚████║')}${chalk.hex('#ED5B9B')(' ██╔╝ ██╗')}${chalk.hex('#ED5B6B')(' ██║ ██║')}
|
27
|
-
${chalk.blue(' ╚═╝ ')}${chalk.hex('#6B5BED')(' ╚═════╝ ')}${chalk.hex('#9B5BED')(' ╚═╝ ╚═╝')}${chalk.hex('#CB5BED')(' ╚═╝ ╚═══╝')}${chalk.hex('#ED5B9B')(' ╚═╝ ╚═╝')}${chalk.hex('#ED5B6B')(' ╚═╝ ╚═╝')}
|
29
|
+
const welcomeMessage = `
|
30
|
+
${chalk.bold.cyan('TokNXR CLI - AI Effectiveness & Code Quality Analysis')}
|
31
|
+
|
32
|
+
${chalk.bold.underline('Getting Started Guide:')}
|
28
33
|
|
29
|
-
${chalk.
|
30
|
-
${chalk.white('1.
|
31
|
-
${chalk.
|
32
|
-
${chalk.white('
|
33
|
-
${chalk.
|
34
|
-
${chalk.white('
|
35
|
-
${chalk.
|
34
|
+
${chalk.bold.white('Chapter 1: Core Functionality')}
|
35
|
+
${chalk.white('1.1 Launching the Proxy Server:')}
|
36
|
+
${chalk.yellow('toknxr start')} ${chalk.gray('- Begin tracking your AI interactions.')}
|
37
|
+
${chalk.white('1.2 Viewing Analytics:')}
|
38
|
+
${chalk.yellow('toknxr stats')} ${chalk.gray('- Get an overview of token usage and code quality.')}
|
39
|
+
${chalk.white('1.3 Deep Code Analysis:')}
|
40
|
+
${chalk.yellow('toknxr code-analysis')} ${chalk.gray('- Dive into detailed quality insights for your AI-generated code.')}
|
36
41
|
|
37
|
-
${chalk.
|
42
|
+
${chalk.bold.white('Chapter 2: Advanced Usage')}
|
43
|
+
${chalk.white('2.1 Interactive Command Menu:')}
|
44
|
+
${chalk.yellow('toknxr menu')} ${chalk.gray('- Navigate through commands with a guided interface.')}
|
45
|
+
${chalk.white('2.2 Configuring Spending Policies:')}
|
46
|
+
${chalk.yellow('toknxr policy:init')} ${chalk.gray('- Set up and manage your AI spending limits.')}
|
38
47
|
|
39
|
-
${chalk.
|
48
|
+
${chalk.bold.white('Chapter 3: Need Assistance?')}
|
49
|
+
${chalk.white('3.1 Accessing Help:')}
|
50
|
+
${chalk.yellow('toknxr --help')} ${chalk.gray('- View all available commands and their options.')}
|
40
51
|
|
52
|
+
${chalk.gray('------------------------------------------------------------')}
|
41
53
|
`;
|
42
|
-
console.log(asciiArt);
|
43
54
|
/**
|
44
55
|
* Generate weekly cost trends for the cost chart visualization
|
45
56
|
*/
|
@@ -75,7 +86,7 @@ function generateWeeklyCostTrends(interactions) {
|
|
75
86
|
}
|
76
87
|
return dailyCosts;
|
77
88
|
}
|
78
|
-
program.name('toknxr').description('AI Effectiveness & Code Quality Analysis CLI').version('0.
|
89
|
+
program.name('toknxr').description('AI Effectiveness & Code Quality Analysis CLI').version('0.4.0');
|
79
90
|
program
|
80
91
|
.command('menu')
|
81
92
|
.description('Interactive menu system for TokNXR operations')
|
@@ -98,38 +109,36 @@ program
|
|
98
109
|
name: chalk.yellow('🧠 AI Analysis') + chalk.gray(' - Hallucination detection'),
|
99
110
|
value: 'hallucinations',
|
100
111
|
},
|
112
|
+
{
|
113
|
+
name: chalk.cyan('🔬 Enhanced Detection') + chalk.gray(' - CodeHalu analysis'),
|
114
|
+
value: 'enhanced_detection',
|
115
|
+
},
|
101
116
|
{ name: chalk.red('⚙️ Initialize') + chalk.gray(' - Set up configuration'), value: 'init' },
|
102
117
|
]);
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
'
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
case 'hallucinations':
|
128
|
-
// This will trigger hallucination analytics
|
129
|
-
break;
|
130
|
-
case 'init':
|
131
|
-
// This will trigger init command
|
132
|
-
break;
|
118
|
+
try {
|
119
|
+
switch (choice) {
|
120
|
+
case 'start':
|
121
|
+
await program.parseAsync(['node', 'toknxr', 'start']);
|
122
|
+
break;
|
123
|
+
case 'stats':
|
124
|
+
await program.parseAsync(['node', 'toknxr', 'stats']);
|
125
|
+
break;
|
126
|
+
case 'analysis':
|
127
|
+
await program.parseAsync(['node', 'toknxr', 'code-analysis']);
|
128
|
+
break;
|
129
|
+
case 'hallucinations':
|
130
|
+
await program.parseAsync(['node', 'toknxr', 'hallucinations']);
|
131
|
+
break;
|
132
|
+
case 'enhanced_detection':
|
133
|
+
await program.parseAsync(['node', 'toknxr', 'hallucinations-detailed']);
|
134
|
+
break;
|
135
|
+
case 'init':
|
136
|
+
await program.parseAsync(['node', 'toknxr', 'init']);
|
137
|
+
break;
|
138
|
+
}
|
139
|
+
}
|
140
|
+
catch (e) {
|
141
|
+
// This will catch the exit override
|
133
142
|
}
|
134
143
|
}
|
135
144
|
catch (error) {
|
@@ -327,10 +336,6 @@ program
|
|
327
336
|
// Interactive navigation and context-aware guidance
|
328
337
|
console.log('\n' + chalk.blue.bold('🔍 Interactive Navigation'));
|
329
338
|
console.log(chalk.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
330
|
-
const rl = readline.createInterface({
|
331
|
-
input: process.stdin,
|
332
|
-
output: process.stdout,
|
333
|
-
});
|
334
339
|
// Analyze current situation for intelligent suggestions
|
335
340
|
const currentSituation = {
|
336
341
|
needsBudgetAttention: grandTotals.costUSD > 40, // 80% of monthly budget
|
@@ -383,94 +388,97 @@ program
|
|
383
388
|
recommended: false,
|
384
389
|
},
|
385
390
|
];
|
386
|
-
console.log(chalk.gray('
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
case '2':
|
413
|
-
console.log(chalk.magenta('\n🧠 Navigating to AI performance insights...'));
|
414
|
-
console.log(chalk.cyan('Use: ') +
|
415
|
-
chalk.yellow('toknxr hallucinations') +
|
416
|
-
chalk.gray(' for hallucination analysis'));
|
417
|
-
setTimeout(async () => {
|
418
|
-
try {
|
419
|
-
const { execSync } = await import('child_process');
|
420
|
-
execSync("node -e \"if(require('fs').existsSync('package.json')) { process.chdir(__dirname); require('tsx/dist/esbuild-register').register(); require('./hallucinations.ts').action(); }\" 2>/dev/null || echo \"Please run: toknxr hallucinations\"", { stdio: 'inherit' });
|
421
|
-
}
|
422
|
-
catch {
|
423
|
-
console.log(chalk.gray('Please run: ') + chalk.cyan('toknxr hallucinations'));
|
424
|
-
}
|
425
|
-
}, 500);
|
426
|
-
break;
|
427
|
-
case '3':
|
428
|
-
console.log(chalk.yellow('\n💰 Navigating to budget optimization...'));
|
429
|
-
console.log(chalk.gray('📋 Budget Optimization Strategies:'));
|
430
|
-
console.log(` • Monthly budget: $${(50).toFixed(2)} (spent: $${grandTotals.costUSD.toFixed(2)})`);
|
431
|
-
console.log(` • Daily average: $${(grandTotals.costUSD / Math.max(1, Math.ceil((new Date().getTime() - new Date(interactions[0]?.timestamp || Date.now()).getTime()) / (1000 * 60 * 60 * 24)))).toFixed(2)}`);
|
432
|
-
console.log(` • ${avgQuality >= 80 ? 'Try cheaper providers with similar quality' : 'Focus on prompt optimization to reduce request volume'}`);
|
433
|
-
break;
|
434
|
-
case '4':
|
435
|
-
console.log(chalk.green('\n📈 Starting real-time monitoring...'));
|
436
|
-
console.log(chalk.cyan('Use: ') +
|
437
|
-
chalk.yellow('toknxr tail') +
|
438
|
-
chalk.gray(' to monitor live interactions'));
|
439
|
-
setTimeout(async () => {
|
440
|
-
try {
|
441
|
-
const { execSync } = await import('child_process');
|
442
|
-
execSync("node -e \"if(require('fs').existsSync('package.json')) { process.chdir(__dirname); require('tsx/dist/esbuild-register').register(); require('./tail.ts').action(); }\" 2>/dev/null || echo \"Please run: toknxr tail\"", { stdio: 'inherit' });
|
443
|
-
}
|
444
|
-
catch {
|
445
|
-
console.log(chalk.gray('Please run: ') + chalk.cyan('toknxr tail'));
|
446
|
-
}
|
447
|
-
}, 500);
|
448
|
-
break;
|
449
|
-
case '5':
|
450
|
-
console.log(chalk.blue('\n🚀 Starting AI refinement journey...'));
|
451
|
-
console.log(chalk.gray('📝 Improvement Recommendations:'));
|
452
|
-
if (avgQuality < 75) {
|
453
|
-
console.log(chalk.yellow(' • 🎯 Switch to higher-quality AI models (91vs88 scores)'));
|
454
|
-
console.log(chalk.yellow(' • 📝 Use more specific prompt instructions'));
|
391
|
+
console.log(chalk.gray('\nSelect an option to continue your analysis journey:'));
|
392
|
+
const choices = navigationOptions.map(option => ({
|
393
|
+
name: `${option.recommended ? chalk.green('★') : ' '}${chalk.bold(option.key)}) ${option.title}\n ${option.description}`,
|
394
|
+
value: option.key,
|
395
|
+
}));
|
396
|
+
const { choice } = await inquirer.prompt([
|
397
|
+
{
|
398
|
+
type: 'list',
|
399
|
+
name: 'choice',
|
400
|
+
message: 'Your choice:',
|
401
|
+
choices: choices,
|
402
|
+
},
|
403
|
+
]);
|
404
|
+
switch (choice) {
|
405
|
+
case '1':
|
406
|
+
console.log(chalk.blue('\n🔍 Navigating to detailed analysis...'));
|
407
|
+
console.log(chalk.cyan('Use: ') +
|
408
|
+
chalk.yellow('toknxr code-analysis') +
|
409
|
+
chalk.gray(' for deep quality insights'));
|
410
|
+
setTimeout(async () => {
|
411
|
+
try {
|
412
|
+
const { execSync } = await import('child_process');
|
413
|
+
execSync("node -e \"if(require('fs').existsSync('package.json')) { process.chdir(__dirname); require('tsx/dist/esbuild-register').register(); require('./code-analysis.ts').action(); }\" 2>/dev/null || echo \"Please run: toknxr code-analysis\"", { stdio: 'inherit' });
|
414
|
+
}
|
415
|
+
catch {
|
416
|
+
console.log(chalk.gray('Please run: ') + chalk.cyan('toknxr code-analysis'));
|
455
417
|
}
|
456
|
-
|
457
|
-
|
458
|
-
|
418
|
+
}, 500);
|
419
|
+
break;
|
420
|
+
case '2':
|
421
|
+
console.log(chalk.magenta('\n🧠 Navigating to AI performance insights...'));
|
422
|
+
console.log(chalk.cyan('Use: ') +
|
423
|
+
chalk.yellow('toknxr hallucinations') +
|
424
|
+
chalk.gray(' for hallucination analysis'));
|
425
|
+
setTimeout(async () => {
|
426
|
+
try {
|
427
|
+
const { execSync } = await import('child_process');
|
428
|
+
execSync("node -e \"if(require('fs').existsSync('package.json')) { process.chdir(__dirname); require('tsx/dist/esbuild-register').register(); require('./hallucinations.ts').action(); }\" 2>/dev/null || echo \"Please run: toknxr hallucinations\"", { stdio: 'inherit' });
|
459
429
|
}
|
460
|
-
|
461
|
-
console.log(chalk.
|
430
|
+
catch {
|
431
|
+
console.log(chalk.gray('Please run: ') + chalk.cyan('toknxr hallucinations'));
|
462
432
|
}
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
433
|
+
}, 500);
|
434
|
+
break;
|
435
|
+
case '3':
|
436
|
+
console.log(chalk.yellow('\n💰 Navigating to budget optimization...'));
|
437
|
+
console.log(chalk.gray('📋 Budget Optimization Strategies:'));
|
438
|
+
console.log(` • Monthly budget: $${(50).toFixed(2)} (spent: $${grandTotals.costUSD.toFixed(2)})`);
|
439
|
+
console.log(` • Daily average: $${(grandTotals.costUSD / Math.max(1, Math.ceil((new Date().getTime() - new Date(interactions[0]?.timestamp || Date.now()).getTime()) / (1000 * 60 * 60 * 24)))).toFixed(2)}`);
|
440
|
+
console.log(` • ${avgQuality >= 80 ? 'Try cheaper providers with similar quality' : 'Focus on prompt optimization to reduce request volume'}`);
|
441
|
+
break;
|
442
|
+
case '4':
|
443
|
+
console.log(chalk.green('\n📈 Starting real-time monitoring...'));
|
444
|
+
console.log(chalk.cyan('Use: ') +
|
445
|
+
chalk.yellow('toknxr tail') +
|
446
|
+
chalk.gray(' to monitor live interactions'));
|
447
|
+
setTimeout(async () => {
|
448
|
+
try {
|
449
|
+
const { execSync } = await import('child_process');
|
450
|
+
execSync("node -e \"if(require('fs').existsSync('package.json')) { process.chdir(__dirname); require('tsx/dist/esbuild-register').register(); require('./tail.ts').action(); }\" 2>/dev/null || echo \"Please run: toknxr tail\"", { stdio: 'inherit' });
|
451
|
+
}
|
452
|
+
catch {
|
453
|
+
console.log(chalk.gray('Please run: ') + chalk.cyan('toknxr tail'));
|
454
|
+
}
|
455
|
+
}, 500);
|
456
|
+
break;
|
457
|
+
case '5':
|
458
|
+
console.log(chalk.blue('\n🚀 Starting AI refinement journey...'));
|
459
|
+
console.log(chalk.gray('📝 Improvement Recommendations:'));
|
460
|
+
if (avgQuality < 75) {
|
461
|
+
console.log(chalk.yellow(' • 🎯 Switch to higher-quality AI models (91vs88 scores)'));
|
462
|
+
console.log(chalk.yellow(' • 📝 Use more specific prompt instructions'));
|
463
|
+
}
|
464
|
+
if (grandTotals.costUSD > 25) {
|
465
|
+
console.log(chalk.red(' • 💰 Consider API usage limits or budget alerts'));
|
466
|
+
console.log(chalk.red(' • 🔄 Explore cost-efficient AI providers'));
|
467
|
+
}
|
468
|
+
if (Object.keys(stats).length === 1) {
|
469
|
+
console.log(chalk.blue(' • 🧪 A/B test multiple AI providers for your use case'));
|
470
|
+
}
|
471
|
+
break;
|
472
|
+
case 'm':
|
473
|
+
case 'menu':
|
474
|
+
console.log(chalk.gray('\n📋 Opening interactive menu...'));
|
475
|
+
setTimeout(async () => {
|
476
|
+
try {
|
477
|
+
const { spawn } = await import('child_process');
|
478
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
479
|
+
const menuCommand = spawn('node', [
|
480
|
+
'-e',
|
481
|
+
`
|
474
482
|
try {
|
475
483
|
process.chdir('${process.cwd().replace(/\\/g, '\\\\')}');
|
476
484
|
require('tsx/dist/esbuild-register').register();
|
@@ -483,29 +491,27 @@ program
|
|
483
491
|
console.log('Please run: toknxr menu');
|
484
492
|
}
|
485
493
|
`,
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
rl.close();
|
508
|
-
});
|
494
|
+
], { stdio: 'inherit' });
|
495
|
+
}
|
496
|
+
catch (e) {
|
497
|
+
console.log('Please run: toknxr menu');
|
498
|
+
}
|
499
|
+
}, 300);
|
500
|
+
break;
|
501
|
+
case 'q':
|
502
|
+
case 'quit':
|
503
|
+
case 'exit':
|
504
|
+
console.log(chalk.gray('\n👋 Thanks for using TokNXR analytics!'));
|
505
|
+
console.log(chalk.gray('Raw data available in: ') + chalk.cyan('interactions.log'));
|
506
|
+
break;
|
507
|
+
default:
|
508
|
+
console.log(chalk.yellow(`Unknown option "${choice}". Showing help...`));
|
509
|
+
console.log(chalk.cyan('\nAvailable commands:'));
|
510
|
+
console.log(` ${chalk.yellow('toknxr menu')} - Interactive command menu`);
|
511
|
+
console.log(` ${chalk.yellow('toknxr stats')} - Current usage overview`);
|
512
|
+
console.log(` ${chalk.yellow('toknxr start')} - Launch proxy server`);
|
513
|
+
console.log(` ${chalk.yellow('toknxr --help')} - View all commands`);
|
514
|
+
}
|
509
515
|
});
|
510
516
|
program
|
511
517
|
.command('init')
|
@@ -612,7 +618,7 @@ program
|
|
612
618
|
program
|
613
619
|
.command('code-analysis')
|
614
620
|
.description('Show detailed code quality analysis from coding interactions')
|
615
|
-
.action(() => {
|
621
|
+
.action(async () => {
|
616
622
|
const logFilePath = path.resolve(process.cwd(), 'interactions.log');
|
617
623
|
if (!fs.existsSync(logFilePath)) {
|
618
624
|
console.log(chalk.yellow('No interactions logged yet. Use the `start` command to begin tracking.'));
|
@@ -721,12 +727,112 @@ program
|
|
721
727
|
console.log(' • Consider establishing code review processes for edge cases');
|
722
728
|
}
|
723
729
|
console.log(`\n${chalk.gray('Total coding interactions analyzed: ' + interactions.length)}`);
|
730
|
+
// Interactive navigation
|
731
|
+
console.log('\n' + chalk.blue.bold('🔍 Interactive Navigation'));
|
732
|
+
console.log(chalk.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
733
|
+
const navigationOptions = [
|
734
|
+
{
|
735
|
+
key: '1',
|
736
|
+
title: chalk.cyan('📊 View Detailed Metrics'),
|
737
|
+
description: 'Drill down into specific quality metrics',
|
738
|
+
available: interactions.length > 0,
|
739
|
+
},
|
740
|
+
{
|
741
|
+
key: '2',
|
742
|
+
title: chalk.magenta('🧠 Hallucination Analysis'),
|
743
|
+
description: 'Analyze potential hallucinations in code',
|
744
|
+
available: interactions.length > 0,
|
745
|
+
},
|
746
|
+
{
|
747
|
+
key: '3',
|
748
|
+
title: chalk.yellow('🔄 Provider Comparison'),
|
749
|
+
description: 'Compare code quality across AI providers',
|
750
|
+
available: interactions.length > 0,
|
751
|
+
},
|
752
|
+
{
|
753
|
+
key: '4',
|
754
|
+
title: chalk.green('📈 View All Analytics'),
|
755
|
+
description: 'Go to comprehensive statistics view',
|
756
|
+
available: true,
|
757
|
+
},
|
758
|
+
{
|
759
|
+
key: '5',
|
760
|
+
title: chalk.blue('🔍 Browse Interactions'),
|
761
|
+
description: 'Browse through individual interactions',
|
762
|
+
available: interactions.length > 0,
|
763
|
+
},
|
764
|
+
{
|
765
|
+
key: 'm',
|
766
|
+
title: chalk.gray('📋 Main Menu'),
|
767
|
+
description: 'Return to main menu',
|
768
|
+
available: true,
|
769
|
+
},
|
770
|
+
{
|
771
|
+
key: 'q',
|
772
|
+
title: chalk.gray('❌ Exit'),
|
773
|
+
description: 'Exit code analysis',
|
774
|
+
available: true,
|
775
|
+
},
|
776
|
+
];
|
777
|
+
const availableChoices = navigationOptions
|
778
|
+
.filter(option => option.available)
|
779
|
+
.map(option => ({
|
780
|
+
name: `${chalk.bold(option.key)}) ${option.title}\n ${option.description}`,
|
781
|
+
value: option.key,
|
782
|
+
}));
|
783
|
+
const { choice } = await inquirer.prompt([
|
784
|
+
{
|
785
|
+
type: 'list',
|
786
|
+
name: 'choice',
|
787
|
+
message: 'What would you like to explore?',
|
788
|
+
choices: availableChoices,
|
789
|
+
},
|
790
|
+
]);
|
791
|
+
switch (choice) {
|
792
|
+
case '1':
|
793
|
+
console.log(chalk.cyan('\n📊 Showing detailed quality metrics...'));
|
794
|
+
console.log(chalk.gray('Detailed breakdown by language and complexity:'));
|
795
|
+
Object.entries(langStats).forEach(([lang, count]) => {
|
796
|
+
const langInteractions = interactions.filter(i => (i.codeQualityMetrics?.language || 'unknown') === lang);
|
797
|
+
const avgQuality = langInteractions.reduce((sum, i) => sum + (i.codeQualityScore || 0), 0) / langInteractions.length;
|
798
|
+
console.log(` • ${lang}: ${count} requests, avg quality: ${avgQuality.toFixed(1)}/100`);
|
799
|
+
});
|
800
|
+
break;
|
801
|
+
case '2':
|
802
|
+
console.log(chalk.magenta('\n🧠 Opening hallucination analysis...'));
|
803
|
+
await program.parseAsync(['node', 'toknxr', 'hallucinations']);
|
804
|
+
break;
|
805
|
+
case '3':
|
806
|
+
console.log(chalk.yellow('\n🔄 Opening provider comparison...'));
|
807
|
+
await program.parseAsync(['node', 'toknxr', 'providers']);
|
808
|
+
break;
|
809
|
+
case '4':
|
810
|
+
console.log(chalk.green('\n📈 Opening comprehensive analytics...'));
|
811
|
+
await program.parseAsync(['node', 'toknxr', 'stats']);
|
812
|
+
break;
|
813
|
+
case '5':
|
814
|
+
console.log(chalk.blue('\n🔍 Opening interaction browser...'));
|
815
|
+
await program.parseAsync(['node', 'toknxr', 'browse']);
|
816
|
+
break;
|
817
|
+
case 'm':
|
818
|
+
console.log(chalk.gray('\n📋 Opening main menu...'));
|
819
|
+
await program.parseAsync(['node', 'toknxr', 'menu']);
|
820
|
+
break;
|
821
|
+
case 'q':
|
822
|
+
console.log(chalk.gray('\n👋 Exiting code analysis...'));
|
823
|
+
break;
|
824
|
+
default:
|
825
|
+
console.log(chalk.yellow(`Unknown option "${choice}".`));
|
826
|
+
}
|
724
827
|
});
|
725
828
|
// Import required modules for new AI analysis commands
|
726
829
|
import { hallucinationDetector } from './hallucination-detector.js';
|
727
830
|
import { analyzeCodeQuality, scoreEffectiveness, extractCodeFromResponse, } from './code-analysis.js';
|
728
831
|
import { aiAnalytics } from './ai-analytics.js';
|
729
832
|
import { auditLogger, AuditEventType, initializeAuditLogging } from './audit-logger.js';
|
833
|
+
// Import enhanced hallucination detection commands
|
834
|
+
import { hallucinationsDetailedCommand, codeQualityReportCommand } from './commands/hallucination-commands.js';
|
835
|
+
import { detectCodeHallucinations } from './enhanced-hallucination-detector.js';
|
730
836
|
program
|
731
837
|
.command('analyze')
|
732
838
|
.description('Analyze an AI response for hallucinations and quality issues')
|
@@ -831,12 +937,71 @@ program
|
|
831
937
|
console.log(chalk.red(' ❌ Poor quality - significant issues detected'));
|
832
938
|
}
|
833
939
|
});
|
940
|
+
// Enhanced hallucination detection commands
|
941
|
+
program
|
942
|
+
.command('hallucinations-detailed')
|
943
|
+
.description('Detailed hallucination analysis with CodeHalu categories')
|
944
|
+
.option('-c, --category <category>', 'Filter by hallucination category')
|
945
|
+
.option('-p, --provider <provider>', 'Filter by AI provider')
|
946
|
+
.option('-f, --file <file>', 'Analyze code from file')
|
947
|
+
.option('--code <code>', 'Analyze code directly')
|
948
|
+
.option('-l, --language <language>', 'Programming language (default: python)')
|
949
|
+
.option('--no-execution', 'Disable execution analysis')
|
950
|
+
.option('-o, --output <file>', 'Export results to file')
|
951
|
+
.action(hallucinationsDetailedCommand);
|
952
|
+
program
|
953
|
+
.command('code-quality-report')
|
954
|
+
.description('Generate comprehensive code quality report')
|
955
|
+
.option('-o, --output <file>', 'Output file path')
|
956
|
+
.option('--format <format>', 'Output format (json|html)', 'json')
|
957
|
+
.option('--include-execution', 'Include execution analysis in report')
|
958
|
+
.action(codeQualityReportCommand);
|
959
|
+
program
|
960
|
+
.command('detect-hallucinations')
|
961
|
+
.description('Quick hallucination detection for code snippet')
|
962
|
+
.argument('<code>', 'Code to analyze')
|
963
|
+
.option('-l, --language <language>', 'Programming language (default: python)', 'python')
|
964
|
+
.option('--no-execution', 'Disable execution analysis')
|
965
|
+
.option('--confidence <threshold>', 'Confidence threshold (0.0-1.0)', '0.6')
|
966
|
+
.action(async (code, options) => {
|
967
|
+
console.log(chalk.bold.blue('🔍 Quick Hallucination Detection'));
|
968
|
+
console.log(chalk.gray('━'.repeat(50)));
|
969
|
+
try {
|
970
|
+
const result = await detectCodeHallucinations(code, options.language, {
|
971
|
+
enableExecution: !options.noExecution,
|
972
|
+
confidenceThreshold: parseFloat(options.confidence),
|
973
|
+
});
|
974
|
+
const rateColor = result.overallHallucinationRate > 0.7 ? chalk.red :
|
975
|
+
result.overallHallucinationRate > 0.4 ? chalk.yellow : chalk.green;
|
976
|
+
console.log(`\n🎯 Hallucination Rate: ${rateColor(`${(result.overallHallucinationRate * 100).toFixed(1)}%`)}`);
|
977
|
+
console.log(`📊 Issues Found: ${result.categories.length}`);
|
978
|
+
console.log(`⏱️ Analysis Time: ${result.analysisMetadata.detectionTimeMs}ms`);
|
979
|
+
if (result.categories.length > 0) {
|
980
|
+
console.log(chalk.yellow('\n⚠️ Top Issues:'));
|
981
|
+
result.categories.slice(0, 3).forEach((category, index) => {
|
982
|
+
console.log(` ${index + 1}. ${category.type}/${category.subtype} - ${category.description}`);
|
983
|
+
});
|
984
|
+
}
|
985
|
+
else {
|
986
|
+
console.log(chalk.green('\n✅ No significant hallucinations detected!'));
|
987
|
+
}
|
988
|
+
if (result.recommendations.length > 0) {
|
989
|
+
console.log(chalk.blue('\n💡 Quick Recommendations:'));
|
990
|
+
result.recommendations.slice(0, 2).forEach(rec => {
|
991
|
+
console.log(` • ${rec.title}`);
|
992
|
+
});
|
993
|
+
}
|
994
|
+
}
|
995
|
+
catch (error) {
|
996
|
+
console.error(chalk.red('❌ Detection failed:'), error);
|
997
|
+
}
|
998
|
+
});
|
834
999
|
program
|
835
1000
|
.command('hallucinations')
|
836
1001
|
.description('Show hallucination statistics and trends')
|
837
1002
|
.option('-p, --provider <provider>', 'Filter by AI provider')
|
838
1003
|
.option('-l, --last <hours>', 'Show last N hours (default: 24)', '24')
|
839
|
-
.action(options => {
|
1004
|
+
.action(async (options) => {
|
840
1005
|
const analytics = aiAnalytics.generateAnalytics();
|
841
1006
|
console.log(chalk.bold.blue('🧠 Hallucination Analytics'));
|
842
1007
|
console.log(chalk.gray('━'.repeat(50)));
|
@@ -867,11 +1032,98 @@ program
|
|
867
1032
|
console.log(` • ${rec}`);
|
868
1033
|
});
|
869
1034
|
}
|
1035
|
+
// Interactive navigation
|
1036
|
+
console.log('\n' + chalk.blue.bold('🔍 Interactive Navigation'));
|
1037
|
+
console.log(chalk.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
1038
|
+
const navigationOptions = [
|
1039
|
+
{
|
1040
|
+
key: '1',
|
1041
|
+
title: chalk.cyan('📊 View Detailed Analysis'),
|
1042
|
+
description: 'Deep dive into specific hallucination patterns',
|
1043
|
+
available: analytics.totalInteractions > 0,
|
1044
|
+
},
|
1045
|
+
{
|
1046
|
+
key: '2',
|
1047
|
+
title: chalk.magenta('🔄 Compare Providers'),
|
1048
|
+
description: 'Detailed provider comparison for hallucinations',
|
1049
|
+
available: Object.keys(analytics.providerComparison).length > 1,
|
1050
|
+
},
|
1051
|
+
{
|
1052
|
+
key: '3',
|
1053
|
+
title: chalk.yellow('💰 Business Impact Analysis'),
|
1054
|
+
description: 'Analyze cost and time impact of hallucinations',
|
1055
|
+
available: analytics.hallucinationMetrics.businessImpact.estimatedDevTimeWasted > 0,
|
1056
|
+
},
|
1057
|
+
{
|
1058
|
+
key: '4',
|
1059
|
+
title: chalk.green('📈 View All Analytics'),
|
1060
|
+
description: 'Go to comprehensive statistics view',
|
1061
|
+
available: true,
|
1062
|
+
},
|
1063
|
+
{
|
1064
|
+
key: 'm',
|
1065
|
+
title: chalk.gray('📋 Main Menu'),
|
1066
|
+
description: 'Return to main menu',
|
1067
|
+
available: true,
|
1068
|
+
},
|
1069
|
+
{
|
1070
|
+
key: 'q',
|
1071
|
+
title: chalk.gray('❌ Exit'),
|
1072
|
+
description: 'Exit hallucination analysis',
|
1073
|
+
available: true,
|
1074
|
+
},
|
1075
|
+
];
|
1076
|
+
const availableChoices = navigationOptions
|
1077
|
+
.filter(option => option.available)
|
1078
|
+
.map(option => ({
|
1079
|
+
name: `${chalk.bold(option.key)}) ${option.title}\n ${option.description}`,
|
1080
|
+
value: option.key,
|
1081
|
+
}));
|
1082
|
+
const { choice } = await inquirer.prompt([
|
1083
|
+
{
|
1084
|
+
type: 'list',
|
1085
|
+
name: 'choice',
|
1086
|
+
message: 'What would you like to explore?',
|
1087
|
+
choices: availableChoices,
|
1088
|
+
},
|
1089
|
+
]);
|
1090
|
+
switch (choice) {
|
1091
|
+
case '1':
|
1092
|
+
console.log(chalk.cyan('\n📊 Opening detailed hallucination analysis...'));
|
1093
|
+
await program.parseAsync(['node', 'toknxr', 'code-analysis']);
|
1094
|
+
break;
|
1095
|
+
case '2':
|
1096
|
+
console.log(chalk.magenta('\n🔄 Opening provider comparison...'));
|
1097
|
+
await program.parseAsync(['node', 'toknxr', 'providers']);
|
1098
|
+
break;
|
1099
|
+
case '3':
|
1100
|
+
console.log(chalk.yellow('\n💰 Analyzing business impact...'));
|
1101
|
+
console.log(chalk.gray('Business impact details:'));
|
1102
|
+
const impact = analytics.hallucinationMetrics.businessImpact;
|
1103
|
+
console.log(` • Estimated dev time wasted: ${impact.estimatedDevTimeWasted}h`);
|
1104
|
+
console.log(` • Quality degradation score: ${impact.qualityDegradationScore}/100`);
|
1105
|
+
console.log(` • ROI impact: ${impact.roiImpact}% reduction`);
|
1106
|
+
console.log(` • Extra cost from hallucinations: ${impact.costOfHallucinations.toFixed(2)}`);
|
1107
|
+
break;
|
1108
|
+
case '4':
|
1109
|
+
console.log(chalk.green('\n📈 Opening comprehensive analytics...'));
|
1110
|
+
await program.parseAsync(['node', 'toknxr', 'stats']);
|
1111
|
+
break;
|
1112
|
+
case 'm':
|
1113
|
+
console.log(chalk.gray('\n📋 Opening main menu...'));
|
1114
|
+
await program.parseAsync(['node', 'toknxr', 'menu']);
|
1115
|
+
break;
|
1116
|
+
case 'q':
|
1117
|
+
console.log(chalk.gray('\n👋 Exiting hallucination analysis...'));
|
1118
|
+
break;
|
1119
|
+
default:
|
1120
|
+
console.log(chalk.yellow(`Unknown option "${choice}".`));
|
1121
|
+
}
|
870
1122
|
});
|
871
1123
|
program
|
872
1124
|
.command('providers')
|
873
1125
|
.description('Compare AI provider performance')
|
874
|
-
.action(() => {
|
1126
|
+
.action(async () => {
|
875
1127
|
const analytics = aiAnalytics.generateAnalytics();
|
876
1128
|
console.log(chalk.bold.blue('🔄 AI Provider Comparison'));
|
877
1129
|
console.log(chalk.gray('━'.repeat(50)));
|
@@ -918,18 +1170,172 @@ program
|
|
918
1170
|
console.log(` Best Provider: ${chalk.green(bestProvider[0])} (${bestProvider[1].avgQualityScore}/100 quality)`);
|
919
1171
|
console.log(` Needs Attention: ${chalk.red(worstProvider[0])} (${worstProvider[1].avgQualityScore}/100 quality)`);
|
920
1172
|
}
|
1173
|
+
// Interactive navigation
|
1174
|
+
console.log('\n' + chalk.blue.bold('🔍 Interactive Navigation'));
|
1175
|
+
console.log(chalk.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
1176
|
+
const providerList = Object.keys(analytics.providerComparison);
|
1177
|
+
const navigationOptions = [
|
1178
|
+
{
|
1179
|
+
key: '1',
|
1180
|
+
title: chalk.cyan('📊 Detailed Provider Analysis'),
|
1181
|
+
description: 'Deep dive into individual provider metrics',
|
1182
|
+
available: providerList.length > 0,
|
1183
|
+
},
|
1184
|
+
{
|
1185
|
+
key: '2',
|
1186
|
+
title: chalk.magenta('🧠 Hallucination Comparison'),
|
1187
|
+
description: 'Compare hallucination rates across providers',
|
1188
|
+
available: providerList.length > 1,
|
1189
|
+
},
|
1190
|
+
{
|
1191
|
+
key: '3',
|
1192
|
+
title: chalk.yellow('💰 Cost Optimization'),
|
1193
|
+
description: 'Analyze cost-effectiveness by provider',
|
1194
|
+
available: true,
|
1195
|
+
},
|
1196
|
+
{
|
1197
|
+
key: '4',
|
1198
|
+
title: chalk.green('📈 View All Analytics'),
|
1199
|
+
description: 'Go to comprehensive statistics view',
|
1200
|
+
available: true,
|
1201
|
+
},
|
1202
|
+
{
|
1203
|
+
key: 'm',
|
1204
|
+
title: chalk.gray('📋 Main Menu'),
|
1205
|
+
description: 'Return to main menu',
|
1206
|
+
available: true,
|
1207
|
+
},
|
1208
|
+
{
|
1209
|
+
key: 'q',
|
1210
|
+
title: chalk.gray('❌ Exit'),
|
1211
|
+
description: 'Exit provider comparison',
|
1212
|
+
available: true,
|
1213
|
+
},
|
1214
|
+
];
|
1215
|
+
const availableChoices = navigationOptions
|
1216
|
+
.filter(option => option.available)
|
1217
|
+
.map(option => ({
|
1218
|
+
name: `${chalk.bold(option.key)}) ${option.title}\n ${option.description}`,
|
1219
|
+
value: option.key,
|
1220
|
+
}));
|
1221
|
+
const { choice } = await inquirer.prompt([
|
1222
|
+
{
|
1223
|
+
type: 'list',
|
1224
|
+
name: 'choice',
|
1225
|
+
message: 'What would you like to explore?',
|
1226
|
+
choices: availableChoices,
|
1227
|
+
},
|
1228
|
+
]);
|
1229
|
+
switch (choice) {
|
1230
|
+
case '1':
|
1231
|
+
console.log(chalk.cyan('\n📊 Opening detailed provider analysis...'));
|
1232
|
+
if (providerList.length > 1) {
|
1233
|
+
const { selectedProvider } = await inquirer.prompt([
|
1234
|
+
{
|
1235
|
+
type: 'list',
|
1236
|
+
name: 'selectedProvider',
|
1237
|
+
message: 'Select a provider to analyze:',
|
1238
|
+
choices: providerList.map(provider => ({
|
1239
|
+
name: `${provider} (${analytics.providerComparison[provider].avgQualityScore}/100 quality)`,
|
1240
|
+
value: provider,
|
1241
|
+
})),
|
1242
|
+
},
|
1243
|
+
]);
|
1244
|
+
const providerStats = analytics.providerComparison[selectedProvider];
|
1245
|
+
console.log(chalk.cyan(`\n📊 Detailed Analysis for ${selectedProvider}:`));
|
1246
|
+
console.log(` Total Interactions: ${providerStats.totalInteractions}`);
|
1247
|
+
console.log(` Avg Quality Score: ${providerStats.avgQualityScore}/100`);
|
1248
|
+
console.log(` Avg Effectiveness: ${providerStats.avgEffectivenessScore}/100`);
|
1249
|
+
console.log(` Hallucination Rate: ${providerStats.hallucinationRate}%`);
|
1250
|
+
console.log(` Dev Time Wasted: ${providerStats.businessImpact.estimatedDevTimeWasted}h`);
|
1251
|
+
}
|
1252
|
+
else {
|
1253
|
+
console.log(chalk.gray('Only one provider available. Run: ') + chalk.yellow('toknxr code-analysis'));
|
1254
|
+
}
|
1255
|
+
break;
|
1256
|
+
case '2':
|
1257
|
+
console.log(chalk.magenta('\n🧠 Opening hallucination comparison...'));
|
1258
|
+
await program.parseAsync(['node', 'toknxr', 'hallucinations']);
|
1259
|
+
break;
|
1260
|
+
case '3':
|
1261
|
+
console.log(chalk.yellow('\n💰 Analyzing cost optimization...'));
|
1262
|
+
console.log(chalk.gray('Cost optimization recommendations:'));
|
1263
|
+
providerList.forEach(provider => {
|
1264
|
+
const stats = analytics.providerComparison[provider];
|
1265
|
+
const efficiency = stats.avgQualityScore / (stats.businessImpact.costOfHallucinations || 1);
|
1266
|
+
console.log(` • ${provider}: Quality/Cost ratio = ${efficiency.toFixed(2)}`);
|
1267
|
+
});
|
1268
|
+
break;
|
1269
|
+
case '4':
|
1270
|
+
console.log(chalk.green('\n📈 Opening comprehensive analytics...'));
|
1271
|
+
await program.parseAsync(['node', 'toknxr', 'stats']);
|
1272
|
+
break;
|
1273
|
+
case 'm':
|
1274
|
+
console.log(chalk.gray('\n📋 Opening main menu...'));
|
1275
|
+
await program.parseAsync(['node', 'toknxr', 'menu']);
|
1276
|
+
break;
|
1277
|
+
case 'q':
|
1278
|
+
console.log(chalk.gray('\n👋 Exiting provider comparison...'));
|
1279
|
+
break;
|
1280
|
+
default:
|
1281
|
+
console.log(chalk.yellow(`Unknown option "${choice}".`));
|
1282
|
+
}
|
921
1283
|
});
|
922
1284
|
program
|
923
1285
|
.command('export')
|
924
1286
|
.description('Export analytics data to JSON file')
|
925
|
-
.option('-o, --output <file>', 'Output file path'
|
926
|
-
.action(options => {
|
1287
|
+
.option('-o, --output <file>', 'Output file path')
|
1288
|
+
.action(async (options) => {
|
927
1289
|
try {
|
928
|
-
|
929
|
-
|
1290
|
+
let outputPath = options.output;
|
1291
|
+
if (!outputPath) { // If -o option is not provided, use interactive mode
|
1292
|
+
const { locationType } = await inquirer.prompt([
|
1293
|
+
{
|
1294
|
+
type: 'list',
|
1295
|
+
name: 'locationType',
|
1296
|
+
message: 'Where would you like to save the export file?',
|
1297
|
+
choices: [
|
1298
|
+
{ name: 'Current Directory', value: 'current' },
|
1299
|
+
{ name: 'Desktop', value: 'desktop' },
|
1300
|
+
{ name: 'Custom Path', value: 'custom' },
|
1301
|
+
],
|
1302
|
+
},
|
1303
|
+
]);
|
1304
|
+
let chosenDirectory = process.cwd();
|
1305
|
+
if (locationType === 'desktop') {
|
1306
|
+
chosenDirectory = path.join(os.homedir(), 'Desktop');
|
1307
|
+
}
|
1308
|
+
else if (locationType === 'custom') {
|
1309
|
+
const { customPath } = await inquirer.prompt([
|
1310
|
+
{
|
1311
|
+
type: 'input',
|
1312
|
+
name: 'customPath',
|
1313
|
+
message: 'Enter the custom directory path:',
|
1314
|
+
default: process.cwd(),
|
1315
|
+
},
|
1316
|
+
]);
|
1317
|
+
chosenDirectory = customPath;
|
1318
|
+
}
|
1319
|
+
const { filename } = await inquirer.prompt([
|
1320
|
+
{
|
1321
|
+
type: 'input',
|
1322
|
+
name: 'filename',
|
1323
|
+
message: 'Enter the filename for the export:',
|
1324
|
+
default: 'ai-analytics-export.json',
|
1325
|
+
},
|
1326
|
+
]);
|
1327
|
+
// Ensure the chosen directory exists
|
1328
|
+
if (!fs.existsSync(chosenDirectory)) {
|
1329
|
+
fs.mkdirSync(chosenDirectory, { recursive: true });
|
1330
|
+
console.log(chalk.green(`Created directory: ${chosenDirectory}`));
|
1331
|
+
}
|
1332
|
+
outputPath = path.join(chosenDirectory, filename);
|
1333
|
+
}
|
1334
|
+
aiAnalytics.exportAnalytics(outputPath);
|
1335
|
+
console.log(chalk.green(`✅ Analytics exported to ${outputPath}`));
|
930
1336
|
}
|
931
|
-
catch {
|
932
|
-
console.error(chalk.red('❌ Export failed'));
|
1337
|
+
catch (error) {
|
1338
|
+
console.error(chalk.red('❌ Export failed'), error);
|
933
1339
|
}
|
934
1340
|
});
|
935
1341
|
// Enhanced Interactive Commands - Phase 3+
|
@@ -998,15 +1404,169 @@ program
|
|
998
1404
|
console.log(createPaginatedDisplay(explorer.getCurrentPageData(), pageSize, currentPage, renderInteraction, `🐙 AI Interactions Browser (${explorer.getPaginationInfo().totalItems} total)`));
|
999
1405
|
// Interactive navigation
|
1000
1406
|
const pagination = explorer.getPaginationInfo();
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1407
|
+
console.log('\n' + chalk.blue.bold('🔍 Interactive Navigation'));
|
1408
|
+
console.log(chalk.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
1409
|
+
const navigationOptions = [
|
1410
|
+
{
|
1411
|
+
key: 'n',
|
1412
|
+
title: chalk.cyan('📄 Next Page'),
|
1413
|
+
description: `Go to page ${currentPage + 1}`,
|
1414
|
+
available: currentPage < pagination.totalPages,
|
1415
|
+
},
|
1416
|
+
{
|
1417
|
+
key: 'p',
|
1418
|
+
title: chalk.cyan('📄 Previous Page'),
|
1419
|
+
description: `Go to page ${currentPage - 1}`,
|
1420
|
+
available: currentPage > 1,
|
1421
|
+
},
|
1422
|
+
{
|
1423
|
+
key: 'f',
|
1424
|
+
title: chalk.magenta('🔍 Filter Results'),
|
1425
|
+
description: 'Apply filters to narrow down interactions',
|
1426
|
+
available: true,
|
1427
|
+
},
|
1428
|
+
{
|
1429
|
+
key: 's',
|
1430
|
+
title: chalk.yellow('🔍 Search Interactions'),
|
1431
|
+
description: 'Search through all interactions',
|
1432
|
+
available: true,
|
1433
|
+
},
|
1434
|
+
{
|
1435
|
+
key: 'a',
|
1436
|
+
title: chalk.green('📊 View Analytics'),
|
1437
|
+
description: 'Go to detailed statistics view',
|
1438
|
+
available: true,
|
1439
|
+
},
|
1440
|
+
{
|
1441
|
+
key: 'm',
|
1442
|
+
title: chalk.gray('📋 Main Menu'),
|
1443
|
+
description: 'Return to main menu',
|
1444
|
+
available: true,
|
1445
|
+
},
|
1446
|
+
{
|
1447
|
+
key: 'q',
|
1448
|
+
title: chalk.gray('❌ Exit'),
|
1449
|
+
description: 'Exit browser',
|
1450
|
+
available: true,
|
1451
|
+
},
|
1452
|
+
];
|
1453
|
+
const availableChoices = navigationOptions
|
1454
|
+
.filter(option => option.available)
|
1455
|
+
.map(option => ({
|
1456
|
+
name: `${chalk.bold(option.key)}) ${option.title}\n ${option.description}`,
|
1457
|
+
value: option.key,
|
1458
|
+
}));
|
1459
|
+
const { choice } = await inquirer.prompt([
|
1460
|
+
{
|
1461
|
+
type: 'list',
|
1462
|
+
name: 'choice',
|
1463
|
+
message: 'What would you like to do?',
|
1464
|
+
choices: availableChoices,
|
1465
|
+
},
|
1466
|
+
]);
|
1467
|
+
switch (choice) {
|
1468
|
+
case 'n':
|
1469
|
+
console.log(chalk.cyan(`\n📄 Going to page ${currentPage + 1}...`));
|
1470
|
+
console.log(chalk.gray('Run: ') + chalk.yellow(`toknxr browse --page ${currentPage + 1}`));
|
1471
|
+
break;
|
1472
|
+
case 'p':
|
1473
|
+
console.log(chalk.cyan(`\n📄 Going to page ${currentPage - 1}...`));
|
1474
|
+
console.log(chalk.gray('Run: ') + chalk.yellow(`toknxr browse --page ${currentPage - 1}`));
|
1475
|
+
break;
|
1476
|
+
case 'f':
|
1477
|
+
console.log(chalk.magenta('\n🔍 Opening filter interface...'));
|
1478
|
+
await program.parseAsync(['node', 'toknxr', 'filter']);
|
1479
|
+
break;
|
1480
|
+
case 's':
|
1481
|
+
console.log(chalk.yellow('\n🔍 Opening search interface...'));
|
1482
|
+
// Load interactions to generate relevant search suggestions
|
1483
|
+
const logFilePath = path.resolve(process.cwd(), 'interactions.log');
|
1484
|
+
let searchSuggestions = [];
|
1485
|
+
if (fs.existsSync(logFilePath)) {
|
1486
|
+
const fileContent = fs.readFileSync(logFilePath, 'utf8');
|
1487
|
+
const lines = fileContent.trim().split('\n');
|
1488
|
+
const interactions = lines
|
1489
|
+
.map(line => {
|
1490
|
+
try {
|
1491
|
+
return JSON.parse(line);
|
1492
|
+
}
|
1493
|
+
catch {
|
1494
|
+
return null;
|
1495
|
+
}
|
1496
|
+
})
|
1497
|
+
.filter((interaction) => interaction !== null);
|
1498
|
+
// Extract unique search suggestions from actual data
|
1499
|
+
const suggestions = new Set();
|
1500
|
+
interactions.forEach(interaction => {
|
1501
|
+
// Add providers
|
1502
|
+
if (interaction.provider)
|
1503
|
+
suggestions.add(interaction.provider);
|
1504
|
+
// Add models
|
1505
|
+
if (interaction.model && interaction.model !== 'unknown')
|
1506
|
+
suggestions.add(interaction.model);
|
1507
|
+
// Add task types
|
1508
|
+
if (interaction.taskType)
|
1509
|
+
suggestions.add(interaction.taskType);
|
1510
|
+
// Add key words from prompts
|
1511
|
+
if (interaction.userPrompt) {
|
1512
|
+
const words = interaction.userPrompt.toLowerCase().split(/\s+/);
|
1513
|
+
words.forEach(word => {
|
1514
|
+
if (word.length >= 3 && !['the', 'and', 'for', 'with', 'this', 'that', 'from', 'they', 'have', 'been', 'will', 'are', 'was', 'were'].includes(word)) {
|
1515
|
+
suggestions.add(word);
|
1516
|
+
}
|
1517
|
+
});
|
1518
|
+
}
|
1519
|
+
});
|
1520
|
+
searchSuggestions = Array.from(suggestions).slice(0, 15); // Limit to 15 suggestions
|
1521
|
+
}
|
1522
|
+
// Show search options
|
1523
|
+
const searchChoices = [
|
1524
|
+
{ name: chalk.cyan('🔍 Custom Search') + chalk.gray(' - Enter your own search terms'), value: 'custom' },
|
1525
|
+
...searchSuggestions.map(suggestion => ({
|
1526
|
+
name: `🔎 "${suggestion}"`,
|
1527
|
+
value: suggestion
|
1528
|
+
}))
|
1529
|
+
];
|
1530
|
+
const { searchChoice } = await inquirer.prompt([
|
1531
|
+
{
|
1532
|
+
type: 'list',
|
1533
|
+
name: 'searchChoice',
|
1534
|
+
message: 'What would you like to search for?',
|
1535
|
+
choices: searchChoices,
|
1536
|
+
pageSize: 10
|
1537
|
+
}
|
1538
|
+
]);
|
1539
|
+
let searchQuery;
|
1540
|
+
if (searchChoice === 'custom') {
|
1541
|
+
const { customQuery } = await inquirer.prompt([
|
1542
|
+
{
|
1543
|
+
type: 'input',
|
1544
|
+
name: 'customQuery',
|
1545
|
+
message: 'Enter your search terms:',
|
1546
|
+
validate: (input) => input.trim().length >= 2 || 'Please enter at least 2 characters'
|
1547
|
+
}
|
1548
|
+
]);
|
1549
|
+
searchQuery = customQuery;
|
1550
|
+
}
|
1551
|
+
else {
|
1552
|
+
searchQuery = searchChoice;
|
1553
|
+
}
|
1554
|
+
// Execute search command
|
1555
|
+
await program.parseAsync(['node', 'toknxr', 'search', '--query', searchQuery]);
|
1556
|
+
break;
|
1557
|
+
case 'a':
|
1558
|
+
console.log(chalk.green('\n📊 Opening analytics view...'));
|
1559
|
+
await program.parseAsync(['node', 'toknxr', 'stats']);
|
1560
|
+
break;
|
1561
|
+
case 'm':
|
1562
|
+
console.log(chalk.gray('\n📋 Opening main menu...'));
|
1563
|
+
console.log(chalk.gray('Run: ') + chalk.yellow('toknxr menu'));
|
1564
|
+
break;
|
1565
|
+
case 'q':
|
1566
|
+
console.log(chalk.gray('\n👋 Exiting browser...'));
|
1567
|
+
break;
|
1568
|
+
default:
|
1569
|
+
console.log(chalk.yellow(`Unknown option "${choice}".`));
|
1010
1570
|
}
|
1011
1571
|
});
|
1012
1572
|
program
|
@@ -1043,6 +1603,8 @@ program
|
|
1043
1603
|
console.log(chalk.blue.bold(`\n🔍 Searching for: "${query}"`));
|
1044
1604
|
const availableFields = ['provider', 'model', 'userPrompt', 'taskType', 'requestId'];
|
1045
1605
|
const searchOptions = await createSearchInterface(availableFields);
|
1606
|
+
// Set the query from the command line option
|
1607
|
+
searchOptions.query = query;
|
1046
1608
|
if (!searchOptions) {
|
1047
1609
|
console.log(chalk.gray('Search cancelled.'));
|
1048
1610
|
return;
|
@@ -1090,20 +1652,33 @@ program
|
|
1090
1652
|
let highlightColor;
|
1091
1653
|
if (score >= 0.8)
|
1092
1654
|
highlightColor = 'green';
|
1093
|
-
else if (score >= 0.
|
1655
|
+
else if (score >= 0.5)
|
1094
1656
|
highlightColor = 'yellow';
|
1095
1657
|
else
|
1096
1658
|
highlightColor = 'red';
|
1659
|
+
// Show which fields matched
|
1660
|
+
const matchedFields = [];
|
1661
|
+
searchOptions.fields.forEach(field => {
|
1662
|
+
const value = interaction[field];
|
1663
|
+
if (typeof value === 'string' && value.toLowerCase().includes(searchOptions.query.toLowerCase())) {
|
1664
|
+
matchedFields.push(field);
|
1665
|
+
}
|
1666
|
+
});
|
1097
1667
|
const highlightedPrompt = highlightMatch(interaction.userPrompt || '', searchOptions.query);
|
1668
|
+
const highlightedModel = highlightMatch(interaction.model || '', searchOptions.query);
|
1669
|
+
const highlightedProvider = highlightMatch(interaction.provider || '', searchOptions.query);
|
1098
1670
|
const colorFn = highlightColor === 'green'
|
1099
1671
|
? chalk.green
|
1100
1672
|
: highlightColor === 'yellow'
|
1101
1673
|
? chalk.yellow
|
1102
1674
|
: chalk.red;
|
1103
|
-
|
1675
|
+
const starCount = Math.max(1, Math.ceil(score * 5));
|
1676
|
+
const stars = colorFn('★'.repeat(starCount) + '☆'.repeat(5 - starCount));
|
1677
|
+
return createBox(`#${num} ${highlightedProvider} • ${highlightedModel} (${stars})`, [
|
1104
1678
|
`📅 ${chalk.gray(new Date(interaction.timestamp || Date.now()).toLocaleDateString())}`,
|
1105
1679
|
`🎯 ${highlightedPrompt || 'No prompt available'}`,
|
1106
|
-
`💰
|
1680
|
+
`💰 ${interaction.costUSD?.toFixed(4) || '0.0000'} • ⭐ ${interaction.codeQualityScore || 'N/A'}/100`,
|
1681
|
+
`🔍 Matched: ${chalk.cyan(matchedFields.join(', '))}`,
|
1107
1682
|
], {
|
1108
1683
|
borderColor: highlightColor,
|
1109
1684
|
titleColor: 'cyan',
|
@@ -1427,10 +2002,10 @@ program
|
|
1427
2002
|
console.log(chalk.gray('━'.repeat(50)));
|
1428
2003
|
console.log(chalk.bold('\n🗂️ Log File Information:'));
|
1429
2004
|
try {
|
1430
|
-
const
|
1431
|
-
if (fs.existsSync(
|
1432
|
-
const stats = fs.statSync(
|
1433
|
-
console.log(` Location: ${
|
2005
|
+
const auditLogPath = path.resolve(process.cwd(), 'audit.log');
|
2006
|
+
if (fs.existsSync(auditLogPath)) {
|
2007
|
+
const stats = fs.statSync(auditLogPath);
|
2008
|
+
console.log(` Location: ${auditLogPath}`);
|
1434
2009
|
console.log(` Size: ${stats.size} bytes (${(stats.size / 1024).toFixed(1)} KB)`);
|
1435
2010
|
console.log(` Modified: ${stats.mtime.toLocaleString()}`);
|
1436
2011
|
console.log(` Encrypted: ${auditLogger['config'].encryptionEnabled ? 'Yes' : 'No'}`);
|
@@ -1481,22 +2056,60 @@ program
|
|
1481
2056
|
.description('Validate environment, config, and runtime readiness')
|
1482
2057
|
.action(async () => {
|
1483
2058
|
const results = [];
|
1484
|
-
// Environment checks
|
1485
|
-
const geminiKey = process.env.GEMINI_API_KEY;
|
1486
|
-
const openaiKey = process.env.OPENAI_API_KEY;
|
1487
|
-
const anthropicKey = process.env.ANTHROPIC_API_KEY;
|
1488
|
-
results.push({
|
1489
|
-
label: 'AI Provider API key',
|
1490
|
-
ok: !!(geminiKey || openaiKey || anthropicKey),
|
1491
|
-
hint: 'Set GEMINI_API_KEY, OPENAI_API_KEY, or ANTHROPIC_API_KEY in your environment',
|
1492
|
-
});
|
1493
2059
|
// Filesystem checks
|
1494
2060
|
const configPath = path.resolve(process.cwd(), 'toknxr.config.json');
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
2061
|
+
let providerConfig;
|
2062
|
+
if (fs.existsSync(configPath)) {
|
2063
|
+
results.push({
|
2064
|
+
label: `Provider config at ${configPath}`,
|
2065
|
+
ok: true,
|
2066
|
+
});
|
2067
|
+
try {
|
2068
|
+
const configFile = fs.readFileSync(configPath, 'utf8');
|
2069
|
+
providerConfig = JSON.parse(configFile);
|
2070
|
+
}
|
2071
|
+
catch (error) {
|
2072
|
+
results.push({
|
2073
|
+
label: `Parse toknxr.config.json`,
|
2074
|
+
ok: false,
|
2075
|
+
hint: `Error parsing config file: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
2076
|
+
});
|
2077
|
+
}
|
2078
|
+
}
|
2079
|
+
else {
|
2080
|
+
results.push({
|
2081
|
+
label: `Provider config at ${configPath}`,
|
2082
|
+
ok: false,
|
2083
|
+
hint: 'Run: toknxr init',
|
2084
|
+
});
|
2085
|
+
}
|
2086
|
+
// Environment variable checks for each configured provider
|
2087
|
+
if (providerConfig && providerConfig.providers) {
|
2088
|
+
for (const provider of providerConfig.providers) {
|
2089
|
+
if (provider.apiKeyEnvVar) {
|
2090
|
+
const apiKey = process.env[provider.apiKeyEnvVar];
|
2091
|
+
results.push({
|
2092
|
+
label: `${provider.name} API Key (${provider.apiKeyEnvVar})`,
|
2093
|
+
ok: !!apiKey,
|
2094
|
+
hint: `Set ${provider.apiKeyEnvVar} in your environment or .env file`,
|
2095
|
+
});
|
2096
|
+
}
|
2097
|
+
else {
|
2098
|
+
results.push({
|
2099
|
+
label: `${provider.name} API Key`,
|
2100
|
+
ok: true,
|
2101
|
+
hint: 'No API key required for this provider',
|
2102
|
+
});
|
2103
|
+
}
|
2104
|
+
}
|
2105
|
+
}
|
2106
|
+
else if (providerConfig) {
|
2107
|
+
results.push({
|
2108
|
+
label: 'No providers configured in toknxr.config.json',
|
2109
|
+
ok: false,
|
2110
|
+
hint: 'Edit toknxr.config.json to add AI providers',
|
2111
|
+
});
|
2112
|
+
}
|
1500
2113
|
const logPath = path.resolve(process.cwd(), 'interactions.log');
|
1501
2114
|
try {
|
1502
2115
|
// Touch file if missing (no-op if exists)
|
@@ -1508,30 +2121,94 @@ program
|
|
1508
2121
|
results.push({ label: `interactions.log at ${logPath}`, ok: false, hint: 'Check write permissions' });
|
1509
2122
|
}
|
1510
2123
|
// Runtime checks (proxy health if running)
|
1511
|
-
let
|
2124
|
+
let proxyHealthOk = false;
|
1512
2125
|
try {
|
1513
2126
|
const res = await fetch('http://localhost:8788/health');
|
1514
|
-
|
2127
|
+
proxyHealthOk = res.ok;
|
1515
2128
|
}
|
1516
2129
|
catch {
|
1517
|
-
|
2130
|
+
proxyHealthOk = false;
|
1518
2131
|
}
|
1519
2132
|
results.push({
|
1520
|
-
label: 'Proxy
|
1521
|
-
ok:
|
2133
|
+
label: 'Proxy server running (http://localhost:8788/health)',
|
2134
|
+
ok: proxyHealthOk,
|
1522
2135
|
hint: 'Run: toknxr start (then retry doctor)'
|
1523
2136
|
});
|
1524
|
-
//
|
1525
|
-
|
1526
|
-
|
1527
|
-
const
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
2137
|
+
// AI Provider Connectivity Tests (only if proxy is running and config is loaded)
|
2138
|
+
let anyProviderConnected = false;
|
2139
|
+
if (proxyHealthOk && providerConfig && providerConfig.providers) {
|
2140
|
+
for (const provider of providerConfig.providers) {
|
2141
|
+
const apiKey = provider.apiKeyEnvVar ? process.env[provider.apiKeyEnvVar] : undefined;
|
2142
|
+
if (provider.apiKeyEnvVar && !apiKey) {
|
2143
|
+
results.push({
|
2144
|
+
label: `${provider.name} connection test`,
|
2145
|
+
ok: false,
|
2146
|
+
hint: `Skipped: API key (${provider.apiKeyEnvVar}) not set.`,
|
2147
|
+
});
|
2148
|
+
continue;
|
2149
|
+
}
|
2150
|
+
const { ok, message } = await testConnection(provider, apiKey);
|
2151
|
+
if (ok) {
|
2152
|
+
anyProviderConnected = true;
|
2153
|
+
}
|
2154
|
+
results.push({
|
2155
|
+
label: `${provider.name} connection test`,
|
2156
|
+
ok: ok,
|
2157
|
+
hint: ok ? undefined : message,
|
2158
|
+
});
|
2159
|
+
}
|
2160
|
+
}
|
2161
|
+
else if (proxyHealthOk && !providerConfig) {
|
2162
|
+
results.push({
|
2163
|
+
label: 'AI Provider connection tests',
|
2164
|
+
ok: false,
|
2165
|
+
hint: 'Skipped: toknxr.config.json not loaded or invalid.',
|
2166
|
+
});
|
2167
|
+
}
|
2168
|
+
else if (!proxyHealthOk) {
|
2169
|
+
results.push({
|
2170
|
+
label: 'AI Provider connection tests',
|
2171
|
+
ok: false,
|
2172
|
+
hint: 'Skipped: Proxy server is not running.',
|
2173
|
+
});
|
2174
|
+
}
|
2175
|
+
// Conditional: add one sample interaction if none exists and at least one provider connected
|
2176
|
+
const logFileExists = fs.existsSync(logPath);
|
2177
|
+
let logFileSize = 0;
|
2178
|
+
if (logFileExists) {
|
2179
|
+
try {
|
2180
|
+
logFileSize = fs.statSync(logPath).size;
|
2181
|
+
}
|
2182
|
+
catch { }
|
2183
|
+
}
|
2184
|
+
if (anyProviderConnected && logFileSize === 0) {
|
2185
|
+
const firstConnectedProvider = providerConfig?.providers.find((p) => {
|
2186
|
+
const apiKey = p.apiKeyEnvVar ? process.env[p.apiKeyEnvVar] : undefined;
|
2187
|
+
return p.apiKeyEnvVar ? !!apiKey : true; // Check if key is set for providers that need it
|
2188
|
+
});
|
2189
|
+
if (firstConnectedProvider) {
|
2190
|
+
const { ok, message } = generateSampleInteraction(firstConnectedProvider.name, logPath);
|
2191
|
+
results.push({
|
2192
|
+
label: 'Generate sample interaction',
|
2193
|
+
ok: ok,
|
2194
|
+
hint: ok ? 'Run: toknxr stats or toknxr code-analysis' : message,
|
2195
|
+
});
|
1532
2196
|
}
|
1533
2197
|
}
|
1534
|
-
|
2198
|
+
else if (logFileSize > 0) {
|
2199
|
+
results.push({
|
2200
|
+
label: 'Generate sample interaction',
|
2201
|
+
ok: true,
|
2202
|
+
hint: 'interactions.log already contains data.',
|
2203
|
+
});
|
2204
|
+
}
|
2205
|
+
else {
|
2206
|
+
results.push({
|
2207
|
+
label: 'Generate sample interaction',
|
2208
|
+
ok: false,
|
2209
|
+
hint: 'No connected providers or interactions.log already has data.',
|
2210
|
+
});
|
2211
|
+
}
|
1535
2212
|
// Print report
|
1536
2213
|
console.log(chalk.blue.bold('\nTokNXR Doctor Report'));
|
1537
2214
|
console.log(chalk.gray('━'.repeat(60)));
|
@@ -1544,7 +2221,7 @@ program
|
|
1544
2221
|
}
|
1545
2222
|
console.log(chalk.gray('━'.repeat(60)));
|
1546
2223
|
if (allOk) {
|
1547
|
-
console.log(chalk.green('All checks passed. You are ready to use TokNXR.'));
|
2224
|
+
console.log(chalk.green('All essential checks passed. You are ready to use TokNXR.'));
|
1548
2225
|
}
|
1549
2226
|
else {
|
1550
2227
|
console.log(chalk.yellow('Some checks failed. Fix the hints above and re-run: toknxr doctor'));
|
@@ -1576,4 +2253,10 @@ function highlightMatch(text, query) {
|
|
1576
2253
|
});
|
1577
2254
|
return highlighted;
|
1578
2255
|
}
|
1579
|
-
program.
|
2256
|
+
program.exitOverride();
|
2257
|
+
try {
|
2258
|
+
program.parse(process.argv);
|
2259
|
+
}
|
2260
|
+
catch (e) {
|
2261
|
+
// This will catch the exit override and prevent the process from exiting
|
2262
|
+
}
|