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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llm-checker",
3
- "version": "3.5.13",
3
+ "version": "3.5.15",
4
4
  "description": "Intelligent CLI tool with AI-powered model selection that analyzes your hardware and recommends optimal LLM models for your system",
5
5
  "bin": {
6
6
  "llm-checker": "bin/cli.js",
@@ -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
+ +--------------------------------------------------------------------------------------------------------------------------------------------------+
@@ -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
- path.join(os.homedir(), 'Desktop', 'llm-checker', 'banner-profesional-v2.txt'),
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.write('\x1b[2J\x1b[0f');
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.write('\x1b[2J\x1b[0f');
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
  };