llm-checker 3.5.12 → 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.
@@ -83,7 +83,7 @@ class OllamaNativeScraper {
83
83
 
84
84
  parseModelFromHTML(html) {
85
85
  const models = [];
86
- const pattern = /<a[^>]*href="\/library\/([^"]*)"[^>]*>[\s\S]{0,5000}?<h3[^>]*>([^<]*)<\/h3>[\s\S]{0,2000}?<p[^>]*>([^<]*)<\/p>[\s\S]{0,2000}?(?:<span[^>]*>([^<]*)<\/span>)[\s\S]{0,2000}?(?:(\d+(?:\.\d+)?[KMB]?)\s*(?:Pulls|pulls))[\s\S]{0,1000}?(?:(\d+)\s*(?:Tags|tags))[\s\S]{0,1000}?(?:Updated\s*(\d+\s*\w+\s*ago))?[\s\S]{0,500}?<\/a>/gi;
86
+ const pattern = /<a[^>]*href="\/library\/([^"]*)"[^>]*>[\s\S]{0,5000}?<h3[^>]*>([^<]*)<\/h3>[\s\S]{0,2000}?<p[^>]*>([^<]*)<\/p>[\s\S]{0,2000}?(?:<span[^>]*>([^<]*)<\/span>)[\s\S]{0,2000}?(?:(\d+(?:\.\d+)?[KMB]?)\s*(?:Pulls|Downloads))[\s\S]{0,1000}?(?:(\d+)\s*(?:Tags|Models|tags|models))[\s\S]{0,1000}?(?:Updated\s*(\d+\s*\w+\s*ago))?[\s\S]{0,500}?<\/a>/gi;
87
87
 
88
88
  let match;
89
89
  while ((match = pattern.exec(html)) !== null) {
@@ -126,16 +126,18 @@ class OllamaNativeScraper {
126
126
  const section = html.substring(Math.max(0, linkIndex - 500), linkIndex + 500);
127
127
  const nameMatch = section.match(/<h[2-4][^>]*>([^<]*)<\/h[2-4]>/);
128
128
  const descMatch = section.match(/<p[^>]*>([^<]*)<\/p>/);
129
- const pullsMatch = section.match(/(\d+(?:\.\d+)?[KMB]?)\s*(?:Pulls|pulls)/i);
129
+ const sectionText = this.htmlToText(section);
130
+ const pullsMatch = sectionText.match(/(\d+(?:\.\d+)?\s*[KMB]?)\s*(?:Pulls|Downloads)/i);
131
+ const updatedMatch = sectionText.match(/Updated\s+(\d+\s*(?:minutes?|hours?|days?|weeks?|months?|years?)\s+ago)/i);
130
132
 
131
133
  models.push({
132
134
  model_identifier: identifier,
133
135
  model_name: nameMatch ? this.cleanText(nameMatch[1]) : identifier,
134
136
  description: descMatch ? this.cleanText(descMatch[1]) : '',
135
137
  labels: [],
136
- pulls: pullsMatch ? this.parsePulls(pullsMatch[1]) : 0,
138
+ pulls: pullsMatch ? this.parsePulls(pullsMatch[1].replace(/\s+/g, '')) : 0,
137
139
  tags: 0,
138
- last_updated: 'Unknown',
140
+ last_updated: updatedMatch ? updatedMatch[1].replace(/\s+/g, ' ').trim() : 'Unknown',
139
141
  url: `${this.baseURL}/library/${identifier}`,
140
142
  namespace: identifier.includes('/') ? identifier.split('/')[0] : null,
141
143
  model_type: identifier.includes('/') ? 'community' : 'official'
@@ -147,16 +149,25 @@ class OllamaNativeScraper {
147
149
  }
148
150
 
149
151
  cleanText(text) {
150
- return text
152
+ return String(text || '')
153
+ .replace(/<script[\s\S]*?<\/script>/gi, ' ')
154
+ .replace(/<style[\s\S]*?<\/style>/gi, ' ')
155
+ .replace(/<[^>]+>/g, ' ')
156
+ .replace(/&nbsp;|&#160;/g, ' ')
151
157
  .replace(/&amp;/g, '&')
152
158
  .replace(/&lt;/g, '<')
153
159
  .replace(/&gt;/g, '>')
154
160
  .replace(/&quot;/g, '"')
161
+ .replace(/&#x27;/g, "'")
155
162
  .replace(/&#39;/g, "'")
156
163
  .replace(/\s+/g, ' ')
157
164
  .trim();
158
165
  }
159
166
 
167
+ htmlToText(html) {
168
+ return this.cleanText(html);
169
+ }
170
+
160
171
  parsePulls(pullsStr) {
161
172
  if (!pullsStr) return 0;
162
173
  const num = parseFloat(pullsStr);
@@ -167,6 +178,20 @@ class OllamaNativeScraper {
167
178
  return Math.floor(num);
168
179
  }
169
180
 
181
+ extractPageSummary(html) {
182
+ const text = this.htmlToText(html);
183
+ const downloadsMatch = text.match(/(\d+(?:\.\d+)?\s*[KMB]?)\s*(?:Downloads|Pulls)\b/i);
184
+ const updatedMatch = text.match(/Updated\s+(\d+\s*(?:minutes?|hours?|days?|weeks?|months?|years?)\s+ago)/i);
185
+ const modelCountMatch = text.match(/\bName\s+(\d+)\s+models?\s+Size\b/i) ||
186
+ text.match(/\b(\d+)\s+models?\s+Size\s+Context\s+Input\b/i);
187
+
188
+ return {
189
+ pulls: downloadsMatch ? this.parsePulls(downloadsMatch[1].replace(/\s+/g, '')) : 0,
190
+ lastUpdated: updatedMatch ? updatedMatch[1].replace(/\s+/g, ' ').trim() : 'Unknown',
191
+ tagsCount: modelCountMatch ? parseInt(modelCountMatch[1], 10) : 0
192
+ };
193
+ }
194
+
170
195
  isCacheValid() {
171
196
  const file = fs.existsSync(this.cacheFile) ? this.cacheFile : (fs.existsSync(this.legacyCacheFile) ? this.legacyCacheFile : null);
172
197
  if (!file) return false;
@@ -280,6 +305,8 @@ class OllamaNativeScraper {
280
305
  ...detailedInfo,
281
306
  // Usar datos mejorados si están disponibles
282
307
  pulls: detailedInfo.actual_pulls || basicModel.pulls || 0,
308
+ last_updated: detailedInfo.last_updated || basicModel.last_updated || 'Unknown',
309
+ tags: detailedInfo.tags_count || detailedInfo.tags?.length || basicModel.tags || 0,
283
310
  main_size: detailedInfo.main_size || 'Unknown',
284
311
  detailed_scraped_at: new Date().toISOString()
285
312
  };
@@ -302,11 +329,18 @@ class OllamaNativeScraper {
302
329
  use_cases: [],
303
330
  main_size: 'Unknown',
304
331
  actual_pulls: 0,
332
+ tags_count: 0,
333
+ last_updated: 'Unknown',
305
334
  context_length: 'Unknown',
306
335
  input_types: []
307
336
  };
308
337
 
309
338
  try {
339
+ const pageSummary = this.extractPageSummary(html);
340
+ details.actual_pulls = pageSummary.pulls;
341
+ details.last_updated = pageSummary.lastUpdated;
342
+ details.tags_count = pageSummary.tagsCount;
343
+
310
344
  // MEJORAR: Extraer TODOS los tags incluyendo quantizaciones específicas
311
345
  const allTagMatches = [];
312
346
 
@@ -370,37 +404,28 @@ class OllamaNativeScraper {
370
404
  }
371
405
 
372
406
  // NUEVO: Detectar tipos de input soportados
373
- const inputTypes = [];
374
- if (html.toLowerCase().includes('text') || html.toLowerCase().includes('chat')) {
375
- inputTypes.push('text');
376
- }
377
- if (html.toLowerCase().includes('image') || html.toLowerCase().includes('vision') ||
378
- html.toLowerCase().includes('visual')) {
407
+ const modelNameForInputs = String(basicModel.model_identifier || '').toLowerCase();
408
+ const inputTypes = ['text'];
409
+ if (/llava|bakllava|moondream|vision|vl\b|qwen2\.5vl|qwen-vl|qwen3-vl|minicpm-v|pixtral|gemma3n|llama3\.2-vision/.test(modelNameForInputs)) {
379
410
  inputTypes.push('image');
380
411
  }
381
- if (html.toLowerCase().includes('code') || html.toLowerCase().includes('programming')) {
382
- inputTypes.push('code');
383
- }
384
- if (html.toLowerCase().includes('audio') || html.toLowerCase().includes('speech')) {
385
- inputTypes.push('audio');
386
- }
387
412
 
388
- details.input_types = inputTypes.length > 0 ? inputTypes : ['text'];
413
+ details.input_types = [...new Set(inputTypes)];
389
414
 
390
415
  // Mejor extracción de tamaños con regex más específico
391
- const sizeMatches = html.match(/\b(\d+(?:\.\d+)?)\s*[BG]B?\b/gi);
392
- if (sizeMatches) {
393
- details.model_sizes = [...new Set(sizeMatches.map(size => size.toLowerCase()))];
394
- // Determinar el tamaño principal (más común)
395
- if (details.model_sizes.length > 0) {
396
- details.main_size = details.model_sizes[0];
397
- }
416
+ details.model_sizes = [...new Set(
417
+ details.tags
418
+ .map(tag => this.extractSizeFromTag(tag))
419
+ .filter(size => size !== 'unknown')
420
+ )];
421
+ if (details.model_sizes.length > 0) {
422
+ details.main_size = details.model_sizes[0];
398
423
  }
399
424
 
400
425
  // Extraer pulls reales del HTML
401
- const pullsMatch = html.match(/(\d+(?:\.\d+)?[KMB]?)\s*pulls?/i);
426
+ const pullsMatch = this.htmlToText(html).match(/(\d+(?:\.\d+)?\s*[KMB]?)\s*(?:pulls?|downloads?)/i);
402
427
  if (pullsMatch) {
403
- details.actual_pulls = this.parsePulls(pullsMatch[1]);
428
+ details.actual_pulls = this.parsePulls(pullsMatch[1].replace(/\s+/g, ''));
404
429
  }
405
430
 
406
431
  // Mejorar detección de quantizaciones
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const chalk = require('chalk');
4
+ const { execFileSync } = require('child_process');
4
5
  const fs = require('fs');
5
6
  const os = require('os');
6
7
  const path = require('path');
@@ -51,14 +52,46 @@ const FRAMES_PER_SECOND = 14;
51
52
  // Security: do not auto-load executable-style banner sources from user-writable folders.
52
53
  // External banner loading is opt-in via LLM_CHECKER_BANNER_SOURCE and supports JSON only.
53
54
  const DEFAULT_BANNER_SOURCE = null;
54
- const DEFAULT_TEXT_BANNER_SOURCE = path.join(
55
- os.homedir(),
56
- 'Desktop',
57
- 'llm-checker',
58
- 'banner-profesional-v2.txt'
59
- );
55
+ 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
+ )
66
+ ];
60
67
  let cachedExternalBanner = null;
61
68
  let cachedTextBanner = null;
69
+ let cachedMacOsDarkBackground = null;
70
+
71
+ const TEXT_BANNER_PALETTES = {
72
+ dark: {
73
+ border: '#0066FF',
74
+ primary: '#60A5FA',
75
+ feature: '#A7F3D0',
76
+ subtitle: '#C7D2FE',
77
+ link: '#3B82F6',
78
+ install: '#F8FAFC',
79
+ art: '#F8FAFC',
80
+ solidLogo: ['#F8FAFC', '#E2ECFF', '#DBEAFE', '#E2ECFF'],
81
+ shadedLogo: ['#93C5FD', '#60A5FA', '#38BDF8', '#22D3EE', '#38BDF8', '#60A5FA']
82
+ },
83
+ light: {
84
+ border: '#0047B3',
85
+ primary: '#003A8C',
86
+ feature: '#047857',
87
+ subtitle: '#3730A3',
88
+ link: '#1D4ED8',
89
+ install: '#111827',
90
+ art: '#111827',
91
+ solidLogo: ['#0F172A', '#1E3A8A', '#111827', '#1E40AF'],
92
+ shadedLogo: ['#1D4ED8', '#0369A1', '#0F766E', '#047857', '#1D4ED8']
93
+ }
94
+ };
62
95
 
63
96
  function sleep(ms) {
64
97
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -68,6 +101,78 @@ function clearTerminal() {
68
101
  process.stdout.write('\x1b[2J\x1b[0f');
69
102
  }
70
103
 
104
+ function parseDarkBackgroundValue(value) {
105
+ if (typeof value !== 'string' || value.trim() === '') return null;
106
+ const normalized = value.trim().toLowerCase();
107
+
108
+ if (['dark', 'true', '1', 'yes', 'on'].includes(normalized)) return true;
109
+ if (['light', 'false', '0', 'no', 'off'].includes(normalized)) return false;
110
+
111
+ return null;
112
+ }
113
+
114
+ function detectDarkBackgroundFromColorFgbg(value) {
115
+ if (typeof value !== 'string' || value.trim() === '') return null;
116
+
117
+ const parts = value.split(';');
118
+ const backgroundCode = Number.parseInt(parts[parts.length - 1], 10);
119
+ if (!Number.isFinite(backgroundCode)) return null;
120
+
121
+ const lightBackgrounds = new Set([7, 10, 11, 14, 15]);
122
+ const darkBackgrounds = new Set([0, 1, 2, 4, 5, 6, 8]);
123
+
124
+ if (lightBackgrounds.has(backgroundCode)) return false;
125
+ if (darkBackgrounds.has(backgroundCode)) return true;
126
+
127
+ return null;
128
+ }
129
+
130
+ function detectDarkBackgroundFromMacOsAppearance() {
131
+ if (process.platform !== 'darwin') return null;
132
+ if (cachedMacOsDarkBackground !== null) return cachedMacOsDarkBackground;
133
+
134
+ try {
135
+ const output = execFileSync('defaults', ['read', '-g', 'AppleInterfaceStyle'], {
136
+ encoding: 'utf8',
137
+ stdio: ['ignore', 'pipe', 'ignore'],
138
+ timeout: 100
139
+ });
140
+ cachedMacOsDarkBackground = output.trim().toLowerCase().includes('dark');
141
+ } catch {
142
+ // macOS omits AppleInterfaceStyle when the system appearance is Light.
143
+ cachedMacOsDarkBackground = false;
144
+ }
145
+
146
+ return cachedMacOsDarkBackground;
147
+ }
148
+
149
+ function resolveHasDarkBackground(options = {}) {
150
+ if (typeof options.hasDarkBackground === 'boolean') {
151
+ return options.hasDarkBackground;
152
+ }
153
+
154
+ const explicit =
155
+ parseDarkBackgroundValue(process.env.LLM_CHECKER_BANNER_THEME) ??
156
+ parseDarkBackgroundValue(process.env.LLM_CHECKER_HAS_DARK_BACKGROUND) ??
157
+ parseDarkBackgroundValue(process.env.TERM_BACKGROUND);
158
+
159
+ if (explicit !== null) return explicit;
160
+
161
+ const colorFgbg = detectDarkBackgroundFromColorFgbg(process.env.COLORFGBG);
162
+ if (colorFgbg !== null) return colorFgbg;
163
+
164
+ const macOsAppearance = detectDarkBackgroundFromMacOsAppearance();
165
+ if (macOsAppearance !== null) return macOsAppearance;
166
+
167
+ return true;
168
+ }
169
+
170
+ function getTextBannerPalette(options = {}) {
171
+ return resolveHasDarkBackground(options)
172
+ ? TEXT_BANNER_PALETTES.dark
173
+ : TEXT_BANNER_PALETTES.light;
174
+ }
175
+
71
176
  function fitLine(line, width) {
72
177
  const value = String(line || '');
73
178
  if (value.length <= width) return value;
@@ -190,10 +295,12 @@ function loadExternalBanner(sourceFile) {
190
295
  }
191
296
 
192
297
  function loadTextBanner(sourceFile) {
193
- const filePath =
298
+ const requestedFile =
194
299
  sourceFile ||
195
300
  process.env.LLM_CHECKER_TEXT_BANNER_SOURCE ||
196
- DEFAULT_TEXT_BANNER_SOURCE;
301
+ DEFAULT_TEXT_BANNER_SOURCES.find((candidate) => fs.existsSync(candidate)) ||
302
+ DEFAULT_TEXT_BANNER_SOURCES[0];
303
+ const filePath = requestedFile;
197
304
  let mtimeMs = -1;
198
305
 
199
306
  try {
@@ -236,6 +343,7 @@ function loadTextBanner(sourceFile) {
236
343
  }
237
344
 
238
345
  function drawTextBanner(lines, options = {}) {
346
+ const palette = getTextBannerPalette(options);
239
347
  const colorPhase = Number.isFinite(options.colorPhase)
240
348
  ? Math.max(0, Math.floor(options.colorPhase))
241
349
  : 0;
@@ -265,8 +373,8 @@ function drawTextBanner(lines, options = {}) {
265
373
  };
266
374
 
267
375
  const colorizeDosRebelLine = (text) => {
268
- const solidPalette = ['#F8FAFC', '#E2ECFF', '#DBEAFE', '#E2ECFF'];
269
- const shadePalette = ['#93C5FD', '#60A5FA', '#38BDF8', '#22D3EE', '#38BDF8', '#60A5FA'];
376
+ const solidPalette = palette.solidLogo;
377
+ const shadePalette = palette.shadedLogo;
270
378
  let out = '';
271
379
  for (let index = 0; index < text.length; index += 1) {
272
380
  const ch = text[index];
@@ -292,9 +400,9 @@ function drawTextBanner(lines, options = {}) {
292
400
  if (/^\s*\+[-+]+\+\s*$/.test(line)) {
293
401
  if (terminalWidth && terminalWidth >= 10) {
294
402
  const inner = Math.max(6, terminalWidth - 4);
295
- console.log(chalk.hex('#0066FF')(` +${'-'.repeat(inner)}+ `));
403
+ console.log(chalk.hex(palette.border)(` +${'-'.repeat(inner)}+ `));
296
404
  } else {
297
- console.log(chalk.hex('#0066FF')(line));
405
+ console.log(chalk.hex(palette.border)(line));
298
406
  }
299
407
  continue;
300
408
  }
@@ -305,8 +413,8 @@ function drawTextBanner(lines, options = {}) {
305
413
  continue;
306
414
  }
307
415
 
308
- const left = chalk.hex('#0066FF')(frameMatch[1]);
309
- const right = chalk.hex('#0066FF')(frameMatch[3]);
416
+ const left = chalk.hex(palette.border)(frameMatch[1]);
417
+ const right = chalk.hex(palette.border)(frameMatch[3]);
310
418
  const content = frameMatch[2];
311
419
  const maxInnerWidth = terminalWidth
312
420
  ? Math.max(0, terminalWidth - (frameMatch[1].length + frameMatch[3].length))
@@ -336,7 +444,7 @@ function drawTextBanner(lines, options = {}) {
336
444
  fittedContent.includes('Deterministic scoring across') ||
337
445
  fittedContent.includes('Run: llm-checker recommend')
338
446
  ) {
339
- inner = chalk.hex('#60A5FA')(fittedContent);
447
+ inner = chalk.hex(palette.primary)(fittedContent);
340
448
  } else if (
341
449
  fittedContent.includes('[200+ DYNAMIC MODELS]') ||
342
450
  fittedContent.includes('[35+ FALLBACK]') ||
@@ -344,18 +452,18 @@ function drawTextBanner(lines, options = {}) {
344
452
  fittedContent.includes('[MULTI-GPU]') ||
345
453
  fittedContent.includes('[MCP SERVER]')
346
454
  ) {
347
- inner = chalk.hex('#A7F3D0')(fittedContent);
455
+ inner = chalk.hex(palette.feature)(fittedContent);
348
456
  } else if (
349
457
  fittedContent.includes('AI-powered CLI for hardware-aware local LLM recommendations')
350
458
  ) {
351
- inner = chalk.hex('#C7D2FE')(fittedContent);
459
+ inner = chalk.hex(palette.subtitle)(fittedContent);
352
460
  } else if (
353
461
  fittedContent.includes('github.com/Pavelevich/llm-checker') ||
354
462
  fittedContent.includes('npmjs.com/package/llm-checker')
355
463
  ) {
356
- inner = chalk.hex('#3B82F6')(fittedContent);
464
+ inner = chalk.hex(palette.link)(fittedContent);
357
465
  } else if (fittedContent.includes('Install: npm install -g llm-checker')) {
358
- inner = chalk.hex('#F8FAFC')(fittedContent);
466
+ inner = chalk.hex(palette.install)(fittedContent);
359
467
  } else if (
360
468
  fittedContent.includes('█') ||
361
469
  fittedContent.includes('░') ||
@@ -369,7 +477,7 @@ function drawTextBanner(lines, options = {}) {
369
477
  fittedContent.includes('▀') ||
370
478
  fittedContent.includes('▄')
371
479
  ) {
372
- inner = chalk.hex('#F8FAFC')(fittedContent);
480
+ inner = chalk.hex(palette.art)(fittedContent);
373
481
  }
374
482
 
375
483
  console.log(left + inner + right);
@@ -625,14 +733,14 @@ async function animateBanner(options = {}) {
625
733
  if (textBanner && textBanner.length > 0) {
626
734
  if (!shouldAnimate) {
627
735
  clearTerminal();
628
- drawTextBanner(textBanner);
736
+ drawTextBanner(textBanner, { hasDarkBackground });
629
737
  return;
630
738
  }
631
739
 
632
740
  const textFrames = Math.max(10, Math.min(24, frames));
633
741
  for (let frameIndex = 0; frameIndex < textFrames; frameIndex += 1) {
634
742
  clearTerminal();
635
- drawTextBanner(textBanner, { colorPhase: frameIndex });
743
+ drawTextBanner(textBanner, { colorPhase: frameIndex, hasDarkBackground });
636
744
  await sleep(frameDurationMs);
637
745
  }
638
746
  return;
@@ -658,7 +766,11 @@ function renderPersistentBanner(width = 74, options = {}) {
658
766
  return;
659
767
  }
660
768
 
661
- const prepared = makeFrames({ frameCount: 1, width });
769
+ const prepared = makeFrames({
770
+ frameCount: 1,
771
+ width,
772
+ hasDarkBackground: resolveHasDarkBackground(options)
773
+ });
662
774
  drawFrame(prepared.frames[0], prepared.width, prepared.theme);
663
775
  }
664
776
 
@@ -674,7 +786,10 @@ module.exports = {
674
786
  renderPersistentBanner,
675
787
  renderCommandHeader,
676
788
  __private: {
789
+ detectDarkBackgroundFromColorFgbg,
790
+ detectDarkBackgroundFromMacOsAppearance,
677
791
  makeFrames,
678
- drawFrame
792
+ drawFrame,
793
+ resolveHasDarkBackground
679
794
  }
680
795
  };
@@ -13,6 +13,7 @@ const PRIMARY_COMMAND_PRIORITY = [
13
13
  'recommend',
14
14
  'simulate',
15
15
  'ai-run',
16
+ 'sync',
16
17
  'ollama-plan',
17
18
  'list-models',
18
19
  'search',
@@ -314,7 +315,6 @@ async function collectCommandArgs(commandMeta) {
314
315
  const prompts = [];
315
316
  const requiredPrompts = REQUIRED_ARG_PROMPTS[commandMeta.name] || [];
316
317
  const requiredOptionPrompts = getRequiredOptionPrompts(commandMeta);
317
- const promptOptionalArgs = commandMeta.name !== 'help';
318
318
 
319
319
  for (const requiredPrompt of requiredPrompts) {
320
320
  prompts.push({
@@ -354,16 +354,6 @@ async function collectCommandArgs(commandMeta) {
354
354
  });
355
355
  }
356
356
 
357
- if (promptOptionalArgs) {
358
- prompts.push({
359
- type: 'input',
360
- name: 'extraArgs',
361
- message: 'Optional extra params (example: --json --limit 5):',
362
- prefix: ' ',
363
- default: ''
364
- });
365
- }
366
-
367
357
  if (prompts.length === 0) {
368
358
  return [];
369
359
  }
@@ -378,13 +368,6 @@ async function collectCommandArgs(commandMeta) {
378
368
 
379
369
  args.push(...buildRequiredOptionArgs(requiredOptionPrompts, answers));
380
370
 
381
- if (promptOptionalArgs) {
382
- const extraArgs = String(answers.extraArgs || '').trim();
383
- if (extraArgs) {
384
- args.push(...tokenizeArgString(extraArgs));
385
- }
386
- }
387
-
388
371
  return args;
389
372
  }
390
373