@juspay/neurolink 7.0.0 → 7.2.0

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.
Files changed (101) hide show
  1. package/CHANGELOG.md +15 -4
  2. package/README.md +16 -11
  3. package/dist/cli/commands/config.d.ts +2 -2
  4. package/dist/cli/commands/config.js +22 -21
  5. package/dist/cli/commands/mcp.d.ts +79 -0
  6. package/dist/cli/commands/mcp.js +916 -0
  7. package/dist/cli/commands/models.d.ts +63 -0
  8. package/dist/cli/commands/models.js +653 -0
  9. package/dist/cli/commands/ollama.js +56 -55
  10. package/dist/cli/factories/commandFactory.d.ts +67 -2
  11. package/dist/cli/factories/commandFactory.js +840 -92
  12. package/dist/cli/index.d.ts +6 -0
  13. package/dist/cli/index.js +42 -999
  14. package/dist/cli/utils/completeSetup.js +9 -8
  15. package/dist/cli/utils/envManager.js +7 -6
  16. package/dist/cli/utils/interactiveSetup.js +20 -19
  17. package/dist/core/analytics.js +25 -38
  18. package/dist/core/baseProvider.d.ts +8 -0
  19. package/dist/core/baseProvider.js +177 -68
  20. package/dist/core/constants.d.ts +11 -0
  21. package/dist/core/constants.js +17 -0
  22. package/dist/core/evaluation.js +25 -14
  23. package/dist/core/factory.js +21 -18
  24. package/dist/core/streamAnalytics.d.ts +65 -0
  25. package/dist/core/streamAnalytics.js +125 -0
  26. package/dist/factories/providerRegistry.js +3 -1
  27. package/dist/lib/core/analytics.js +25 -38
  28. package/dist/lib/core/baseProvider.d.ts +8 -0
  29. package/dist/lib/core/baseProvider.js +177 -68
  30. package/dist/lib/core/constants.d.ts +11 -0
  31. package/dist/lib/core/constants.js +17 -0
  32. package/dist/lib/core/evaluation.js +25 -14
  33. package/dist/lib/core/factory.js +22 -18
  34. package/dist/lib/core/streamAnalytics.d.ts +65 -0
  35. package/dist/lib/core/streamAnalytics.js +125 -0
  36. package/dist/lib/factories/providerRegistry.js +3 -1
  37. package/dist/lib/mcp/toolRegistry.d.ts +5 -0
  38. package/dist/lib/mcp/toolRegistry.js +60 -0
  39. package/dist/lib/models/modelRegistry.d.ts +132 -0
  40. package/dist/lib/models/modelRegistry.js +483 -0
  41. package/dist/lib/models/modelResolver.d.ts +115 -0
  42. package/dist/lib/models/modelResolver.js +467 -0
  43. package/dist/lib/neurolink.d.ts +4 -1
  44. package/dist/lib/neurolink.js +108 -69
  45. package/dist/lib/providers/anthropic.js +3 -0
  46. package/dist/lib/providers/googleAiStudio.js +13 -0
  47. package/dist/lib/providers/huggingFace.js +15 -3
  48. package/dist/lib/providers/mistral.js +19 -7
  49. package/dist/lib/providers/ollama.js +31 -7
  50. package/dist/lib/providers/openAI.js +12 -0
  51. package/dist/lib/sdk/toolRegistration.js +17 -0
  52. package/dist/lib/types/cli.d.ts +56 -1
  53. package/dist/lib/types/contextTypes.d.ts +110 -0
  54. package/dist/lib/types/contextTypes.js +176 -0
  55. package/dist/lib/types/index.d.ts +4 -1
  56. package/dist/lib/types/mcpTypes.d.ts +118 -7
  57. package/dist/lib/types/providers.d.ts +81 -0
  58. package/dist/lib/types/streamTypes.d.ts +44 -7
  59. package/dist/lib/types/tools.d.ts +9 -0
  60. package/dist/lib/types/universalProviderOptions.d.ts +3 -1
  61. package/dist/lib/types/universalProviderOptions.js +2 -1
  62. package/dist/lib/utils/logger.d.ts +7 -0
  63. package/dist/lib/utils/logger.js +16 -6
  64. package/dist/lib/utils/performance.d.ts +105 -0
  65. package/dist/lib/utils/performance.js +210 -0
  66. package/dist/lib/utils/providerUtils.js +9 -2
  67. package/dist/lib/utils/retryHandler.d.ts +89 -0
  68. package/dist/lib/utils/retryHandler.js +269 -0
  69. package/dist/mcp/toolRegistry.d.ts +5 -0
  70. package/dist/mcp/toolRegistry.js +60 -0
  71. package/dist/models/modelRegistry.d.ts +132 -0
  72. package/dist/models/modelRegistry.js +483 -0
  73. package/dist/models/modelResolver.d.ts +115 -0
  74. package/dist/models/modelResolver.js +468 -0
  75. package/dist/neurolink.d.ts +4 -1
  76. package/dist/neurolink.js +108 -69
  77. package/dist/providers/anthropic.js +3 -0
  78. package/dist/providers/googleAiStudio.js +13 -0
  79. package/dist/providers/huggingFace.js +15 -3
  80. package/dist/providers/mistral.js +19 -7
  81. package/dist/providers/ollama.js +31 -7
  82. package/dist/providers/openAI.js +12 -0
  83. package/dist/sdk/toolRegistration.js +17 -0
  84. package/dist/types/cli.d.ts +56 -1
  85. package/dist/types/contextTypes.d.ts +110 -0
  86. package/dist/types/contextTypes.js +177 -0
  87. package/dist/types/index.d.ts +4 -1
  88. package/dist/types/mcpTypes.d.ts +118 -7
  89. package/dist/types/providers.d.ts +81 -0
  90. package/dist/types/streamTypes.d.ts +44 -7
  91. package/dist/types/tools.d.ts +9 -0
  92. package/dist/types/universalProviderOptions.d.ts +3 -1
  93. package/dist/types/universalProviderOptions.js +3 -1
  94. package/dist/utils/logger.d.ts +7 -0
  95. package/dist/utils/logger.js +16 -6
  96. package/dist/utils/performance.d.ts +105 -0
  97. package/dist/utils/performance.js +210 -0
  98. package/dist/utils/providerUtils.js +9 -2
  99. package/dist/utils/retryHandler.d.ts +89 -0
  100. package/dist/utils/retryHandler.js +269 -0
  101. package/package.json +2 -1
@@ -0,0 +1,653 @@
1
+ /**
2
+ * Models CLI Commands for NeuroLink
3
+ * Implements comprehensive model management commands
4
+ * Part of Phase 4.1 - Models Command System
5
+ */
6
+ import { ModelResolver, formatSearchResults, formatRecommendation, formatComparison, } from "../../lib/models/modelResolver.js";
7
+ import { getAllModels, formatModelForDisplay, } from "../../lib/models/modelRegistry.js";
8
+ import chalk from "chalk";
9
+ import ora from "ora";
10
+ import { logger } from "../../lib/utils/logger.js";
11
+ /**
12
+ * Type-safe mapping functions to convert CLI types to SDK types
13
+ */
14
+ // Map CLI capability queries to SDK ModelCapabilities keys
15
+ function mapQueryToCapability(query) {
16
+ const queryLower = query.toLowerCase();
17
+ const mappings = {
18
+ vision: "vision",
19
+ function: "functionCalling",
20
+ code: "codeGeneration",
21
+ reasoning: "reasoning",
22
+ multimodal: "multimodal",
23
+ streaming: "streaming",
24
+ json: "jsonMode",
25
+ };
26
+ // Find matching capability
27
+ for (const [key, capability] of Object.entries(mappings)) {
28
+ if (queryLower.includes(key)) {
29
+ return capability;
30
+ }
31
+ }
32
+ return undefined;
33
+ }
34
+ // Map CLI use cases to SDK UseCaseSuitability keys
35
+ function mapUseCaseToSuitability(useCase) {
36
+ const mappings = {
37
+ chat: "conversation",
38
+ coding: "coding",
39
+ creative: "creative",
40
+ analysis: "analysis",
41
+ reasoning: "reasoning",
42
+ translation: "translation",
43
+ summarization: "summarization",
44
+ };
45
+ return mappings[useCase.toLowerCase()];
46
+ }
47
+ // Map CLI requirement flags to SDK ModelCapabilities keys
48
+ function mapRequirementToCapability(requirement) {
49
+ const mappings = {
50
+ vision: "vision",
51
+ functionCalling: "functionCalling",
52
+ };
53
+ return mappings[requirement];
54
+ }
55
+ /**
56
+ * Models CLI command factory
57
+ */
58
+ export class ModelsCommandFactory {
59
+ /**
60
+ * Create the main models command with subcommands
61
+ */
62
+ static createModelsCommands() {
63
+ return {
64
+ command: "models <subcommand>",
65
+ describe: "Manage and discover AI models",
66
+ builder: (yargs) => {
67
+ return yargs
68
+ .command("list", "List available models with filtering options", this.buildListOptions, this.executeList)
69
+ .command("search [query]", "Search models by capabilities, use case, or features", this.buildSearchOptions, this.executeSearch)
70
+ .command("best", "Get the best model recommendation for your use case", this.buildBestOptions, this.executeBest)
71
+ .command("resolve <model>", "Resolve model aliases and find exact model names", this.buildResolveOptions, this.executeResolve)
72
+ .command("compare <models..>", "Compare multiple models side by side", this.buildCompareOptions, this.executeCompare)
73
+ .command("stats", "Show model registry statistics and insights", this.buildStatsOptions, this.executeStats)
74
+ .option("format", {
75
+ choices: ["table", "json", "compact"],
76
+ default: "table",
77
+ description: "Output format",
78
+ })
79
+ .option("output", {
80
+ type: "string",
81
+ description: "Save output to file",
82
+ })
83
+ .option("quiet", {
84
+ type: "boolean",
85
+ alias: "q",
86
+ default: false,
87
+ description: "Suppress non-essential output",
88
+ })
89
+ .option("debug", {
90
+ type: "boolean",
91
+ default: false,
92
+ description: "Enable debug output",
93
+ })
94
+ .demandCommand(1, "Please specify a models subcommand")
95
+ .help();
96
+ },
97
+ handler: () => {
98
+ // No-op handler as subcommands handle everything
99
+ },
100
+ };
101
+ }
102
+ /**
103
+ * Build options for list command
104
+ */
105
+ static buildListOptions(yargs) {
106
+ return yargs
107
+ .option("provider", {
108
+ choices: [
109
+ "openai",
110
+ "bedrock",
111
+ "vertex",
112
+ "anthropic",
113
+ "azure",
114
+ "google-ai",
115
+ "huggingface",
116
+ "ollama",
117
+ "mistral",
118
+ ],
119
+ description: "Filter by AI provider",
120
+ })
121
+ .option("category", {
122
+ choices: ["general", "coding", "creative", "vision", "reasoning"],
123
+ description: "Filter by model category",
124
+ })
125
+ .option("capability", {
126
+ type: "array",
127
+ choices: [
128
+ "vision",
129
+ "functionCalling",
130
+ "codeGeneration",
131
+ "reasoning",
132
+ "multimodal",
133
+ "streaming",
134
+ "jsonMode",
135
+ ],
136
+ description: "Filter by required capabilities",
137
+ })
138
+ .option("deprecated", {
139
+ type: "boolean",
140
+ default: false,
141
+ description: "Include deprecated models",
142
+ })
143
+ .example("neurolink models list", "List all available models")
144
+ .example("neurolink models list --provider openai", "List OpenAI models only")
145
+ .example("neurolink models list --capability vision", "List models with vision capability")
146
+ .example("neurolink models list --category coding", "List coding-focused models");
147
+ }
148
+ /**
149
+ * Build options for search command
150
+ */
151
+ static buildSearchOptions(yargs) {
152
+ return yargs
153
+ .positional("query", {
154
+ type: "string",
155
+ description: "Search query (capability, use case, or model name)",
156
+ })
157
+ .option("use-case", {
158
+ choices: [
159
+ "coding",
160
+ "creative",
161
+ "analysis",
162
+ "conversation",
163
+ "reasoning",
164
+ "translation",
165
+ "summarization",
166
+ ],
167
+ description: "Filter by primary use case",
168
+ })
169
+ .option("max-cost", {
170
+ type: "number",
171
+ description: "Maximum cost per 1K tokens (USD)",
172
+ })
173
+ .option("min-context", {
174
+ type: "number",
175
+ description: "Minimum context window size (tokens)",
176
+ })
177
+ .option("max-context", {
178
+ type: "number",
179
+ description: "Maximum context window size (tokens)",
180
+ })
181
+ .option("performance", {
182
+ choices: ["fast", "medium", "slow", "high", "low"],
183
+ description: "Required performance level (speed or quality)",
184
+ })
185
+ .example("neurolink models search vision", "Search for models with vision capabilities")
186
+ .example("neurolink models search --use-case coding --max-cost 0.01", "Find coding models under $0.01/1K tokens")
187
+ .example("neurolink models search --min-context 100000", "Find models with large context windows");
188
+ }
189
+ /**
190
+ * Build options for best command
191
+ */
192
+ static buildBestOptions(yargs) {
193
+ return yargs
194
+ .option("coding", {
195
+ type: "boolean",
196
+ description: "Optimize for code generation and programming",
197
+ })
198
+ .option("creative", {
199
+ type: "boolean",
200
+ description: "Optimize for creative writing and content",
201
+ })
202
+ .option("analysis", {
203
+ type: "boolean",
204
+ description: "Optimize for data analysis and research",
205
+ })
206
+ .option("conversation", {
207
+ type: "boolean",
208
+ description: "Optimize for conversational interactions",
209
+ })
210
+ .option("reasoning", {
211
+ type: "boolean",
212
+ description: "Optimize for logical reasoning tasks",
213
+ })
214
+ .option("translation", {
215
+ type: "boolean",
216
+ description: "Optimize for language translation",
217
+ })
218
+ .option("summarization", {
219
+ type: "boolean",
220
+ description: "Optimize for text summarization",
221
+ })
222
+ .option("cost-effective", {
223
+ type: "boolean",
224
+ description: "Prioritize cost-effectiveness",
225
+ })
226
+ .option("high-quality", {
227
+ type: "boolean",
228
+ description: "Prioritize output quality over cost",
229
+ })
230
+ .option("fast", {
231
+ type: "boolean",
232
+ description: "Prioritize response speed",
233
+ })
234
+ .option("require-vision", {
235
+ type: "boolean",
236
+ description: "Require vision/image processing capability",
237
+ })
238
+ .option("require-function-calling", {
239
+ type: "boolean",
240
+ description: "Require function calling capability",
241
+ })
242
+ .option("exclude-providers", {
243
+ type: "array",
244
+ description: "Exclude specific providers",
245
+ })
246
+ .option("prefer-local", {
247
+ type: "boolean",
248
+ description: "Prefer local/offline models",
249
+ })
250
+ .example("neurolink models best --coding", "Get best model for coding tasks")
251
+ .example("neurolink models best --cost-effective --require-vision", "Get cheapest model with vision")
252
+ .example("neurolink models best --fast --exclude-providers ollama", "Get fastest non-local model");
253
+ }
254
+ /**
255
+ * Build options for resolve command
256
+ */
257
+ static buildResolveOptions(yargs) {
258
+ return yargs
259
+ .positional("model", {
260
+ type: "string",
261
+ description: "Model name, alias, or partial match to resolve",
262
+ demandOption: true,
263
+ })
264
+ .option("fuzzy", {
265
+ type: "boolean",
266
+ default: true,
267
+ description: "Enable fuzzy matching for partial names",
268
+ })
269
+ .example("neurolink models resolve claude-latest", "Resolve claude-latest alias")
270
+ .example("neurolink models resolve gpt4", "Fuzzy match for GPT-4 models")
271
+ .example("neurolink models resolve fastest", "Resolve fastest model alias");
272
+ }
273
+ /**
274
+ * Build options for compare command
275
+ */
276
+ static buildCompareOptions(yargs) {
277
+ return yargs
278
+ .positional("models", {
279
+ type: "string",
280
+ array: true,
281
+ description: "Model IDs or aliases to compare",
282
+ demandOption: true,
283
+ })
284
+ .example("neurolink models compare gpt-4o claude-3.5-sonnet gemini-2.5-pro", "Compare three flagship models")
285
+ .example("neurolink models compare fastest cheapest best-coding", "Compare models by alias");
286
+ }
287
+ /**
288
+ * Build options for stats command
289
+ */
290
+ static buildStatsOptions(yargs) {
291
+ return yargs
292
+ .option("detailed", {
293
+ type: "boolean",
294
+ default: false,
295
+ description: "Show detailed statistics breakdown",
296
+ })
297
+ .example("neurolink models stats", "Show model registry statistics")
298
+ .example("neurolink models stats --detailed", "Show detailed statistics with breakdowns");
299
+ }
300
+ /**
301
+ * Execute list command
302
+ */
303
+ static async executeList(argv) {
304
+ try {
305
+ const spinner = argv.quiet
306
+ ? null
307
+ : ora("Loading model registry...").start();
308
+ let models = getAllModels();
309
+ // Apply filters
310
+ if (argv.provider) {
311
+ const providers = Array.isArray(argv.provider)
312
+ ? argv.provider
313
+ : [argv.provider];
314
+ models = models.filter((model) => providers.includes(model.provider));
315
+ }
316
+ if (argv.category) {
317
+ models = models.filter((model) => model.category === argv.category);
318
+ }
319
+ if (argv.capability) {
320
+ models = models.filter((model) => {
321
+ return argv.capability.every((cap) => model.capabilities[cap]);
322
+ });
323
+ }
324
+ if (!argv.deprecated) {
325
+ models = models.filter((model) => !model.deprecated);
326
+ }
327
+ if (spinner) {
328
+ spinner.succeed(`Found ${models.length} models`);
329
+ }
330
+ // Format and display results
331
+ if (argv.format === "json") {
332
+ const output = models.map(formatModelForDisplay);
333
+ logger.always(JSON.stringify(output, null, 2));
334
+ }
335
+ else if (argv.format === "compact") {
336
+ models.forEach((model) => {
337
+ logger.always(`${model.id} (${model.provider}) - ${model.description}`);
338
+ });
339
+ }
340
+ else {
341
+ // Table format
342
+ logger.always(chalk.bold("\nšŸ“‹ Available Models:\n"));
343
+ for (const model of models) {
344
+ const status = model.deprecated
345
+ ? chalk.red("DEPRECATED")
346
+ : chalk.green("ACTIVE");
347
+ const cost = model.pricing.inputCostPer1K === 0
348
+ ? chalk.green("FREE")
349
+ : `$${(model.pricing.inputCostPer1K + model.pricing.outputCostPer1K).toFixed(6)}/1K`;
350
+ logger.always(`${chalk.cyan(model.id)} ${status}`);
351
+ logger.always(` Provider: ${model.provider} | Category: ${model.category}`);
352
+ logger.always(` Cost: ${cost} | Context: ${(model.limits.maxContextTokens / 1000).toFixed(0)}K tokens`);
353
+ logger.always(` ${chalk.gray(model.description)}`);
354
+ logger.always();
355
+ }
356
+ }
357
+ }
358
+ catch (error) {
359
+ logger.error(chalk.red(`āŒ List command failed: ${error.message}`));
360
+ process.exit(1);
361
+ }
362
+ }
363
+ /**
364
+ * Execute search command
365
+ */
366
+ static async executeSearch(argv) {
367
+ try {
368
+ const spinner = argv.quiet ? null : ora("Searching models...").start();
369
+ // Build search filters
370
+ const filters = {};
371
+ if (argv.query) {
372
+ // Use type-safe capability mapping
373
+ const mappedCapability = mapQueryToCapability(argv.query);
374
+ if (mappedCapability) {
375
+ filters.capability = mappedCapability;
376
+ }
377
+ }
378
+ if (argv.useCase) {
379
+ // Use type-safe use case mapping
380
+ const mappedUseCase = mapUseCaseToSuitability(argv.useCase);
381
+ if (mappedUseCase) {
382
+ filters.useCase = mappedUseCase;
383
+ }
384
+ }
385
+ if (argv.maxCost) {
386
+ filters.maxCost = argv.maxCost;
387
+ }
388
+ if (argv.minContext) {
389
+ filters.minContextSize = argv.minContext;
390
+ }
391
+ if (argv.maxContext) {
392
+ filters.maxContextSize = argv.maxContext;
393
+ }
394
+ if (argv.performance) {
395
+ filters.performance = argv.performance;
396
+ }
397
+ const results = ModelResolver.searchModels(filters);
398
+ if (spinner) {
399
+ spinner.succeed(`Found ${results.length} matching models`);
400
+ }
401
+ if (results.length === 0) {
402
+ logger.always(chalk.yellow("No models found matching your criteria."));
403
+ return;
404
+ }
405
+ // Display results
406
+ if (argv.format === "json") {
407
+ logger.always(JSON.stringify(formatSearchResults(results), null, 2));
408
+ }
409
+ else {
410
+ logger.always(chalk.bold("\nšŸ” Search Results:\n"));
411
+ results.slice(0, 10).forEach((result, index) => {
412
+ logger.always(`${index + 1}. ${chalk.cyan(result.model.id)} (Score: ${result.score})`);
413
+ logger.always(` ${result.model.description}`);
414
+ logger.always(` Matches: ${chalk.green(result.matchReasons.join(", "))}`);
415
+ logger.always();
416
+ });
417
+ if (results.length > 10) {
418
+ logger.always(chalk.gray(`... and ${results.length - 10} more results`));
419
+ }
420
+ }
421
+ }
422
+ catch (error) {
423
+ logger.error(chalk.red(`āŒ Search command failed: ${error.message}`));
424
+ process.exit(1);
425
+ }
426
+ }
427
+ /**
428
+ * Execute best command
429
+ */
430
+ static async executeBest(argv) {
431
+ try {
432
+ const spinner = argv.quiet ? null : ora("Finding best model...").start();
433
+ // Build recommendation context
434
+ const context = {};
435
+ // Determine use case from flags
436
+ if (argv.coding) {
437
+ context.useCase = "coding";
438
+ }
439
+ else if (argv.creative) {
440
+ context.useCase = "creative";
441
+ }
442
+ else if (argv.analysis) {
443
+ context.useCase = "analysis";
444
+ }
445
+ else if (argv.conversation) {
446
+ context.useCase = "conversation";
447
+ }
448
+ else if (argv.reasoning) {
449
+ context.useCase = "reasoning";
450
+ }
451
+ else if (argv.translation) {
452
+ context.useCase = "translation";
453
+ }
454
+ else if (argv.summarization) {
455
+ context.useCase = "summarization";
456
+ }
457
+ // Apply other preferences
458
+ if (argv.costEffective) {
459
+ context.maxCost = 0.01;
460
+ }
461
+ if (argv.highQuality) {
462
+ context.minQuality = "high";
463
+ }
464
+ if (argv.preferLocal) {
465
+ context.preferLocal = true;
466
+ }
467
+ // Required capabilities - build with correct type from start
468
+ const requiredCapabilities = [];
469
+ if (argv.requireVision) {
470
+ const mapped = mapRequirementToCapability("vision");
471
+ if (mapped) {
472
+ requiredCapabilities.push(mapped);
473
+ }
474
+ }
475
+ if (argv.requireFunctionCalling) {
476
+ const mapped = mapRequirementToCapability("functionCalling");
477
+ if (mapped) {
478
+ requiredCapabilities.push(mapped);
479
+ }
480
+ }
481
+ if (requiredCapabilities.length > 0) {
482
+ // Now we can assign safely since types match
483
+ context.requireCapabilities = requiredCapabilities;
484
+ }
485
+ // Excluded providers
486
+ if (argv.excludeProviders) {
487
+ context.excludeProviders = argv.excludeProviders;
488
+ }
489
+ const recommendation = ModelResolver.getBestModel(context);
490
+ if (spinner) {
491
+ spinner.succeed("Found best model recommendation");
492
+ }
493
+ // Display recommendation
494
+ if (argv.format === "json") {
495
+ logger.always(JSON.stringify(formatRecommendation(recommendation), null, 2));
496
+ }
497
+ else {
498
+ logger.always(chalk.bold("\nšŸŽÆ Best Model Recommendation:\n"));
499
+ const model = recommendation.model;
500
+ logger.always(`${chalk.green("āœ…")} ${chalk.cyan(model.id)} (${model.name})`);
501
+ logger.always(` Provider: ${model.provider} | Category: ${model.category}`);
502
+ logger.always(` Score: ${recommendation.score}/100`);
503
+ logger.always();
504
+ logger.always(chalk.bold("šŸ“‹ Why this model:"));
505
+ recommendation.reasoning.forEach((reason) => {
506
+ logger.always(` • ${reason}`);
507
+ });
508
+ logger.always();
509
+ if (recommendation.alternatives.length > 0) {
510
+ logger.always(chalk.bold("šŸ”„ Alternatives to consider:"));
511
+ recommendation.alternatives.slice(0, 3).forEach((alt) => {
512
+ logger.always(` • ${alt.id} (${alt.provider})`);
513
+ });
514
+ }
515
+ }
516
+ }
517
+ catch (error) {
518
+ logger.error(chalk.red(`āŒ Best command failed: ${error.message}`));
519
+ process.exit(1);
520
+ }
521
+ }
522
+ /**
523
+ * Execute resolve command
524
+ */
525
+ static async executeResolve(argv) {
526
+ try {
527
+ const query = argv.model;
528
+ const model = ModelResolver.resolveModel(query);
529
+ if (!model) {
530
+ logger.always(chalk.red(`āŒ Could not resolve model: ${query}`));
531
+ // Suggest similar models
532
+ const allModels = getAllModels();
533
+ const similar = allModels
534
+ .filter((m) => m.id.toLowerCase().includes(query.toLowerCase()) ||
535
+ m.name.toLowerCase().includes(query.toLowerCase()))
536
+ .slice(0, 3);
537
+ if (similar.length > 0) {
538
+ logger.always(chalk.yellow("\nšŸ’” Did you mean:"));
539
+ similar.forEach((m) => logger.always(` • ${m.id}`));
540
+ }
541
+ process.exit(1);
542
+ }
543
+ // Display resolution
544
+ if (argv.format === "json") {
545
+ logger.always(JSON.stringify(formatModelForDisplay(model), null, 2));
546
+ }
547
+ else {
548
+ logger.always(chalk.bold("\nšŸ” Model Resolution:\n"));
549
+ logger.always(`Query: ${chalk.yellow(query)}`);
550
+ logger.always(`Resolved: ${chalk.green(model.id)}`);
551
+ logger.always(`Name: ${model.name}`);
552
+ logger.always(`Provider: ${model.provider}`);
553
+ logger.always(`Description: ${chalk.gray(model.description)}`);
554
+ if (model.aliases.length > 0) {
555
+ logger.always(`Aliases: ${model.aliases.join(", ")}`);
556
+ }
557
+ }
558
+ }
559
+ catch (error) {
560
+ logger.error(chalk.red(`āŒ Resolve command failed: ${error.message}`));
561
+ process.exit(1);
562
+ }
563
+ }
564
+ /**
565
+ * Execute compare command
566
+ */
567
+ static async executeCompare(argv) {
568
+ try {
569
+ const modelIds = argv.models;
570
+ const comparison = ModelResolver.compareModels(modelIds);
571
+ if (argv.format === "json") {
572
+ logger.always(JSON.stringify(formatComparison(comparison), null, 2));
573
+ }
574
+ else {
575
+ logger.always(chalk.bold("\nāš–ļø Model Comparison:\n"));
576
+ // Display models being compared
577
+ logger.always(chalk.bold("Models:"));
578
+ comparison.models.forEach((model, index) => {
579
+ logger.always(`${index + 1}. ${chalk.cyan(model.id)} (${model.provider})`);
580
+ });
581
+ logger.always();
582
+ // Pricing comparison
583
+ logger.always(chalk.bold("šŸ’° Pricing:"));
584
+ logger.always(` Cheapest: ${chalk.green(comparison.comparison.pricing.cheapest.id)}`);
585
+ logger.always(` Most Expensive: ${chalk.red(comparison.comparison.pricing.mostExpensive.id)}`);
586
+ logger.always();
587
+ // Context size comparison
588
+ logger.always(chalk.bold("šŸ“ Context Size:"));
589
+ logger.always(` Largest: ${chalk.green(comparison.comparison.contextSize.largest.id)}`);
590
+ logger.always(` Smallest: ${chalk.yellow(comparison.comparison.contextSize.smallest.id)}`);
591
+ logger.always();
592
+ // Capabilities comparison
593
+ logger.always(chalk.bold("šŸ› ļø Capabilities:"));
594
+ Object.entries(comparison.comparison.capabilities).forEach(([capability, models]) => {
595
+ if (models.length > 0) {
596
+ logger.always(` ${capability}: ${models.map((m) => m.id).join(", ")}`);
597
+ }
598
+ });
599
+ }
600
+ }
601
+ catch (error) {
602
+ logger.error(chalk.red(`āŒ Compare command failed: ${error.message}`));
603
+ process.exit(1);
604
+ }
605
+ }
606
+ /**
607
+ * Execute stats command
608
+ */
609
+ static async executeStats(argv) {
610
+ try {
611
+ const stats = ModelResolver.getModelStatistics();
612
+ if (argv.format === "json") {
613
+ logger.always(JSON.stringify(stats, null, 2));
614
+ }
615
+ else {
616
+ logger.always(chalk.bold("\nšŸ“Š Model Registry Statistics:\n"));
617
+ if (typeof stats === "object" && stats !== null) {
618
+ const statsObj = stats;
619
+ logger.always(`Total Models: ${chalk.cyan(statsObj.total)}`);
620
+ logger.always(`Providers: ${chalk.cyan(statsObj.providers)}`);
621
+ logger.always(`Deprecated: ${chalk.yellow(statsObj.deprecated)}`);
622
+ logger.always();
623
+ logger.always(chalk.bold("By Provider:"));
624
+ Object.entries(statsObj.byProvider).forEach(([provider, count]) => {
625
+ logger.always(` ${provider}: ${count}`);
626
+ });
627
+ logger.always();
628
+ logger.always(chalk.bold("By Category:"));
629
+ Object.entries(statsObj.byCategory).forEach(([category, count]) => {
630
+ logger.always(` ${category}: ${count}`);
631
+ });
632
+ logger.always();
633
+ if (argv.detailed) {
634
+ logger.always(chalk.bold("Capability Distribution:"));
635
+ Object.entries(statsObj.capabilities).forEach(([capability, count]) => {
636
+ logger.always(` ${capability}: ${count} models`);
637
+ });
638
+ logger.always();
639
+ const pricing = statsObj.pricing;
640
+ logger.always(chalk.bold("Pricing Overview:"));
641
+ logger.always(` Average: $${(pricing.average || 0).toFixed(6)}/1K tokens`);
642
+ logger.always(` Range: $${(pricing.min || 0).toFixed(6)} - $${(pricing.max || 0).toFixed(6)}/1K`);
643
+ logger.always(` Free models: ${pricing.free || false}`);
644
+ }
645
+ }
646
+ }
647
+ }
648
+ catch (error) {
649
+ logger.error(chalk.red(`āŒ Stats command failed: ${error.message}`));
650
+ process.exit(1);
651
+ }
652
+ }
653
+ }