llm-checker 3.5.13 → 3.5.15
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/package.json +1 -1
- package/src/ui/banner-profesional-v2.txt +36 -0
- package/src/ui/cli-theme.js +15 -65
- package/src/ui/interactive-panel.js +23 -5
package/package.json
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
+--------------------------------------------------------------------------------------------------------------------------------------------------+
|
|
2
|
+
| |
|
|
3
|
+
| [ INTELLIGENT OLLAMA MODEL SELECTOR ] |
|
|
4
|
+
| -------------------------------------------------------------------------------------- |
|
|
5
|
+
| |
|
|
6
|
+
| █████ █████ ██████ ██████ |
|
|
7
|
+
| ▒▒███ ▒▒███ ▒▒██████ ██████ |
|
|
8
|
+
| ▒███ ▒███ ▒███▒█████▒███ |
|
|
9
|
+
| ▒███ ▒███ ▒███▒▒███ ▒███ |
|
|
10
|
+
| ▒███ ▒███ ▒███ ▒▒▒ ▒███ |
|
|
11
|
+
| ▒███ █ ▒███ █ ▒███ ▒███ |
|
|
12
|
+
| ███████████ ███████████ █████ █████ |
|
|
13
|
+
| ▒▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ |
|
|
14
|
+
| |
|
|
15
|
+
| █████████ █████ █████ ██████████ █████████ █████ ████ ██████████ ███████████ |
|
|
16
|
+
| ███▒▒▒▒▒███▒▒███ ▒▒███ ▒▒███▒▒▒▒▒█ ███▒▒▒▒▒███▒▒███ ███▒ ▒▒███▒▒▒▒▒█▒▒███▒▒▒▒▒███ |
|
|
17
|
+
| ███ ▒▒▒ ▒███ ▒███ ▒███ █ ▒ ███ ▒▒▒ ▒███ ███ ▒███ █ ▒ ▒███ ▒███ |
|
|
18
|
+
|▒███ ▒███████████ ▒██████ ▒███ ▒███████ ▒██████ ▒██████████ |
|
|
19
|
+
|▒███ ▒███▒▒▒▒▒███ ▒███▒▒█ ▒███ ▒███▒▒███ ▒███▒▒█ ▒███▒▒▒▒▒███ |
|
|
20
|
+
|▒▒███ ███ ▒███ ▒███ ▒███ ▒ █▒▒███ ███ ▒███ ▒▒███ ▒███ ▒ █ ▒███ ▒███ |
|
|
21
|
+
| ▒▒█████████ █████ █████ ██████████ ▒▒█████████ █████ ▒▒████ ██████████ █████ █████ |
|
|
22
|
+
| ▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ |
|
|
23
|
+
| |
|
|
24
|
+
| AI-powered CLI for hardware-aware local LLM recommendations |
|
|
25
|
+
| Deterministic scoring across 200+ dynamic models (35+ curated fallback) |
|
|
26
|
+
| |
|
|
27
|
+
| [200+ DYNAMIC MODELS] [35+ FALLBACK] [4D SCORING] [MCP SERVER] |
|
|
28
|
+
| |
|
|
29
|
+
| ---------------------------------------------------------------------------------------------- |
|
|
30
|
+
| |
|
|
31
|
+
| Install: npm install -g llm-checker |
|
|
32
|
+
| Run: llm-checker recommend |
|
|
33
|
+
| |
|
|
34
|
+
| github.com/Pavelevich/llm-checker | npmjs.com/package/llm-checker |
|
|
35
|
+
| |
|
|
36
|
+
+--------------------------------------------------------------------------------------------------------------------------------------------------+
|
package/src/ui/cli-theme.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
const chalk = require('chalk');
|
|
4
4
|
const { execFileSync } = require('child_process');
|
|
5
5
|
const fs = require('fs');
|
|
6
|
-
const os = require('os');
|
|
7
6
|
const path = require('path');
|
|
7
|
+
const readline = require('readline');
|
|
8
8
|
|
|
9
9
|
// Adapted from /Users/pchmirenko/Downloads/ascii-motion-cli.tsx frame model.
|
|
10
10
|
const THEME_DARK = {
|
|
@@ -37,32 +37,16 @@ const LOGO_LINES = [
|
|
|
37
37
|
'|_____||_____||_| |_| \\____|_| |_|\\___|\\___|_|\\_\\___|_| '
|
|
38
38
|
];
|
|
39
39
|
|
|
40
|
-
const MASCOT_MASK = [
|
|
41
|
-
' /\\_/\\ ',
|
|
42
|
-
' / o o \\ ',
|
|
43
|
-
' ( ^ ) ',
|
|
44
|
-
' \\ _ / ',
|
|
45
|
-
' /___\\ ',
|
|
46
|
-
' / \\ ',
|
|
47
|
-
' (_/ \\_) '
|
|
48
|
-
];
|
|
49
|
-
|
|
50
40
|
const DEFAULT_LOOP = true;
|
|
51
41
|
const FRAMES_PER_SECOND = 14;
|
|
52
42
|
// Security: do not auto-load executable-style banner sources from user-writable folders.
|
|
53
43
|
// External banner loading is opt-in via LLM_CHECKER_BANNER_SOURCE and supports JSON only.
|
|
54
44
|
const DEFAULT_BANNER_SOURCE = null;
|
|
45
|
+
// Canonical startup banner asset. Do not edit casually: treat as product identity.
|
|
46
|
+
// Any intentional change should update tests/banner-canonical.test.js in the same commit.
|
|
47
|
+
const BUNDLED_TEXT_BANNER_SOURCE = path.join(__dirname, 'banner-profesional-v2.txt');
|
|
55
48
|
const DEFAULT_TEXT_BANNER_SOURCES = [
|
|
56
|
-
|
|
57
|
-
path.join(
|
|
58
|
-
os.homedir(),
|
|
59
|
-
'Library',
|
|
60
|
-
'Mobile Documents',
|
|
61
|
-
'com~apple~CloudDocs',
|
|
62
|
-
'Desktop',
|
|
63
|
-
'llm-checker',
|
|
64
|
-
'banner-profesional-v2.txt'
|
|
65
|
-
)
|
|
49
|
+
BUNDLED_TEXT_BANNER_SOURCE
|
|
66
50
|
];
|
|
67
51
|
let cachedExternalBanner = null;
|
|
68
52
|
let cachedTextBanner = null;
|
|
@@ -98,7 +82,14 @@ function sleep(ms) {
|
|
|
98
82
|
}
|
|
99
83
|
|
|
100
84
|
function clearTerminal() {
|
|
101
|
-
process.stdout.
|
|
85
|
+
if (!process.stdout.isTTY) return;
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
readline.cursorTo(process.stdout, 0, 0);
|
|
89
|
+
readline.clearScreenDown(process.stdout);
|
|
90
|
+
} catch {
|
|
91
|
+
process.stdout.write('\x1b[2J\x1b[H');
|
|
92
|
+
}
|
|
102
93
|
}
|
|
103
94
|
|
|
104
95
|
function parseDarkBackgroundValue(value) {
|
|
@@ -484,41 +475,9 @@ function drawTextBanner(lines, options = {}) {
|
|
|
484
475
|
}
|
|
485
476
|
}
|
|
486
477
|
|
|
487
|
-
function buildScanline(width, row, phase) {
|
|
488
|
-
const stripe = (row + phase) % 2 === 0 ? '=' : '-';
|
|
489
|
-
return stripe.repeat(width);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
function applyMask(baseLine, maskLine) {
|
|
493
|
-
if (!maskLine) return baseLine;
|
|
494
|
-
|
|
495
|
-
const result = baseLine.split('');
|
|
496
|
-
const limit = Math.min(baseLine.length, maskLine.length);
|
|
497
|
-
for (let index = 0; index < limit; index += 1) {
|
|
498
|
-
const symbol = maskLine[index];
|
|
499
|
-
if (symbol !== ' ') result[index] = symbol;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
return result.join('');
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
function buildMascotLines(phase) {
|
|
506
|
-
const width = 34;
|
|
507
|
-
const rows = 11;
|
|
508
|
-
const maskOffset = 2;
|
|
509
|
-
const lines = [];
|
|
510
|
-
|
|
511
|
-
for (let row = 0; row < rows; row += 1) {
|
|
512
|
-
const maskLine = MASCOT_MASK[row - maskOffset];
|
|
513
|
-
lines.push(applyMask(buildScanline(width, row, phase), maskLine));
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
return lines;
|
|
517
|
-
}
|
|
518
|
-
|
|
519
478
|
function buildRows(phase) {
|
|
479
|
+
void phase;
|
|
520
480
|
return [
|
|
521
|
-
...buildMascotLines(phase).map((text) => ({ kind: 'mascot', text })),
|
|
522
481
|
{ kind: 'blank', text: '' },
|
|
523
482
|
...LOGO_LINES.map((text) => ({ kind: 'logo', text })),
|
|
524
483
|
{ kind: 'blank', text: '' },
|
|
@@ -527,23 +486,14 @@ function buildRows(phase) {
|
|
|
527
486
|
];
|
|
528
487
|
}
|
|
529
488
|
|
|
530
|
-
function classifyMascotColor(char) {
|
|
531
|
-
if (char === '=' || char === '-') return 'scan';
|
|
532
|
-
if (char === 'o' || char === '^') return 'accent';
|
|
533
|
-
if (char === '/' || char === '\\' || char === '(' || char === ')' || char === '_') {
|
|
534
|
-
return 'outline';
|
|
535
|
-
}
|
|
536
|
-
return 'outline';
|
|
537
|
-
}
|
|
538
|
-
|
|
539
489
|
function colorKeyForChar(kind, char, visible) {
|
|
490
|
+
void char;
|
|
540
491
|
if (!visible) return 'muted';
|
|
541
492
|
|
|
542
493
|
if (kind === 'blank') return 'muted';
|
|
543
494
|
if (kind === 'logo') return 'logo';
|
|
544
495
|
if (kind === 'byline') return 'byline';
|
|
545
496
|
if (kind === 'subtitle') return 'subtitle';
|
|
546
|
-
if (kind === 'mascot') return classifyMascotColor(char);
|
|
547
497
|
return 'logo';
|
|
548
498
|
}
|
|
549
499
|
|
|
@@ -32,7 +32,14 @@ const REQUIRED_ARG_PROMPTS = {
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
function clearTerminal() {
|
|
35
|
-
process.stdout.
|
|
35
|
+
if (!process.stdout.isTTY) return;
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
readline.cursorTo(process.stdout, 0, 0);
|
|
39
|
+
readline.clearScreenDown(process.stdout);
|
|
40
|
+
} catch {
|
|
41
|
+
process.stdout.write('\x1b[2J\x1b[H');
|
|
42
|
+
}
|
|
36
43
|
}
|
|
37
44
|
|
|
38
45
|
function truncateText(text, maxLength) {
|
|
@@ -311,6 +318,18 @@ function renderPanel(state, catalog, primaryCommands, options = {}) {
|
|
|
311
318
|
);
|
|
312
319
|
}
|
|
313
320
|
|
|
321
|
+
function shouldEnableBannerPulse({
|
|
322
|
+
isTTY = process.stdout.isTTY,
|
|
323
|
+
disableAnimation = process.env.LLM_CHECKER_DISABLE_ANIMATION,
|
|
324
|
+
forcePulse = process.env.LLM_CHECKER_FORCE_PANEL_PULSE,
|
|
325
|
+
platform = process.platform
|
|
326
|
+
} = {}) {
|
|
327
|
+
if (!isTTY) return false;
|
|
328
|
+
if (disableAnimation === '1') return false;
|
|
329
|
+
if (forcePulse === '1') return true;
|
|
330
|
+
return platform !== 'win32';
|
|
331
|
+
}
|
|
332
|
+
|
|
314
333
|
async function collectCommandArgs(commandMeta) {
|
|
315
334
|
const prompts = [];
|
|
316
335
|
const requiredPrompts = REQUIRED_ARG_PROMPTS[commandMeta.name] || [];
|
|
@@ -421,9 +440,7 @@ async function launchInteractivePanel(options) {
|
|
|
421
440
|
readline.emitKeypressEvents(process.stdin);
|
|
422
441
|
|
|
423
442
|
return new Promise((resolve) => {
|
|
424
|
-
const shouldPulseBanner =
|
|
425
|
-
process.stdout.isTTY &&
|
|
426
|
-
process.env.LLM_CHECKER_DISABLE_ANIMATION !== '1';
|
|
443
|
+
const shouldPulseBanner = shouldEnableBannerPulse();
|
|
427
444
|
let pulseTimer = null;
|
|
428
445
|
|
|
429
446
|
const stopBannerPulse = () => {
|
|
@@ -578,6 +595,7 @@ module.exports = {
|
|
|
578
595
|
buildCommandCatalog,
|
|
579
596
|
buildPrimaryCommands,
|
|
580
597
|
getVisibleCommands,
|
|
581
|
-
truncateText
|
|
598
|
+
truncateText,
|
|
599
|
+
shouldEnableBannerPulse
|
|
582
600
|
}
|
|
583
601
|
};
|