llm-checker 3.5.11 → 3.5.13

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.
@@ -2,8 +2,8 @@ const chalk = require('chalk');
2
2
  const ora = require('ora');
3
3
 
4
4
  /**
5
- * Verbose Progress Reporter - Enhanced Visual Style
6
- * Muestra operaciones paso a paso con barras de progreso y spinners
5
+ * Minimal activity indicator used while analysis is running.
6
+ * It intentionally keeps output to one animated line, then clears before results.
7
7
  */
8
8
  class VerboseProgress {
9
9
  constructor(enabled = true) {
@@ -12,255 +12,212 @@ class VerboseProgress {
12
12
  this.totalSteps = 0;
13
13
  this.operationTitle = '';
14
14
  this.startTime = null;
15
- this.stepTimes = [];
16
15
  this.currentSpinner = null;
17
- this.stepStartTime = null;
16
+ this.animationTimer = null;
17
+ this.currentVerb = 'thinking';
18
+ this.currentDetail = '';
19
+ this.colorPhase = 0;
18
20
  }
19
21
 
20
- /**
21
- * Inicia una nueva operación con múltiples pasos
22
- */
23
22
  startOperation(title, totalSteps) {
24
- if (!this.enabled) return;
25
-
23
+ if (!this.enabled) return this;
24
+
26
25
  this.operationTitle = title;
27
26
  this.totalSteps = totalSteps;
28
27
  this.currentStep = 0;
29
28
  this.startTime = Date.now();
30
- this.stepTimes = [];
31
-
32
- console.log(''); // Espacio inicial
29
+ this.currentVerb = 'thinking';
30
+ this.currentDetail = '';
31
+
32
+ if (!this.shouldAnimate()) return this;
33
+
34
+ this.currentSpinner = ora({
35
+ text: this.renderActivityText(),
36
+ spinner: 'dots'
37
+ }).start();
38
+
39
+ this.animationTimer = setInterval(() => {
40
+ this.colorPhase += 1;
41
+ this.updateSpinner();
42
+ }, 120);
43
+
44
+ return this;
33
45
  }
34
46
 
35
- /**
36
- * Avanza al siguiente paso de la operación
37
- */
38
47
  step(description, details = null) {
39
- if (!this.enabled) return;
40
-
41
- // Finalizar spinner anterior si existe (disabled)
42
- if (this.currentSpinner) {
43
- // this.currentSpinner.stop(); // Disabled to prevent UI issues
44
- }
45
-
46
- this.currentStep++;
47
- this.stepStartTime = Date.now();
48
-
49
- // Crear indicador de progreso visual
50
- const progress = this.createProgressBar();
51
- const stepIndicator = chalk.cyan(`[${this.currentStep}/${this.totalSteps}]`);
52
-
53
- // Mostrar el paso actual
54
- console.log(`\n${progress} ${stepIndicator} ${chalk.white.bold(description)}`);
55
-
56
- if (details) {
57
- console.log(` ${chalk.gray('└─ ' + details)}`);
58
- }
59
-
60
- // Crear spinner para este paso (disabled to fix UI issues)
61
- this.currentSpinner = null;
62
-
63
- this.stepTimes.push(this.stepStartTime);
64
-
48
+ if (!this.enabled) return this;
49
+
50
+ this.currentStep += 1;
51
+ this.currentVerb = this.verbForStep(description);
52
+ this.currentDetail = details || description || '';
53
+ this.updateSpinner();
54
+
65
55
  return this;
66
56
  }
67
57
 
68
- /**
69
- * Muestra progreso dentro de un paso (sub-operación)
70
- */
71
- substep(description, isLast = false) {
72
- if (!this.enabled) return;
73
-
74
- const connector = isLast ? '└─' : '├─';
75
- const elapsed = this.getStepElapsedTime();
76
- console.log(` ${chalk.gray(connector)} ${description} ${chalk.dim(`(${elapsed})`)}`);
77
-
58
+ substep(description) {
59
+ if (!this.enabled) return this;
60
+
61
+ this.currentDetail = description || this.currentDetail;
62
+ this.updateSpinner();
63
+
78
64
  return this;
79
65
  }
80
66
 
81
- /**
82
- * Marca el paso actual como completado exitosamente
83
- */
84
- stepComplete(result = null, timing = null) {
85
- if (!this.enabled) return;
86
-
87
- if (result) {
88
- console.log(` ${chalk.green(result)}`);
89
- }
90
-
91
- // Mostrar timing
92
- const elapsed = this.getStepElapsedTime();
93
- console.log(` ${chalk.dim(`└─ ${elapsed}`)}`);
94
-
67
+ stepComplete(result = null) {
68
+ if (!this.enabled) return this;
69
+
70
+ if (result) this.currentDetail = result;
71
+ this.updateSpinner();
72
+
95
73
  return this;
96
74
  }
97
75
 
98
- /**
99
- * Marca el paso actual como fallido
100
- */
101
76
  stepFail(error = null) {
102
- if (!this.enabled) return;
103
-
104
- if (error) {
105
- console.log(` ${chalk.red(error)}`);
106
- }
107
-
77
+ if (!this.enabled) return this;
78
+
79
+ if (error) this.currentDetail = error;
80
+ this.updateSpinner();
81
+
108
82
  return this;
109
83
  }
110
84
 
111
- /**
112
- * Muestra información adicional durante un paso
113
- */
114
- info(message, indent = true) {
115
- if (!this.enabled) return;
116
-
117
- const prefix = indent ? ' ' : '';
118
- console.log(`${prefix}${chalk.gray(message)}`);
119
-
85
+ info(message) {
86
+ if (!this.enabled) return this;
87
+
88
+ this.currentDetail = message || this.currentDetail;
89
+ this.updateSpinner();
90
+
120
91
  return this;
121
92
  }
122
93
 
123
- /**
124
- * Muestra una advertencia
125
- */
126
- warn(message, indent = true) {
127
- if (!this.enabled) return;
128
-
129
- const prefix = indent ? ' ' : '';
130
- console.log(`${prefix}${chalk.yellow(message)}`);
131
-
94
+ warn(message) {
95
+ if (!this.enabled) return this;
96
+
97
+ this.currentDetail = message || this.currentDetail;
98
+ this.updateSpinner();
99
+
132
100
  return this;
133
101
  }
134
102
 
135
- /**
136
- * Muestra resultados o datos encontrados
137
- */
138
- found(message, count = null, indent = true) {
139
- if (!this.enabled) return;
140
-
141
- const prefix = indent ? ' ' : '';
142
- const countStr = count !== null ? chalk.cyan.bold(` (${count})`) : '';
143
- console.log(`${prefix}${chalk.white(message)}${countStr}`);
144
-
103
+ found(message, count = null) {
104
+ if (!this.enabled) return this;
105
+
106
+ const countStr = count !== null ? ` (${count})` : '';
107
+ this.currentDetail = `${message || ''}${countStr}`;
108
+ this.updateSpinner();
109
+
110
+ return this;
111
+ }
112
+
113
+ complete() {
114
+ if (!this.enabled) return this;
115
+
116
+ this.stopSpinner();
117
+
145
118
  return this;
146
119
  }
147
120
 
148
- /**
149
- * Finaliza la operación completa
150
- */
151
- complete(summary = null) {
152
- if (!this.enabled) return;
153
-
154
- // Finalizar spinner si existe (disabled)
121
+ fail(error = null) {
122
+ if (!this.enabled) return this;
123
+
155
124
  if (this.currentSpinner) {
156
- // this.currentSpinner.stop(); // Disabled to prevent UI issues
125
+ this.currentSpinner.fail(error ? `failed: ${error}` : 'failed');
157
126
  this.currentSpinner = null;
158
127
  }
159
-
160
- const totalTime = this.getTotalElapsedTime();
161
-
162
- console.log(chalk.gray('─'.repeat(60)));
163
-
164
- if (summary) {
165
- console.log(chalk.green.bold(`${this.operationTitle} complete!`));
166
- console.log(chalk.gray(` ${summary}`));
167
- } else {
168
- console.log(chalk.green.bold(`Operation complete!`));
169
- }
170
-
171
- console.log(chalk.dim(` Total time: ${totalTime}`));
172
- console.log('');
173
-
128
+ this.clearTimer();
129
+
174
130
  return this;
175
131
  }
176
132
 
177
- /**
178
- * Finaliza la operación con error
179
- */
180
- fail(error = null) {
181
- if (!this.enabled) return;
182
-
183
- // Finalizar spinner si existe (disabled)
133
+ shouldAnimate() {
134
+ return Boolean(
135
+ process.stdout.isTTY &&
136
+ process.env.CI !== 'true' &&
137
+ process.env.LLM_CHECKER_DISABLE_ANIMATION !== '1'
138
+ );
139
+ }
140
+
141
+ verbForStep(description = '') {
142
+ const text = String(description).toLowerCase();
143
+
144
+ if (text.includes('system') || text.includes('hardware')) return 'scanning';
145
+ if (text.includes('database')) return 'syncing';
146
+ if (text.includes('model analysis')) return 'matching';
147
+ if (text.includes('ollama')) return 'checking';
148
+ if (text.includes('filter')) return 'filtering';
149
+ if (text.includes('compatibility')) return 'matching';
150
+ if (text.includes('performance')) return 'estimating';
151
+ if (text.includes('recommend')) return 'ranking';
152
+
153
+ return 'thinking';
154
+ }
155
+
156
+ renderActivityText() {
157
+ const dots = '.'.repeat((this.colorPhase % 3) + 1);
158
+ const verb = this.colorize(`${this.currentVerb}${dots}`);
159
+ const detail = this.currentDetail ? ` ${chalk.dim(this.currentDetail)}` : '';
160
+
161
+ return `${verb}${detail}`;
162
+ }
163
+
164
+ colorize(text) {
165
+ const palette = ['#38BDF8', '#60A5FA', '#818CF8', '#A78BFA', '#22D3EE'];
166
+ return String(text)
167
+ .split('')
168
+ .map((char, index) => chalk.hex(palette[(index + this.colorPhase) % palette.length])(char))
169
+ .join('');
170
+ }
171
+
172
+ updateSpinner() {
173
+ if (!this.currentSpinner) return;
174
+
175
+ this.currentSpinner.text = this.renderActivityText();
176
+ }
177
+
178
+ stopSpinner() {
184
179
  if (this.currentSpinner) {
185
- // this.currentSpinner.fail(); // Disabled to prevent UI issues
180
+ this.currentSpinner.stop();
186
181
  this.currentSpinner = null;
187
182
  }
188
-
189
- console.log(chalk.gray('─'.repeat(60)));
190
- console.log(chalk.red.bold(`Operation failed!`));
191
-
192
- if (error) {
193
- console.log(chalk.red(` ${error}`));
194
- }
195
-
196
- console.log('');
197
-
198
- return this;
183
+ this.clearTimer();
199
184
  }
200
185
 
201
- /**
202
- * Crea una barra de progreso visual
203
- */
204
- createProgressBar() {
205
- if (this.totalSteps === 0) return '';
206
-
207
- const percentage = (this.currentStep / this.totalSteps);
208
- const filledBars = Math.floor(percentage * 20);
209
- const emptyBars = 20 - filledBars;
210
-
211
- const filled = chalk.cyan('█'.repeat(filledBars));
212
- const empty = chalk.gray('░'.repeat(emptyBars));
213
-
214
- return `${filled}${empty} ${Math.round(percentage * 100)}%`;
186
+ clearTimer() {
187
+ if (this.animationTimer) {
188
+ clearInterval(this.animationTimer);
189
+ this.animationTimer = null;
190
+ }
215
191
  }
216
192
 
217
- /**
218
- * Obtiene el tiempo transcurrido desde el inicio de la operación
219
- */
220
193
  getTotalElapsedTime() {
221
194
  if (!this.startTime) return '0ms';
222
-
195
+
223
196
  const elapsed = Date.now() - this.startTime;
224
-
197
+
225
198
  if (elapsed < 1000) return `${elapsed}ms`;
226
199
  if (elapsed < 60000) return `${(elapsed / 1000).toFixed(1)}s`;
227
-
200
+
228
201
  const minutes = Math.floor(elapsed / 60000);
229
202
  const seconds = Math.floor((elapsed % 60000) / 1000);
230
203
  return `${minutes}m ${seconds}s`;
231
204
  }
232
205
 
233
- /**
234
- * Obtiene el tiempo transcurrido desde el último paso
235
- */
236
206
  getElapsedTime() {
237
- if (this.stepTimes.length === 0) return '0ms';
238
-
239
- const lastStepTime = this.stepTimes[this.stepTimes.length - 1];
240
- const elapsed = Date.now() - lastStepTime;
241
-
242
- if (elapsed < 1000) return `${elapsed}ms`;
243
- return `${(elapsed / 1000).toFixed(1)}s`;
207
+ return this.getTotalElapsedTime();
244
208
  }
245
209
 
246
- /**
247
- * Obtiene el tiempo transcurrido desde el inicio del paso actual
248
- */
249
210
  getStepElapsedTime() {
250
- if (!this.stepStartTime) return '0ms';
251
-
252
- const elapsed = Date.now() - this.stepStartTime;
253
-
254
- if (elapsed < 1000) return `${elapsed}ms`;
255
- return `${(elapsed / 1000).toFixed(1)}s`;
211
+ return this.getTotalElapsedTime();
212
+ }
213
+
214
+ createProgressBar() {
215
+ return '';
256
216
  }
257
217
 
258
- /**
259
- * Método estático para crear una instancia rápida
260
- */
261
218
  static create(enabled = true) {
262
219
  return new VerboseProgress(enabled);
263
220
  }
264
221
  }
265
222
 
266
- module.exports = VerboseProgress;
223
+ module.exports = VerboseProgress;