@goldensheepai/toknxr-cli 0.2.0 → 0.2.2

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.
@@ -0,0 +1,49 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { spawn } from 'node:child_process';
3
+ import path from 'node:path';
4
+ const CLI = path.resolve(process.cwd(), 'toknxr-cli', 'lib', 'cli.js');
5
+ function runCli(args, env = {}, timeoutMs = 8000) {
6
+ return new Promise(resolve => {
7
+ const child = spawn('node', [CLI, ...args], {
8
+ env: {
9
+ ...process.env,
10
+ SUPABASE_URL: process.env.SUPABASE_URL || 'http://localhost',
11
+ SUPABASE_KEY: process.env.SUPABASE_KEY || 'dummy',
12
+ ...env,
13
+ },
14
+ });
15
+ let stdout = '';
16
+ let stderr = '';
17
+ const timer = setTimeout(() => {
18
+ child.kill('SIGKILL');
19
+ }, timeoutMs);
20
+ child.stdout.on('data', d => (stdout += String(d)));
21
+ child.stderr.on('data', d => (stderr += String(d)));
22
+ child.on('close', code => {
23
+ clearTimeout(timer);
24
+ resolve({ code, stdout, stderr });
25
+ });
26
+ });
27
+ }
28
+ describe('TokNXR CLI smoke tests', () => {
29
+ it('--help renders', async () => {
30
+ const res = await runCli(['--help']);
31
+ expect(res.code).toBe(0);
32
+ expect(res.stdout).toContain('Usage: toknxr');
33
+ });
34
+ it('budget --view runs', async () => {
35
+ const res = await runCli(['budget', '--view']);
36
+ expect(res.code).toBe(0);
37
+ expect(res.stdout).toContain('Budget Configuration');
38
+ });
39
+ it('audit:view --help renders', async () => {
40
+ const res = await runCli(['audit:view', '--help']);
41
+ expect(res.code).toBe(0);
42
+ expect(res.stdout).toContain('--to <date>');
43
+ });
44
+ it('audit:export --help renders', async () => {
45
+ const res = await runCli(['audit:export', '--help']);
46
+ expect(res.code).toBe(0);
47
+ expect(res.stdout).toContain('--to <date>');
48
+ });
49
+ });
@@ -1,3 +1,4 @@
1
+ import { pluginManager } from './plugin-system.js';
1
2
  /**
2
3
  * Analyzes code quality and provides metrics
3
4
  */
@@ -41,6 +42,34 @@ export function analyzeCodeQuality(code, language) {
41
42
  metrics.syntaxValid = validatePython(code);
42
43
  metrics.estimatedReadability = calculatePythonReadability(code);
43
44
  break;
45
+ case 'go':
46
+ metrics.hasFunctions = /func\s+\w+\(/.test(code);
47
+ metrics.hasClasses = /type\s+\w+\s+struct/.test(code);
48
+ metrics.hasTests = /func\s+Test\w+/.test(code);
49
+ metrics.syntaxValid = validateGo(code);
50
+ metrics.estimatedReadability = calculateGoReadability(code);
51
+ break;
52
+ case 'rust':
53
+ metrics.hasFunctions = /fn\s+\w+\(/.test(code);
54
+ metrics.hasClasses = /(struct|enum)\s+\w+/.test(code);
55
+ metrics.hasTests = /#\[test\]/.test(code);
56
+ metrics.syntaxValid = validateRust(code);
57
+ metrics.estimatedReadability = calculateRustReadability(code);
58
+ break;
59
+ case 'java':
60
+ metrics.hasFunctions = /public\s+(\w+)\s+\w+\(/.test(code);
61
+ metrics.hasClasses = /public\s+class\s+\w+/.test(code);
62
+ metrics.hasTests = /@Test/.test(code);
63
+ metrics.syntaxValid = validateJava(code);
64
+ metrics.estimatedReadability = calculateJavaReadability(code);
65
+ break;
66
+ case 'cpp':
67
+ metrics.hasFunctions = /(\w+)\s+\w+\(/.test(code) && !/class\s+/.test(code);
68
+ metrics.hasClasses = /class\s+\w+/.test(code);
69
+ metrics.hasTests = /(TEST|TEST_F)/.test(code);
70
+ metrics.syntaxValid = validateCpp(code);
71
+ metrics.estimatedReadability = calculateCppReadability(code);
72
+ break;
44
73
  default:
45
74
  metrics.potentialIssues.push('Language not recognized for detailed analysis');
46
75
  }
@@ -51,7 +80,15 @@ export function analyzeCodeQuality(code, language) {
51
80
  if (!metrics.syntaxValid) {
52
81
  metrics.potentialIssues.push('Potential syntax errors detected');
53
82
  }
54
- return metrics;
83
+ // Apply plugin enhancements
84
+ try {
85
+ const enhancedMetrics = pluginManager.analyzeWithPlugins(code, metrics);
86
+ return enhancedMetrics;
87
+ }
88
+ catch (error) {
89
+ console.warn('Plugin analysis failed, returning base metrics:', error);
90
+ return metrics;
91
+ }
55
92
  }
56
93
  /**
57
94
  * Calculate an effectiveness score comparing prompt to code output
@@ -81,7 +118,15 @@ export function scoreEffectiveness(userPrompt, aiResponse, extractedCode) {
81
118
  (score.codeCompleteness * 0.25) +
82
119
  (score.codeCorrectness * 0.25) +
83
120
  (score.codeEfficiency * 0.2));
84
- return score;
121
+ // Apply plugin enhancements for effectiveness scoring
122
+ try {
123
+ const enhancedScore = pluginManager.scoreEffectivenessWithPlugins(userPrompt, aiResponse, score, extractedCode);
124
+ return enhancedScore;
125
+ }
126
+ catch (error) {
127
+ console.warn('Plugin effectiveness scoring failed, returning base score:', error);
128
+ return score;
129
+ }
85
130
  }
86
131
  /**
87
132
  * Extract code blocks from AI responses
@@ -113,12 +158,27 @@ export function extractCodeFromResponse(response) {
113
158
  */
114
159
  function detectLanguage(code) {
115
160
  if (/(?:import|export|function|const|let|var)\s+/.test(code)) {
116
- return code.includes('interface') || code.includes(': string') ? 'typescript' : 'javascript';
161
+ return code.includes('interface') || code.includes(': string') || code.includes('<') ? 'typescript' : 'javascript';
117
162
  }
118
163
  if (/def\s+|import\s+|class\s+/.test(code) && /:/.test(code)) {
119
164
  return 'python';
120
165
  }
121
- // Add more language detection as needed...
166
+ // Go detection
167
+ if (/package\s+\w+|import\s+\(|func\s+\w+\(/.test(code) && /\.\w+\(/.test(code)) {
168
+ return 'go';
169
+ }
170
+ // Rust detection
171
+ if (/fn\s+\w+|let\s+mut|println!|use\s+std::/.test(code) && /::/.test(code)) {
172
+ return 'rust';
173
+ }
174
+ // Java detection
175
+ if (/public\s+class|import\s+java\.|public\s+static\s+void\s+main/.test(code) && /System\.out\.println/.test(code)) {
176
+ return 'java';
177
+ }
178
+ // C++ detection
179
+ if (/#include\s+<|std::|cout\s+<<|int\s+main\(/.test(code) && /namespace\s+std/.test(code)) {
180
+ return 'cpp';
181
+ }
122
182
  return undefined;
123
183
  }
124
184
  /**
@@ -302,3 +362,288 @@ function estimateEfficiencyScore(code, metrics) {
302
362
  }
303
363
  return Math.max(0, Math.min(100, score));
304
364
  }
365
+ /**
366
+ * Basic Go validation
367
+ */
368
+ function validateGo(code) {
369
+ try {
370
+ // Basic bracket matching for Go
371
+ const brackets = { '(': 0, '{': 0 };
372
+ let inString = false;
373
+ let inComment = false;
374
+ for (let i = 0; i < code.length; i++) {
375
+ const char = code[i];
376
+ const nextChar = code[i + 1] || '';
377
+ // Handle strings
378
+ if (char === '"' && (i === 0 || code[i - 1] !== '\\')) {
379
+ inString = !inString;
380
+ continue;
381
+ }
382
+ if (inString)
383
+ continue;
384
+ // Handle comments
385
+ if (char === '/' && nextChar === '/') {
386
+ inComment = true;
387
+ continue;
388
+ }
389
+ if (char === '\n') {
390
+ inComment = false;
391
+ continue;
392
+ }
393
+ if (inComment)
394
+ continue;
395
+ if (char === '(')
396
+ brackets['(']++;
397
+ if (char === ')')
398
+ brackets['(']--;
399
+ if (char === '{')
400
+ brackets['{']++;
401
+ if (char === '}')
402
+ brackets['{']--;
403
+ if (brackets['('] < 0 || brackets['{'] < 0) {
404
+ return false;
405
+ }
406
+ }
407
+ return brackets['('] === 0 && brackets['{'] === 0;
408
+ }
409
+ catch {
410
+ return false;
411
+ }
412
+ }
413
+ /**
414
+ * Basic Rust validation
415
+ */
416
+ function validateRust(code) {
417
+ try {
418
+ // Basic bracket matching for Rust
419
+ const brackets = { '(': 0, '[': 0, '{': 0 };
420
+ let inString = false;
421
+ let inChar = false;
422
+ for (let i = 0; i < code.length; i++) {
423
+ const char = code[i];
424
+ // Handle strings and characters
425
+ if (char === '"' && (i === 0 || code[i - 1] !== '\\')) {
426
+ inString = !inString;
427
+ continue;
428
+ }
429
+ if (char === "'" && code[i + 1] && code[i + 1] !== "\\") {
430
+ inChar = !inChar;
431
+ continue;
432
+ }
433
+ if (inString || inChar)
434
+ continue;
435
+ if (char === '(')
436
+ brackets['(']++;
437
+ if (char === ')')
438
+ brackets['(']--;
439
+ if (char === '[')
440
+ brackets['[']++;
441
+ if (char === ']')
442
+ brackets['[']--;
443
+ if (char === '{')
444
+ brackets['{']++;
445
+ if (char === '}')
446
+ brackets['{']--;
447
+ if (brackets['('] < 0 || brackets['['] < 0 || brackets['{'] < 0) {
448
+ return false;
449
+ }
450
+ }
451
+ return brackets['('] === 0 && brackets['['] === 0 && brackets['{'] === 0;
452
+ }
453
+ catch {
454
+ return false;
455
+ }
456
+ }
457
+ /**
458
+ * Basic Java validation
459
+ */
460
+ function validateJava(code) {
461
+ try {
462
+ // Basic bracket matching for Java
463
+ const brackets = { '(': 0, '[': 0, '{': 0 };
464
+ let inString = false;
465
+ for (let i = 0; i < code.length; i++) {
466
+ const char = code[i];
467
+ // Handle strings
468
+ if (char === '"' && (i === 0 || code[i - 1] !== '\\')) {
469
+ inString = !inString;
470
+ continue;
471
+ }
472
+ if (inString)
473
+ continue;
474
+ if (char === '(')
475
+ brackets['(']++;
476
+ if (char === ')')
477
+ brackets['(']--;
478
+ if (char === '[')
479
+ brackets['[']++;
480
+ if (char === ']')
481
+ brackets['[']--;
482
+ if (char === '{')
483
+ brackets['{']++;
484
+ if (char === '}')
485
+ brackets['{']--;
486
+ if (brackets['('] < 0 || brackets['['] < 0 || brackets['{'] < 0) {
487
+ return false;
488
+ }
489
+ }
490
+ return brackets['('] === 0 && brackets['['] === 0 && brackets['{'] === 0;
491
+ }
492
+ catch {
493
+ return false;
494
+ }
495
+ }
496
+ /**
497
+ * Basic C++ validation
498
+ */
499
+ function validateCpp(code) {
500
+ try {
501
+ // Basic bracket matching for C++
502
+ const brackets = { '(': 0, '[': 0, '{': 0 };
503
+ let inString = false;
504
+ let inComment = false;
505
+ for (let i = 0; i < code.length; i++) {
506
+ const char = code[i];
507
+ const nextChar = code[i + 1] || '';
508
+ // Handle strings
509
+ if (char === '"' && (i === 0 || code[i - 1] !== '\\')) {
510
+ inString = !inString;
511
+ continue;
512
+ }
513
+ if (inString)
514
+ continue;
515
+ // Handle comments
516
+ if (char === '/' && (nextChar === '/' || nextChar === '*')) {
517
+ inComment = true;
518
+ if (nextChar === '*') {
519
+ // Multi-line comment
520
+ const endComment = code.indexOf('*/', i + 2);
521
+ if (endComment !== -1) {
522
+ i = endComment + 1;
523
+ }
524
+ continue;
525
+ }
526
+ }
527
+ if (inComment && char === '\n') {
528
+ inComment = false;
529
+ }
530
+ if (inComment)
531
+ continue;
532
+ if (char === '(')
533
+ brackets['(']++;
534
+ if (char === ')')
535
+ brackets['(']--;
536
+ if (char === '[')
537
+ brackets['[']++;
538
+ if (char === ']')
539
+ brackets['[']--;
540
+ if (char === '{')
541
+ brackets['{']++;
542
+ if (char === '}')
543
+ brackets['{']--;
544
+ if (brackets['('] < 0 || brackets['['] < 0 || brackets['{'] < 0) {
545
+ return false;
546
+ }
547
+ }
548
+ return brackets['('] === 0 && brackets['['] === 0 && brackets['{'] === 0;
549
+ }
550
+ catch {
551
+ return false;
552
+ }
553
+ }
554
+ /**
555
+ * Calculate readability for Go
556
+ */
557
+ function calculateGoReadability(code) {
558
+ let score = 6; // Go is generally readable
559
+ // Length factors
560
+ if (code.length > 2000)
561
+ score -= 2;
562
+ if (code.split('\n').length > 50)
563
+ score -= 1;
564
+ // Good practices
565
+ if (code.includes('//'))
566
+ score += 1; // Has comments
567
+ if (/^\s*\w+\./.test(code))
568
+ score += 0.5; // Uses qualified names
569
+ if (/err\s*!=\s*nil/.test(code))
570
+ score += 0.5; // Error handling
571
+ // Poor practices
572
+ if (/\w+\s*:=\s*[^;]*;/.test(code))
573
+ score -= 1; // Short variable declarations with semicolon
574
+ return Math.max(1, Math.min(10, score));
575
+ }
576
+ /**
577
+ * Calculate readability for Rust
578
+ */
579
+ function calculateRustReadability(code) {
580
+ let score = 5; // Rust can be complex but expressive
581
+ // Length factors
582
+ if (code.length > 2500)
583
+ score -= 2;
584
+ if (code.split('\n').length > 60)
585
+ score -= 1;
586
+ // Good practices
587
+ if (code.includes('//'))
588
+ score += 1; // Has comments
589
+ if (/let\s+/.test(code))
590
+ score += 0.5; // Explicit variable declarations
591
+ if (/->/.test(code))
592
+ score += 0.5; // Return type annotations
593
+ if (/\?/m.test(code))
594
+ score += 0.5; // Error propagation
595
+ // Code organization
596
+ if (/impl\s+\w+/.test(code))
597
+ score += 0.5; // Has implementations
598
+ if (/trait\s+\w+/.test(code))
599
+ score += 0.5; // Has traits
600
+ return Math.max(1, Math.min(10, score));
601
+ }
602
+ /**
603
+ * Calculate readability for Java
604
+ */
605
+ function calculateJavaReadability(code) {
606
+ let score = 4; // Java can be verbose
607
+ // Length factors
608
+ if (code.length > 3000)
609
+ score -= 2;
610
+ if (code.split('\n').length > 70)
611
+ score -= 1;
612
+ // Good practices
613
+ if (code.includes('//') || code.includes('/*'))
614
+ score += 1; // Has comments
615
+ if (/public\s+class\s+\w+/.test(code))
616
+ score += 0.5; // Proper class declaration
617
+ if (/throws\s+\w+/.test(code))
618
+ score += 0.5; // Exception declarations
619
+ // Code organization
620
+ if (/@Override/.test(code))
621
+ score += 0.5; // Uses annotations properly
622
+ if (/import\s+java\./.test(code))
623
+ score += 0.5; // Proper imports
624
+ return Math.max(1, Math.min(10, score));
625
+ }
626
+ /**
627
+ * Calculate readability for C++
628
+ */
629
+ function calculateCppReadability(code) {
630
+ let score = 3; // C++ can be complex
631
+ // Length factors
632
+ if (code.length > 2500)
633
+ score -= 2;
634
+ if (code.split('\n').length > 80)
635
+ score -= 1;
636
+ // Good practices
637
+ if (code.includes('//'))
638
+ score += 1; // Has comments
639
+ if (/std::/.test(code))
640
+ score += 0.5; // Uses standard library
641
+ if (/const\s+/.test(code))
642
+ score += 0.5; // Uses const
643
+ // Code organization
644
+ if (/namespace\s+\w+/.test(code))
645
+ score += 0.5; // Uses namespaces
646
+ if (/#include\s+<[\w\/]+>/.test(code))
647
+ score += 0.5; // Proper includes
648
+ return Math.max(1, Math.min(10, score));
649
+ }
package/lib/dashboard.js CHANGED
@@ -13,22 +13,7 @@ function createDashboard() {
13
13
  text: '#F8FAFC',
14
14
  textMuted: '#94A3B8'
15
15
  };
16
- let stats = null;
17
16
  let lastUpdated = new Date();
18
- // Create stat card HTML
19
- function createStatCard(title, value, icon, color = COLORS.primary) {
20
- return `
21
- <div style="background: ${COLORS.surface}; border-radius: 12px; padding: 24px; border: 1px solid ${color}20; box-shadow: 0 4px 6px -1px ${color}10;">
22
- <div style="display: flex; align-items: center; justify-content: space-between;">
23
- <div>
24
- <p style="color: ${COLORS.textMuted}; font-size: 14px; margin: 0 0 8px 0;">${title}</p>
25
- <p style="font-size: 32px; font-weight: bold; margin: 0; color: ${COLORS.text};">${value}</p>
26
- </div>
27
- <div style="font-size: 24px; color: ${color}; opacity: 0.8;">${icon}</div>
28
- </div>
29
- </div>
30
- `;
31
- }
32
17
  // Create provider card HTML
33
18
  function createProviderChart(name, data) {
34
19
  return `
@@ -132,8 +117,8 @@ function createDashboard() {
132
117
  const providerHTML = Object.entries(data.totals.byProvider).map(([name, providerData]) => createProviderChart(name, {
133
118
  cost: providerData.costUSD || 0,
134
119
  interactions: providerData.requestCount || 0,
135
- avgQuality: providerData.avgQualityScore,
136
- avgEffectiveness: providerData.avgEffectivenessScore
120
+ avgQuality: providerData.avgQuality,
121
+ avgEffectiveness: providerData.avgEffectiveness
137
122
  })).join('');
138
123
  providerContainer.innerHTML = providerHTML;
139
124
  }
@@ -265,6 +250,7 @@ function createDashboard() {
265
250
  }
266
251
  }
267
252
  // Modal for showing prompt details
253
+ // @ts-ignore
268
254
  function showPromptDetails(provider, model, userPrompt, aiResponse, qualityScore, effectivenessScore, taskType, cost) {
269
255
  const modal = document.createElement('div');
270
256
  modal.id = 'prompt-modal';
@@ -341,6 +327,7 @@ function closePromptModal() {
341
327
  }
342
328
  }
343
329
  // Filter prompts based on search and provider filter
330
+ // @ts-ignore
344
331
  function filterPrompts() {
345
332
  const searchTermValue = document.getElementById('prompt-search')?.value.toLowerCase() || '';
346
333
  const providerFilterValue = document.getElementById('provider-filter')?.value || '';
@@ -0,0 +1,18 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ export function appendFixtureInteraction() {
4
+ const logPath = path.resolve(process.cwd(), 'interactions.log');
5
+ const entry = {
6
+ timestamp: new Date().toISOString(),
7
+ provider: 'fixture',
8
+ model: 'fixture-model',
9
+ promptTokens: 5,
10
+ completionTokens: 10,
11
+ totalTokens: 15,
12
+ costUSD: 0,
13
+ taskType: 'chat',
14
+ userPrompt: 'Fixture entry used to validate tooling path',
15
+ aiResponse: 'This is a fixture interaction entry.',
16
+ };
17
+ fs.appendFileSync(logPath, JSON.stringify(entry) + '\n');
18
+ }