@juspay/neurolink 1.6.0 → 1.10.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 (182) hide show
  1. package/CHANGELOG.md +200 -7
  2. package/README.md +101 -18
  3. package/dist/agent/direct-tools.d.ts +1203 -0
  4. package/dist/agent/direct-tools.js +387 -0
  5. package/dist/cli/commands/agent-generate.d.ts +2 -0
  6. package/dist/cli/commands/agent-generate.js +70 -0
  7. package/dist/cli/commands/config.d.ts +6 -6
  8. package/dist/cli/commands/config.js +326 -273
  9. package/dist/cli/commands/mcp.d.ts +2 -1
  10. package/dist/cli/commands/mcp.js +874 -146
  11. package/dist/cli/commands/ollama.d.ts +1 -1
  12. package/dist/cli/commands/ollama.js +153 -143
  13. package/dist/cli/index.js +687 -325
  14. package/dist/cli/utils/complete-setup.d.ts +19 -0
  15. package/dist/cli/utils/complete-setup.js +81 -0
  16. package/dist/cli/utils/env-manager.d.ts +44 -0
  17. package/dist/cli/utils/env-manager.js +226 -0
  18. package/dist/cli/utils/interactive-setup.d.ts +48 -0
  19. package/dist/cli/utils/interactive-setup.js +302 -0
  20. package/dist/core/dynamic-models.d.ts +208 -0
  21. package/dist/core/dynamic-models.js +250 -0
  22. package/dist/core/factory.d.ts +13 -6
  23. package/dist/core/factory.js +177 -62
  24. package/dist/core/types.d.ts +4 -2
  25. package/dist/core/types.js +4 -4
  26. package/dist/index.d.ts +16 -16
  27. package/dist/index.js +16 -16
  28. package/dist/lib/agent/direct-tools.d.ts +1203 -0
  29. package/dist/lib/agent/direct-tools.js +387 -0
  30. package/dist/lib/core/dynamic-models.d.ts +208 -0
  31. package/dist/lib/core/dynamic-models.js +250 -0
  32. package/dist/lib/core/factory.d.ts +13 -6
  33. package/dist/lib/core/factory.js +177 -62
  34. package/dist/lib/core/types.d.ts +4 -2
  35. package/dist/lib/core/types.js +4 -4
  36. package/dist/lib/index.d.ts +16 -16
  37. package/dist/lib/index.js +16 -16
  38. package/dist/lib/mcp/auto-discovery.d.ts +120 -0
  39. package/dist/lib/mcp/auto-discovery.js +793 -0
  40. package/dist/lib/mcp/client.d.ts +66 -0
  41. package/dist/lib/mcp/client.js +245 -0
  42. package/dist/lib/mcp/config.d.ts +31 -0
  43. package/dist/lib/mcp/config.js +74 -0
  44. package/dist/lib/mcp/context-manager.d.ts +4 -4
  45. package/dist/lib/mcp/context-manager.js +24 -18
  46. package/dist/lib/mcp/factory.d.ts +28 -11
  47. package/dist/lib/mcp/factory.js +36 -29
  48. package/dist/lib/mcp/function-calling.d.ts +51 -0
  49. package/dist/lib/mcp/function-calling.js +510 -0
  50. package/dist/lib/mcp/index.d.ts +190 -0
  51. package/dist/lib/mcp/index.js +156 -0
  52. package/dist/lib/mcp/initialize-tools.d.ts +28 -0
  53. package/dist/lib/mcp/initialize-tools.js +209 -0
  54. package/dist/lib/mcp/initialize.d.ts +17 -0
  55. package/dist/lib/mcp/initialize.js +51 -0
  56. package/dist/lib/mcp/logging.d.ts +71 -0
  57. package/dist/lib/mcp/logging.js +183 -0
  58. package/dist/lib/mcp/manager.d.ts +67 -0
  59. package/dist/lib/mcp/manager.js +176 -0
  60. package/dist/lib/mcp/neurolink-mcp-client.d.ts +96 -0
  61. package/dist/lib/mcp/neurolink-mcp-client.js +417 -0
  62. package/dist/lib/mcp/orchestrator.d.ts +3 -3
  63. package/dist/lib/mcp/orchestrator.js +46 -43
  64. package/dist/lib/mcp/registry.d.ts +12 -4
  65. package/dist/lib/mcp/registry.js +64 -37
  66. package/dist/lib/mcp/servers/ai-providers/ai-analysis-tools.d.ts +1 -1
  67. package/dist/lib/mcp/servers/ai-providers/ai-analysis-tools.js +204 -65
  68. package/dist/lib/mcp/servers/ai-providers/ai-core-server.js +142 -102
  69. package/dist/lib/mcp/servers/ai-providers/ai-workflow-tools.d.ts +6 -6
  70. package/dist/lib/mcp/servers/ai-providers/ai-workflow-tools.js +197 -142
  71. package/dist/lib/mcp/servers/utilities/utility-server.d.ts +8 -0
  72. package/dist/lib/mcp/servers/utilities/utility-server.js +326 -0
  73. package/dist/lib/mcp/tool-integration.d.ts +67 -0
  74. package/dist/lib/mcp/tool-integration.js +179 -0
  75. package/dist/lib/mcp/unified-registry.d.ts +269 -0
  76. package/dist/lib/mcp/unified-registry.js +1411 -0
  77. package/dist/lib/neurolink.d.ts +68 -6
  78. package/dist/lib/neurolink.js +304 -42
  79. package/dist/lib/providers/agent-enhanced-provider.d.ts +59 -0
  80. package/dist/lib/providers/agent-enhanced-provider.js +242 -0
  81. package/dist/lib/providers/amazonBedrock.d.ts +3 -3
  82. package/dist/lib/providers/amazonBedrock.js +54 -50
  83. package/dist/lib/providers/anthropic.d.ts +2 -2
  84. package/dist/lib/providers/anthropic.js +92 -84
  85. package/dist/lib/providers/azureOpenAI.d.ts +2 -2
  86. package/dist/lib/providers/azureOpenAI.js +97 -86
  87. package/dist/lib/providers/function-calling-provider.d.ts +70 -0
  88. package/dist/lib/providers/function-calling-provider.js +359 -0
  89. package/dist/lib/providers/googleAIStudio.d.ts +10 -5
  90. package/dist/lib/providers/googleAIStudio.js +60 -38
  91. package/dist/lib/providers/googleVertexAI.d.ts +3 -3
  92. package/dist/lib/providers/googleVertexAI.js +96 -86
  93. package/dist/lib/providers/huggingFace.d.ts +3 -3
  94. package/dist/lib/providers/huggingFace.js +70 -63
  95. package/dist/lib/providers/index.d.ts +11 -11
  96. package/dist/lib/providers/index.js +18 -18
  97. package/dist/lib/providers/mcp-provider.d.ts +62 -0
  98. package/dist/lib/providers/mcp-provider.js +183 -0
  99. package/dist/lib/providers/mistralAI.d.ts +3 -3
  100. package/dist/lib/providers/mistralAI.js +42 -36
  101. package/dist/lib/providers/ollama.d.ts +4 -4
  102. package/dist/lib/providers/ollama.js +128 -98
  103. package/dist/lib/providers/openAI.d.ts +7 -3
  104. package/dist/lib/providers/openAI.js +45 -33
  105. package/dist/lib/utils/logger.js +2 -2
  106. package/dist/lib/utils/providerUtils-fixed.d.ts +8 -0
  107. package/dist/lib/utils/providerUtils-fixed.js +75 -0
  108. package/dist/lib/utils/providerUtils.d.ts +8 -1
  109. package/dist/lib/utils/providerUtils.js +63 -32
  110. package/dist/mcp/auto-discovery.d.ts +120 -0
  111. package/dist/mcp/auto-discovery.js +794 -0
  112. package/dist/mcp/client.d.ts +66 -0
  113. package/dist/mcp/client.js +245 -0
  114. package/dist/mcp/config.d.ts +31 -0
  115. package/dist/mcp/config.js +74 -0
  116. package/dist/mcp/context-manager.d.ts +4 -4
  117. package/dist/mcp/context-manager.js +24 -18
  118. package/dist/mcp/factory.d.ts +28 -11
  119. package/dist/mcp/factory.js +36 -29
  120. package/dist/mcp/function-calling.d.ts +51 -0
  121. package/dist/mcp/function-calling.js +510 -0
  122. package/dist/mcp/index.d.ts +190 -0
  123. package/dist/mcp/index.js +156 -0
  124. package/dist/mcp/initialize-tools.d.ts +28 -0
  125. package/dist/mcp/initialize-tools.js +210 -0
  126. package/dist/mcp/initialize.d.ts +17 -0
  127. package/dist/mcp/initialize.js +51 -0
  128. package/dist/mcp/logging.d.ts +71 -0
  129. package/dist/mcp/logging.js +183 -0
  130. package/dist/mcp/manager.d.ts +67 -0
  131. package/dist/mcp/manager.js +176 -0
  132. package/dist/mcp/neurolink-mcp-client.d.ts +96 -0
  133. package/dist/mcp/neurolink-mcp-client.js +417 -0
  134. package/dist/mcp/orchestrator.d.ts +3 -3
  135. package/dist/mcp/orchestrator.js +46 -43
  136. package/dist/mcp/registry.d.ts +12 -4
  137. package/dist/mcp/registry.js +64 -37
  138. package/dist/mcp/servers/ai-providers/ai-analysis-tools.d.ts +1 -1
  139. package/dist/mcp/servers/ai-providers/ai-analysis-tools.js +204 -65
  140. package/dist/mcp/servers/ai-providers/ai-core-server.js +142 -102
  141. package/dist/mcp/servers/ai-providers/ai-workflow-tools.d.ts +6 -6
  142. package/dist/mcp/servers/ai-providers/ai-workflow-tools.js +197 -142
  143. package/dist/mcp/servers/utilities/utility-server.d.ts +8 -0
  144. package/dist/mcp/servers/utilities/utility-server.js +326 -0
  145. package/dist/mcp/tool-integration.d.ts +67 -0
  146. package/dist/mcp/tool-integration.js +179 -0
  147. package/dist/mcp/unified-registry.d.ts +269 -0
  148. package/dist/mcp/unified-registry.js +1411 -0
  149. package/dist/neurolink.d.ts +68 -6
  150. package/dist/neurolink.js +304 -42
  151. package/dist/providers/agent-enhanced-provider.d.ts +59 -0
  152. package/dist/providers/agent-enhanced-provider.js +242 -0
  153. package/dist/providers/amazonBedrock.d.ts +3 -3
  154. package/dist/providers/amazonBedrock.js +54 -50
  155. package/dist/providers/anthropic.d.ts +2 -2
  156. package/dist/providers/anthropic.js +92 -84
  157. package/dist/providers/azureOpenAI.d.ts +2 -2
  158. package/dist/providers/azureOpenAI.js +97 -86
  159. package/dist/providers/function-calling-provider.d.ts +70 -0
  160. package/dist/providers/function-calling-provider.js +359 -0
  161. package/dist/providers/googleAIStudio.d.ts +10 -5
  162. package/dist/providers/googleAIStudio.js +60 -38
  163. package/dist/providers/googleVertexAI.d.ts +3 -3
  164. package/dist/providers/googleVertexAI.js +96 -86
  165. package/dist/providers/huggingFace.d.ts +3 -3
  166. package/dist/providers/huggingFace.js +70 -63
  167. package/dist/providers/index.d.ts +11 -11
  168. package/dist/providers/index.js +18 -18
  169. package/dist/providers/mcp-provider.d.ts +62 -0
  170. package/dist/providers/mcp-provider.js +183 -0
  171. package/dist/providers/mistralAI.d.ts +3 -3
  172. package/dist/providers/mistralAI.js +42 -36
  173. package/dist/providers/ollama.d.ts +4 -4
  174. package/dist/providers/ollama.js +128 -98
  175. package/dist/providers/openAI.d.ts +7 -3
  176. package/dist/providers/openAI.js +45 -33
  177. package/dist/utils/logger.js +2 -2
  178. package/dist/utils/providerUtils-fixed.d.ts +8 -0
  179. package/dist/utils/providerUtils-fixed.js +75 -0
  180. package/dist/utils/providerUtils.d.ts +8 -1
  181. package/dist/utils/providerUtils.js +63 -32
  182. package/package.json +182 -160
package/dist/cli/index.js CHANGED
@@ -6,18 +6,19 @@
6
6
  * Features: Spinners, colors, batch processing, provider testing, rich help
7
7
  * Implementation: ~300 lines using simple JS utility functions
8
8
  */
9
- import { NeuroLink } from '../lib/neurolink.js';
10
- import yargs from 'yargs';
11
- import { hideBin } from 'yargs/helpers';
12
- import ora from 'ora';
13
- import chalk from 'chalk';
14
- import fs from 'fs';
15
- import { addMCPCommands } from './commands/mcp.js';
16
- import ollamaCommand from './commands/ollama.js';
9
+ import { NeuroLink } from "../lib/neurolink.js";
10
+ import yargs from "yargs";
11
+ import { hideBin } from "yargs/helpers";
12
+ import ora from "ora";
13
+ import chalk from "chalk";
14
+ import fs from "fs";
15
+ import { addMCPCommands } from "./commands/mcp.js";
16
+ import ollamaCommand from "./commands/ollama.js";
17
+ import { agentGenerateCommand } from "./commands/agent-generate.js";
17
18
  // Load environment variables from .env file
18
19
  try {
19
20
  // Try to import and configure dotenv
20
- const { config } = await import('dotenv');
21
+ const { config } = await import("dotenv");
21
22
  config(); // Load .env from current working directory
22
23
  }
23
24
  catch (error) {
@@ -25,146 +26,124 @@ catch (error) {
25
26
  // Environment variables should be set externally in production
26
27
  }
27
28
  // Utility Functions (Simple, Zero Maintenance)
28
- function formatOutput(result, format = 'text') {
29
- if (format === 'json') {
30
- return JSON.stringify(result, null, 2);
31
- }
32
- // Smart text formatting
33
- if (result?.content) {
34
- return result.content;
35
- }
36
- if (typeof result === 'string') {
37
- return result;
38
- }
39
- return JSON.stringify(result, null, 2);
40
- }
41
29
  function handleError(error, context) {
42
- let specificErrorMessage = error.message;
43
- const originalErrorMessageLowerCase = error.message ? error.message.toLowerCase() : '';
30
+ const specificErrorMessage = error.message;
31
+ const originalErrorMessageLowerCase = error.message
32
+ ? error.message.toLowerCase()
33
+ : "";
44
34
  const errorStringLowerCase = String(error).toLowerCase();
45
35
  let isAuthError = false;
46
36
  let genericMessage = specificErrorMessage; // Initialize genericMessage with the specific one
47
- if (originalErrorMessageLowerCase.includes('api_key') ||
48
- originalErrorMessageLowerCase.includes('google_ai_api_key') ||
49
- originalErrorMessageLowerCase.includes('aws_access_key_id') ||
50
- originalErrorMessageLowerCase.includes('aws_secret_access_key') ||
51
- originalErrorMessageLowerCase.includes('aws_session_token') ||
52
- originalErrorMessageLowerCase.includes('google_application_credentials') ||
53
- originalErrorMessageLowerCase.includes('google_service_account_key') ||
54
- originalErrorMessageLowerCase.includes('google_auth_client_email') ||
55
- originalErrorMessageLowerCase.includes('anthropic_api_key') ||
56
- originalErrorMessageLowerCase.includes('azure_openai_api_key')) {
37
+ if (originalErrorMessageLowerCase.includes("api_key") ||
38
+ originalErrorMessageLowerCase.includes("google_ai_api_key") ||
39
+ originalErrorMessageLowerCase.includes("aws_access_key_id") ||
40
+ originalErrorMessageLowerCase.includes("aws_secret_access_key") ||
41
+ originalErrorMessageLowerCase.includes("aws_session_token") ||
42
+ originalErrorMessageLowerCase.includes("google_application_credentials") ||
43
+ originalErrorMessageLowerCase.includes("google_service_account_key") ||
44
+ originalErrorMessageLowerCase.includes("google_auth_client_email") ||
45
+ originalErrorMessageLowerCase.includes("anthropic_api_key") ||
46
+ originalErrorMessageLowerCase.includes("azure_openai_api_key")) {
57
47
  isAuthError = true;
58
48
  }
59
- else if ( // Fallback to checking the full stringified error if direct message didn't match
60
- errorStringLowerCase.includes('api_key') ||
61
- errorStringLowerCase.includes('google_ai_api_key') ||
62
- errorStringLowerCase.includes('aws_access_key_id') ||
63
- errorStringLowerCase.includes('aws_secret_access_key') ||
64
- errorStringLowerCase.includes('aws_session_token') ||
65
- errorStringLowerCase.includes('google_application_credentials') ||
66
- errorStringLowerCase.includes('google_service_account_key') ||
67
- errorStringLowerCase.includes('google_auth_client_email') ||
68
- errorStringLowerCase.includes('anthropic_api_key') ||
69
- errorStringLowerCase.includes('azure_openai_api_key')) {
49
+ else if (
50
+ // Fallback to checking the full stringified error if direct message didn't match
51
+ errorStringLowerCase.includes("api_key") ||
52
+ errorStringLowerCase.includes("google_ai_api_key") ||
53
+ errorStringLowerCase.includes("aws_access_key_id") ||
54
+ errorStringLowerCase.includes("aws_secret_access_key") ||
55
+ errorStringLowerCase.includes("aws_session_token") ||
56
+ errorStringLowerCase.includes("google_application_credentials") ||
57
+ errorStringLowerCase.includes("google_service_account_key") ||
58
+ errorStringLowerCase.includes("google_auth_client_email") ||
59
+ errorStringLowerCase.includes("anthropic_api_key") ||
60
+ errorStringLowerCase.includes("azure_openai_api_key")) {
70
61
  isAuthError = true;
71
62
  }
72
63
  if (isAuthError) {
73
- genericMessage = 'Authentication error: Missing or invalid API key/credentials for the selected provider.';
74
- }
75
- else if (originalErrorMessageLowerCase.includes('enotfound') || // Prefer direct message checks
76
- originalErrorMessageLowerCase.includes('econnrefused') ||
77
- originalErrorMessageLowerCase.includes('invalid-endpoint') ||
78
- originalErrorMessageLowerCase.includes('network error') ||
79
- originalErrorMessageLowerCase.includes('could not connect') ||
80
- originalErrorMessageLowerCase.includes('timeout') ||
81
- errorStringLowerCase.includes('enotfound') || // Fallback to full string
82
- errorStringLowerCase.includes('econnrefused') ||
83
- errorStringLowerCase.includes('invalid-endpoint') ||
84
- errorStringLowerCase.includes('network error') ||
85
- errorStringLowerCase.includes('could not connect') ||
86
- errorStringLowerCase.includes('timeout') // General timeout
64
+ genericMessage =
65
+ "Authentication error: Missing or invalid API key/credentials for the selected provider.";
66
+ }
67
+ else if (originalErrorMessageLowerCase.includes("enotfound") || // Prefer direct message checks
68
+ originalErrorMessageLowerCase.includes("econnrefused") ||
69
+ originalErrorMessageLowerCase.includes("invalid-endpoint") ||
70
+ originalErrorMessageLowerCase.includes("network error") ||
71
+ originalErrorMessageLowerCase.includes("could not connect") ||
72
+ originalErrorMessageLowerCase.includes("timeout") ||
73
+ errorStringLowerCase.includes("enotfound") || // Fallback to full string
74
+ errorStringLowerCase.includes("econnrefused") ||
75
+ errorStringLowerCase.includes("invalid-endpoint") ||
76
+ errorStringLowerCase.includes("network error") ||
77
+ errorStringLowerCase.includes("could not connect") ||
78
+ errorStringLowerCase.includes("timeout") // General timeout
87
79
  ) {
88
- genericMessage = 'Network error: Could not connect to the API endpoint or the request timed out.';
80
+ genericMessage =
81
+ "Network error: Could not connect to the API endpoint or the request timed out.";
89
82
  }
90
- else if (errorStringLowerCase.includes('not authorized') || errorStringLowerCase.includes('permission denied')) {
91
- genericMessage = 'Authorization error: You are not authorized to perform this action or access this resource.';
83
+ else if (errorStringLowerCase.includes("not authorized") ||
84
+ errorStringLowerCase.includes("permission denied")) {
85
+ genericMessage =
86
+ "Authorization error: You are not authorized to perform this action or access this resource.";
92
87
  }
93
88
  // If no specific condition matched, genericMessage remains error.message
94
89
  console.error(chalk.red(`❌ ${context} failed: ${genericMessage}`));
95
90
  // Smart hints for common errors (just string matching!)
96
- if (genericMessage.toLowerCase().includes('api key') || genericMessage.toLowerCase().includes('credential')) {
97
- console.error(chalk.yellow('💡 Set Google AI Studio API key (RECOMMENDED): export GOOGLE_AI_API_KEY=AIza-...'));
98
- console.error(chalk.yellow('💡 Or set OpenAI API key: export OPENAI_API_KEY=sk-...'));
99
- console.error(chalk.yellow('💡 Or set AWS Bedrock credentials: export AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... AWS_REGION=us-east-1'));
100
- console.error(chalk.yellow('💡 Or set Google Vertex AI credentials: export GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json'));
101
- console.error(chalk.yellow('💡 Or set Anthropic API key: export ANTHROPIC_API_KEY=sk-ant-...'));
102
- console.error(chalk.yellow('💡 Or set Azure OpenAI credentials: export AZURE_OPENAI_API_KEY=... AZURE_OPENAI_ENDPOINT=...'));
103
- }
104
- if (error.message.toLowerCase().includes('rate limit')) {
105
- console.error(chalk.yellow('💡 Try again in a few moments or use --provider vertex'));
106
- }
107
- if (error.message.toLowerCase().includes('not authorized') || error.message.toLowerCase().includes('permission denied')) {
108
- console.error(chalk.yellow('💡 Check your account permissions for the selected model/service.'));
109
- console.error(chalk.yellow('💡 For AWS Bedrock, ensure you have permissions for the specific model and consider using inference profile ARNs.'));
91
+ if (genericMessage.toLowerCase().includes("api key") ||
92
+ genericMessage.toLowerCase().includes("credential")) {
93
+ console.error(chalk.yellow("💡 Set Google AI Studio API key (RECOMMENDED): export GOOGLE_AI_API_KEY=AIza-..."));
94
+ console.error(chalk.yellow("💡 Or set OpenAI API key: export OPENAI_API_KEY=sk-..."));
95
+ console.error(chalk.yellow("💡 Or set AWS Bedrock credentials: export AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... AWS_REGION=us-east-1"));
96
+ console.error(chalk.yellow("💡 Or set Google Vertex AI credentials: export GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json"));
97
+ console.error(chalk.yellow("💡 Or set Anthropic API key: export ANTHROPIC_API_KEY=sk-ant-..."));
98
+ console.error(chalk.yellow("💡 Or set Azure OpenAI credentials: export AZURE_OPENAI_API_KEY=... AZURE_OPENAI_ENDPOINT=..."));
99
+ }
100
+ if (error.message.toLowerCase().includes("rate limit")) {
101
+ console.error(chalk.yellow("💡 Try again in a few moments or use --provider vertex"));
102
+ }
103
+ if (error.message.toLowerCase().includes("not authorized") ||
104
+ error.message.toLowerCase().includes("permission denied")) {
105
+ console.error(chalk.yellow("💡 Check your account permissions for the selected model/service."));
106
+ console.error(chalk.yellow("💡 For AWS Bedrock, ensure you have permissions for the specific model and consider using inference profile ARNs."));
110
107
  }
111
108
  process.exit(1);
112
109
  }
113
- function validateConfig() {
114
- const hasGoogleAI = !!process.env.GOOGLE_AI_API_KEY;
115
- const hasOpenAI = !!process.env.OPENAI_API_KEY;
116
- const hasAWS = !!(process.env.AWS_REGION || process.env.AWS_ACCESS_KEY_ID);
117
- const hasGoogle = !!(process.env.GOOGLE_APPLICATION_CREDENTIALS || process.env.GOOGLE_SERVICE_ACCOUNT_KEY || process.env.GOOGLE_AUTH_CLIENT_EMAIL);
118
- const hasAnthropic = !!process.env.ANTHROPIC_API_KEY;
119
- const hasAzure = !!(process.env.AZURE_OPENAI_API_KEY && process.env.AZURE_OPENAI_ENDPOINT);
120
- if (!hasGoogleAI && !hasOpenAI && !hasAWS && !hasGoogle && !hasAnthropic && !hasAzure) {
121
- console.error(chalk.red('⚠️ No AI provider credentials found'));
122
- console.error(chalk.yellow('💡 Set one of:'));
123
- console.error(chalk.yellow(' • GOOGLE_AI_API_KEY=AIza-...'));
124
- console.error(chalk.yellow(' • OPENAI_API_KEY=sk-...'));
125
- console.error(chalk.yellow(' • AWS_REGION=us-east-1 (+ AWS credentials)'));
126
- console.error(chalk.yellow(' • GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json'));
127
- console.error(chalk.yellow(' • ANTHROPIC_API_KEY=sk-ant-...'));
128
- console.error(chalk.yellow(' • AZURE_OPENAI_API_KEY=... (+ AZURE_OPENAI_ENDPOINT)'));
129
- console.error(chalk.blue('\n📚 See: https://github.com/juspay/neurolink#setup'));
130
- process.exit(1);
131
- }
132
- }
133
110
  // Initialize SDK
134
111
  const sdk = new NeuroLink();
135
112
  // Manual pre-validation for unknown flags
136
113
  const args = hideBin(process.argv);
137
114
  // Enhanced CLI with Professional UX
138
115
  const cli = yargs(args)
139
- .scriptName('neurolink')
140
- .usage('Usage: $0 <command> [options]')
116
+ .scriptName("neurolink")
117
+ .usage("Usage: $0 <command> [options]")
141
118
  .version()
142
119
  .help()
143
- .alias('h', 'help')
144
- .alias('V', 'version')
120
+ .alias("h", "help")
121
+ .alias("V", "version")
145
122
  .strictOptions()
146
123
  .strictCommands()
147
- .demandCommand(1, '')
148
- .epilogue('For more info: https://github.com/juspay/neurolink')
149
- .showHelpOnFail(true, 'Specify --help for available options')
124
+ .demandCommand(1, "")
125
+ .epilogue("For more info: https://github.com/juspay/neurolink")
126
+ .showHelpOnFail(true, "Specify --help for available options")
150
127
  .middleware((argv) => {
151
128
  // Control SDK logging based on debug flag
152
129
  if (argv.debug) {
153
- process.env.NEUROLINK_DEBUG = 'true';
130
+ process.env.NEUROLINK_DEBUG = "true";
154
131
  }
155
132
  else {
156
133
  // Always set to false when debug is not enabled (including when not provided)
157
- process.env.NEUROLINK_DEBUG = 'false';
134
+ process.env.NEUROLINK_DEBUG = "false";
158
135
  }
159
136
  // Keep existing quiet middleware
160
- if (process.env.NEUROLINK_QUIET === 'true' && typeof argv.quiet === 'undefined') {
137
+ if (process.env.NEUROLINK_QUIET === "true" &&
138
+ typeof argv.quiet === "undefined") {
161
139
  argv.quiet = true;
162
140
  }
163
141
  })
164
142
  .fail((msg, err, yargsInstance) => {
165
143
  const exitProcess = () => {
166
- if (!process.exitCode)
144
+ if (!process.exitCode) {
167
145
  process.exit(1);
146
+ }
168
147
  };
169
148
  if (err) {
170
149
  // Error likely from an async command handler (e.g., via handleError)
@@ -175,16 +154,19 @@ const cli = yargs(args)
175
154
  // A simple heuristic: if the error message doesn't look like one of our handled generic messages,
176
155
  // it might be a direct yargs parsing error.
177
156
  const isLikelyYargsInternalError = err.message && // Ensure err.message exists
178
- !err.message.includes('Authentication error') &&
179
- !err.message.includes('Network error') &&
180
- !err.message.includes('Authorization error') &&
181
- !err.message.includes('Permission denied') && // from config export
182
- !err.message.includes('Invalid or unparseable JSON'); // from config import
157
+ !err.message.includes("Authentication error") &&
158
+ !err.message.includes("Network error") &&
159
+ !err.message.includes("Authorization error") &&
160
+ !err.message.includes("Permission denied") && // from config export
161
+ !err.message.includes("Invalid or unparseable JSON"); // from config import
183
162
  if (!alreadyExitedByHandleError) {
184
- process.stderr.write(chalk.red(`CLI Error: ${err.message || msg || 'An unexpected error occurred.'}\n`));
163
+ process.stderr.write(chalk.red(`CLI Error: ${err.message || msg || "An unexpected error occurred."}\n`));
185
164
  // If it's a yargs internal parsing error, show help.
186
165
  if (isLikelyYargsInternalError && msg) {
187
- yargsInstance.showHelp(h => { process.stderr.write(h + '\n'); exitProcess(); });
166
+ yargsInstance.showHelp((h) => {
167
+ process.stderr.write(h + "\n");
168
+ exitProcess();
169
+ });
188
170
  return;
189
171
  }
190
172
  exitProcess();
@@ -194,122 +176,225 @@ const cli = yargs(args)
194
176
  // Yargs parsing/validation error (msg is present, err is null)
195
177
  if (msg) {
196
178
  let processedMsg = `Error: ${msg}\n`;
197
- if (msg.includes('Not enough non-option arguments') || msg.includes('Missing required argument') || msg.includes('Unknown command')) {
179
+ if (msg.includes("Not enough non-option arguments") ||
180
+ msg.includes("Missing required argument") ||
181
+ msg.includes("Unknown command")) {
198
182
  process.stderr.write(chalk.red(processedMsg)); // Print error first
199
- yargsInstance.showHelp(h => { process.stderr.write('\n' + h + '\n'); exitProcess(); });
183
+ yargsInstance.showHelp((h) => {
184
+ process.stderr.write("\n" + h + "\n");
185
+ exitProcess();
186
+ });
200
187
  return; // Exit happens in callback
201
188
  }
202
- else if (msg.includes('Unknown argument') || msg.includes('Invalid values')) {
189
+ else if (msg.includes("Unknown argument") ||
190
+ msg.includes("Invalid values")) {
203
191
  processedMsg = `Error: ${msg}\nUse --help to see available options.\n`;
204
192
  }
205
193
  process.stderr.write(chalk.red(processedMsg));
206
194
  }
207
195
  else {
208
196
  // No specific message, but failure occurred (e.g. demandCommand failed silently)
209
- yargsInstance.showHelp(h => { process.stderr.write(h + '\n'); exitProcess(); });
197
+ yargsInstance.showHelp((h) => {
198
+ process.stderr.write(h + "\n");
199
+ exitProcess();
200
+ });
210
201
  return; // Exit happens in callback
211
202
  }
212
203
  exitProcess(); // Default exit
213
204
  })
214
205
  // Generate Text Command
215
- .command(['generate-text <prompt>', 'generate <prompt>'], 'Generate text using AI providers', (yargsInstance) => yargsInstance
216
- .usage('Usage: $0 generate-text <prompt> [options]')
217
- .positional('prompt', {
218
- type: 'string',
219
- description: 'Text prompt for AI generation',
206
+ .command(["generate-text <prompt>", "generate <prompt>"], "Generate text using AI providers", (yargsInstance) => yargsInstance
207
+ .usage("Usage: $0 generate-text <prompt> [options]")
208
+ .positional("prompt", {
209
+ type: "string",
210
+ description: "Text prompt for AI generation",
220
211
  demandOption: true,
221
212
  })
222
- .option('provider', {
223
- choices: ['auto', 'openai', 'bedrock', 'vertex', 'anthropic', 'azure', 'google-ai', 'huggingface', 'ollama', 'mistral'],
224
- default: 'auto',
225
- description: 'AI provider to use (auto-selects best available)'
226
- })
227
- .option('temperature', { type: 'number', default: 0.7, description: 'Creativity level (0.0 = focused, 1.0 = creative)' })
228
- .option('max-tokens', { type: 'number', default: 500, description: 'Maximum tokens to generate' })
229
- .option('system', { type: 'string', description: 'System prompt to guide AI behavior' })
230
- .option('format', { choices: ['text', 'json'], default: 'text', alias: 'f', description: 'Output format' })
231
- .option('debug', { type: 'boolean', default: false, description: 'Enable debug mode with verbose output' }) // Kept for potential specific debug logic
232
- .option('timeout', { type: 'number', default: 30000, description: 'Timeout for the request in milliseconds' })
233
- .example('$0 generate-text "Hello world"', 'Basic text generation')
234
- .example('$0 generate-text "Write a story" --provider openai', 'Use specific provider'), async (argv) => {
213
+ .option("provider", {
214
+ choices: [
215
+ "auto",
216
+ "openai",
217
+ "bedrock",
218
+ "vertex",
219
+ "anthropic",
220
+ "azure",
221
+ "google-ai",
222
+ "huggingface",
223
+ "ollama",
224
+ "mistral",
225
+ ],
226
+ default: "auto",
227
+ description: "AI provider to use (auto-selects best available)",
228
+ })
229
+ .option("temperature", {
230
+ type: "number",
231
+ default: 0.7,
232
+ description: "Creativity level (0.0 = focused, 1.0 = creative)",
233
+ })
234
+ .option("max-tokens", {
235
+ type: "number",
236
+ default: 500,
237
+ description: "Maximum tokens to generate",
238
+ })
239
+ .option("system", {
240
+ type: "string",
241
+ description: "System prompt to guide AI behavior",
242
+ })
243
+ .option("format", {
244
+ choices: ["text", "json"],
245
+ default: "text",
246
+ alias: "f",
247
+ description: "Output format",
248
+ })
249
+ .option("debug", {
250
+ type: "boolean",
251
+ default: false,
252
+ description: "Enable debug mode with verbose output",
253
+ }) // Kept for potential specific debug logic
254
+ .option("timeout", {
255
+ type: "number",
256
+ default: 30000,
257
+ description: "Timeout for the request in milliseconds",
258
+ })
259
+ .option("disable-tools", {
260
+ type: "boolean",
261
+ default: false,
262
+ description: "Disable MCP tool integration (tools enabled by default)",
263
+ })
264
+ .example('$0 generate-text "Hello world"', "Basic text generation")
265
+ .example('$0 generate-text "Write a story" --provider openai', "Use specific provider")
266
+ .example('$0 generate-text "What time is it?"', "Use with natural tool integration (default)")
267
+ .example('$0 generate-text "Hello world" --disable-tools', "Use without tool integration"), async (argv) => {
235
268
  let originalConsole = {};
236
- if (argv.format === 'json' && !argv.quiet) { // Suppress only if not quiet, as quiet implies no spinners anyway
269
+ if (argv.format === "json" && !argv.quiet) {
270
+ // Suppress only if not quiet, as quiet implies no spinners anyway
237
271
  originalConsole = { ...console };
238
272
  Object.keys(originalConsole).forEach((key) => {
239
- if (typeof console[key] === 'function') {
273
+ if (typeof console[key] === "function") {
240
274
  console[key] = () => { };
241
275
  }
242
276
  });
243
277
  }
244
- const spinner = argv.format === 'json' || argv.quiet ? null : ora('🤖 Generating text...').start();
278
+ const spinner = argv.format === "json" || argv.quiet
279
+ ? null
280
+ : ora("🤖 Generating text...").start();
245
281
  try {
246
282
  const timeoutPromise = new Promise((_, reject) => {
247
283
  setTimeout(() => reject(new Error(`Request timeout (${argv.timeout}ms)`)), argv.timeout);
248
284
  });
285
+ // Use enhanced NeuroLink SDK with Lighthouse-style natural tool access
249
286
  const generatePromise = sdk.generateText({
250
- prompt: argv.prompt, // Cast because demandOption is true
251
- provider: argv.provider === 'auto' ? undefined : argv.provider,
287
+ prompt: argv.prompt,
288
+ provider: argv.provider === "auto"
289
+ ? undefined
290
+ : argv.provider,
252
291
  temperature: argv.temperature,
253
292
  maxTokens: argv.maxTokens,
254
- systemPrompt: argv.system
293
+ systemPrompt: argv.system,
294
+ // Lighthouse-style: Tools enabled by default, disable only if explicitly requested
295
+ disableTools: argv.disableTools === true, // Default true, can be disabled with --no-enable-tools
255
296
  });
256
- const result = await Promise.race([generatePromise, timeoutPromise]);
257
- if (argv.format === 'json' && originalConsole.log) {
297
+ const result = (await Promise.race([
298
+ generatePromise,
299
+ timeoutPromise,
300
+ ]));
301
+ if (argv.format === "json" && originalConsole.log) {
258
302
  Object.assign(console, originalConsole);
259
303
  }
260
- if (spinner)
261
- spinner.succeed(chalk.green('✅ Text generated successfully!'));
262
- if (argv.format === 'json') {
304
+ if (spinner) {
305
+ spinner.succeed(chalk.green("✅ Text generated successfully!"));
306
+ }
307
+ if (argv.format === "json") {
263
308
  const jsonOutput = {
264
- content: result.content || '', provider: result.provider,
265
- usage: result.usage || { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
266
- responseTime: result.responseTime || 0
309
+ content: result.content || "",
310
+ provider: result.provider,
311
+ usage: result.usage || {
312
+ promptTokens: 0,
313
+ completionTokens: 0,
314
+ totalTokens: 0,
315
+ },
316
+ responseTime: result.responseTime || 0,
267
317
  };
268
- process.stdout.write(JSON.stringify(jsonOutput, null, 2) + '\n');
318
+ process.stdout.write(JSON.stringify(jsonOutput, null, 2) + "\n");
269
319
  }
270
320
  else if (argv.debug) {
271
321
  // Debug mode: Show AI response + full metadata
272
- if (result.content)
273
- console.log('\n' + result.content + '\n');
274
- console.log(JSON.stringify({ provider: result.provider, usage: result.usage, responseTime: result.responseTime }, null, 2));
275
- if (result.usage)
322
+ if (result.content) {
323
+ console.log("\n" + result.content + "\n");
324
+ }
325
+ console.log(JSON.stringify({
326
+ provider: result.provider,
327
+ usage: result.usage,
328
+ responseTime: result.responseTime,
329
+ }, null, 2));
330
+ if (result.usage) {
276
331
  console.log(chalk.blue(`ℹ️ ${result.usage.totalTokens} tokens used`));
332
+ }
277
333
  }
278
334
  else {
279
335
  // Default mode: Clean AI response only
280
- if (result.content)
336
+ if (result.content) {
281
337
  console.log(result.content);
338
+ }
282
339
  }
283
340
  // Explicitly exit to prevent hanging, especially with Google AI Studio
284
341
  process.exit(0);
285
342
  }
286
343
  catch (error) {
287
- if (argv.format === 'json' && originalConsole.log) {
344
+ if (argv.format === "json" && originalConsole.log) {
288
345
  Object.assign(console, originalConsole);
289
346
  }
290
- if (spinner)
347
+ if (spinner) {
291
348
  spinner.fail();
292
- if (argv.format === 'json') {
293
- process.stdout.write(JSON.stringify({ error: error.message, success: false }, null, 2) + '\n');
349
+ }
350
+ if (argv.format === "json") {
351
+ process.stdout.write(JSON.stringify({ error: error.message, success: false }, null, 2) + "\n");
294
352
  process.exit(1);
295
353
  }
296
354
  else {
297
- handleError(error, 'Text generation');
355
+ handleError(error, "Text generation");
298
356
  }
299
357
  }
300
358
  })
301
359
  // Stream Text Command
302
- .command('stream <prompt>', 'Stream text generation in real-time', (yargsInstance) => yargsInstance
303
- .usage('Usage: $0 stream <prompt> [options]')
304
- .positional('prompt', { type: 'string', description: 'Text prompt for streaming', demandOption: true })
305
- .option('provider', { choices: ['auto', 'openai', 'bedrock', 'vertex', 'anthropic', 'azure', 'google-ai', 'huggingface', 'ollama', 'mistral'], default: 'auto', description: 'AI provider to use' })
306
- .option('temperature', { type: 'number', default: 0.7, description: 'Creativity level' })
307
- .option('debug', { type: 'boolean', default: false, description: 'Enable debug mode with interleaved logging' })
308
- .example('$0 stream "Tell me a story"', 'Stream a story in real-time'), async (argv) => {
360
+ .command("stream <prompt>", "Stream text generation in real-time", (yargsInstance) => yargsInstance
361
+ .usage("Usage: $0 stream <prompt> [options]")
362
+ .positional("prompt", {
363
+ type: "string",
364
+ description: "Text prompt for streaming",
365
+ demandOption: true,
366
+ })
367
+ .option("provider", {
368
+ choices: [
369
+ "auto",
370
+ "openai",
371
+ "bedrock",
372
+ "vertex",
373
+ "anthropic",
374
+ "azure",
375
+ "google-ai",
376
+ "huggingface",
377
+ "ollama",
378
+ "mistral",
379
+ ],
380
+ default: "auto",
381
+ description: "AI provider to use",
382
+ })
383
+ .option("temperature", {
384
+ type: "number",
385
+ default: 0.7,
386
+ description: "Creativity level",
387
+ })
388
+ .option("debug", {
389
+ type: "boolean",
390
+ default: false,
391
+ description: "Enable debug mode with interleaved logging",
392
+ })
393
+ .example('$0 stream "Tell me a story"', "Stream a story in real-time"), async (argv) => {
309
394
  // Default mode: Simple streaming message
310
395
  // Debug mode: More detailed information
311
396
  if (!argv.quiet && !argv.debug) {
312
- console.log(chalk.blue('🔄 Streaming...'));
397
+ console.log(chalk.blue("🔄 Streaming..."));
313
398
  }
314
399
  else if (!argv.quiet && argv.debug) {
315
400
  console.log(chalk.blue(`🔄 Streaming from ${argv.provider} provider with debug logging...\n`));
@@ -317,271 +402,545 @@ const cli = yargs(args)
317
402
  try {
318
403
  const stream = await sdk.generateTextStream({
319
404
  prompt: argv.prompt,
320
- provider: argv.provider === 'auto' ? undefined : argv.provider,
321
- temperature: argv.temperature
405
+ provider: argv.provider === "auto"
406
+ ? undefined
407
+ : argv.provider,
408
+ temperature: argv.temperature,
322
409
  });
323
410
  for await (const chunk of stream) {
324
411
  process.stdout.write(chunk.content);
325
412
  // In debug mode, interleaved logging would appear here
326
413
  // (SDK logs are controlled by NEUROLINK_DEBUG set in middleware)
327
414
  }
328
- if (!argv.quiet)
329
- process.stdout.write('\n'); // Ensure newline after stream
415
+ if (!argv.quiet) {
416
+ process.stdout.write("\n");
417
+ } // Ensure newline after stream
330
418
  }
331
419
  catch (error) {
332
- handleError(error, 'Text streaming');
420
+ handleError(error, "Text streaming");
333
421
  }
334
422
  })
335
423
  // Batch Processing Command
336
- .command('batch <file>', 'Process multiple prompts from a file', (yargsInstance) => yargsInstance
337
- .usage('Usage: $0 batch <file> [options]')
338
- .positional('file', { type: 'string', description: 'File with prompts (one per line)', demandOption: true })
339
- .option('output', { type: 'string', description: 'Output file for results (default: stdout)' })
340
- .option('delay', { type: 'number', default: 1000, description: 'Delay between requests in milliseconds' })
341
- .option('provider', { choices: ['auto', 'openai', 'bedrock', 'vertex', 'anthropic', 'azure', 'google-ai', 'huggingface', 'ollama', 'mistral'], default: 'auto', description: 'AI provider to use' })
342
- .option('timeout', { type: 'number', default: 30000, description: 'Timeout for each request in milliseconds' })
343
- .option('temperature', { type: 'number', description: 'Global temperature for batch jobs' })
344
- .option('max-tokens', { type: 'number', description: 'Global max tokens for batch jobs' })
345
- .option('system', { type: 'string', description: 'Global system prompt for batch jobs' })
346
- .option('debug', { type: 'boolean', default: false, description: 'Enable debug mode with detailed per-item logging' })
347
- .example('$0 batch prompts.txt --output results.json', 'Process and save to file'), async (argv) => {
424
+ .command("batch <file>", "Process multiple prompts from a file", (yargsInstance) => yargsInstance
425
+ .usage("Usage: $0 batch <file> [options]")
426
+ .positional("file", {
427
+ type: "string",
428
+ description: "File with prompts (one per line)",
429
+ demandOption: true,
430
+ })
431
+ .option("output", {
432
+ type: "string",
433
+ description: "Output file for results (default: stdout)",
434
+ })
435
+ .option("delay", {
436
+ type: "number",
437
+ default: 1000,
438
+ description: "Delay between requests in milliseconds",
439
+ })
440
+ .option("provider", {
441
+ choices: [
442
+ "auto",
443
+ "openai",
444
+ "bedrock",
445
+ "vertex",
446
+ "anthropic",
447
+ "azure",
448
+ "google-ai",
449
+ "huggingface",
450
+ "ollama",
451
+ "mistral",
452
+ ],
453
+ default: "auto",
454
+ description: "AI provider to use",
455
+ })
456
+ .option("timeout", {
457
+ type: "number",
458
+ default: 30000,
459
+ description: "Timeout for each request in milliseconds",
460
+ })
461
+ .option("temperature", {
462
+ type: "number",
463
+ description: "Global temperature for batch jobs",
464
+ })
465
+ .option("max-tokens", {
466
+ type: "number",
467
+ description: "Global max tokens for batch jobs",
468
+ })
469
+ .option("system", {
470
+ type: "string",
471
+ description: "Global system prompt for batch jobs",
472
+ })
473
+ .option("debug", {
474
+ type: "boolean",
475
+ default: false,
476
+ description: "Enable debug mode with detailed per-item logging",
477
+ })
478
+ .example("$0 batch prompts.txt --output results.json", "Process and save to file"), async (argv) => {
348
479
  const spinner = argv.quiet ? null : ora().start();
349
480
  try {
350
- if (!fs.existsSync(argv.file))
481
+ if (!fs.existsSync(argv.file)) {
351
482
  throw new Error(`File not found: ${argv.file}`);
483
+ }
352
484
  const buffer = fs.readFileSync(argv.file);
353
485
  const isLikelyBinary = buffer.includes(0) ||
354
- buffer.toString('hex', 0, 100).includes('0000') ||
355
- (!buffer.toString('utf8', 0, 1024).includes('\n') && buffer.length > 512);
356
- if (isLikelyBinary)
486
+ buffer.toString("hex", 0, 100).includes("0000") ||
487
+ (!buffer.toString("utf8", 0, 1024).includes("\n") &&
488
+ buffer.length > 512);
489
+ if (isLikelyBinary) {
357
490
  throw new Error(`Invalid file format: Binary file detected at "${argv.file}". Batch processing requires a plain text file.`);
358
- const prompts = buffer.toString('utf8').split('\n').map(line => line.trim()).filter(Boolean);
359
- if (prompts.length === 0)
360
- throw new Error('No prompts found in file');
361
- if (spinner)
491
+ }
492
+ const prompts = buffer
493
+ .toString("utf8")
494
+ .split("\n")
495
+ .map((line) => line.trim())
496
+ .filter(Boolean);
497
+ if (prompts.length === 0) {
498
+ throw new Error("No prompts found in file");
499
+ }
500
+ if (spinner) {
362
501
  spinner.text = `📦 Processing ${prompts.length} prompts...`;
363
- else if (!argv.quiet)
502
+ }
503
+ else if (!argv.quiet) {
364
504
  console.log(chalk.blue(`📦 Processing ${prompts.length} prompts...\n`));
505
+ }
365
506
  const results = [];
366
507
  for (let i = 0; i < prompts.length; i++) {
367
- if (spinner)
508
+ if (spinner) {
368
509
  spinner.text = `Processing ${i + 1}/${prompts.length}: ${prompts[i].substring(0, 30)}...`;
510
+ }
369
511
  try {
370
- const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Request timeout')), argv.timeout));
512
+ const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error("Request timeout")), argv.timeout));
371
513
  const generatePromise = sdk.generateText({
372
514
  prompt: prompts[i],
373
- provider: argv.provider === 'auto' ? undefined : argv.provider,
374
- temperature: argv.temperature, maxTokens: argv.maxTokens, systemPrompt: argv.system
515
+ provider: argv.provider === "auto"
516
+ ? undefined
517
+ : argv.provider,
518
+ temperature: argv.temperature,
519
+ maxTokens: argv.maxTokens,
520
+ systemPrompt: argv.system,
375
521
  });
376
- const result = await Promise.race([generatePromise, timeoutPromise]);
522
+ const result = (await Promise.race([
523
+ generatePromise,
524
+ timeoutPromise,
525
+ ]));
377
526
  results.push({ prompt: prompts[i], response: result.content });
378
- if (spinner)
379
- spinner.render(); // Update spinner without changing text
527
+ if (spinner) {
528
+ spinner.render();
529
+ } // Update spinner without changing text
380
530
  }
381
531
  catch (error) {
382
- results.push({ prompt: prompts[i], error: error.message });
383
- if (spinner)
532
+ results.push({
533
+ prompt: prompts[i],
534
+ error: error.message,
535
+ });
536
+ if (spinner) {
384
537
  spinner.render();
538
+ }
539
+ }
540
+ if (argv.delay && i < prompts.length - 1) {
541
+ await new Promise((resolve) => setTimeout(resolve, argv.delay));
385
542
  }
386
- if (argv.delay && i < prompts.length - 1)
387
- await new Promise(resolve => setTimeout(resolve, argv.delay));
388
543
  }
389
- if (spinner)
390
- spinner.succeed(chalk.green('✅ Batch processing complete!'));
544
+ if (spinner) {
545
+ spinner.succeed(chalk.green("✅ Batch processing complete!"));
546
+ }
391
547
  const outputData = JSON.stringify(results, null, 2);
392
548
  if (argv.output) {
393
549
  fs.writeFileSync(argv.output, outputData);
394
- if (!argv.quiet)
550
+ if (!argv.quiet) {
395
551
  console.log(chalk.green(`\n✅ Results saved to ${argv.output}`));
552
+ }
396
553
  }
397
554
  else {
398
- process.stdout.write(outputData + '\n');
555
+ process.stdout.write(outputData + "\n");
399
556
  }
400
557
  }
401
558
  catch (error) {
402
- if (spinner)
559
+ if (spinner) {
403
560
  spinner.fail();
404
- handleError(error, 'Batch processing');
561
+ }
562
+ handleError(error, "Batch processing");
405
563
  }
406
564
  })
407
565
  // Provider Command Group (Corrected Structure)
408
- .command('provider <subcommand>', 'Manage AI provider configurations and status', (yargsProvider) => {
566
+ .command("provider <subcommand>", "Manage AI provider configurations and status", (yargsProvider) => {
567
+ // Builder for the main 'provider' command
409
568
  yargsProvider
410
- .usage('Usage: $0 provider <subcommand> [options]') // Add usage here
411
- .command('status', 'Check status of all configured AI providers', (y) => y
412
- .usage('Usage: $0 provider status [options]')
413
- .option('verbose', { type: 'boolean', alias: 'v', description: 'Show detailed information' }) // Default is handled by middleware if NEUROLINK_DEBUG is set
414
- .example('$0 provider status', 'Check all providers')
415
- .example('$0 provider status --verbose', 'Show detailed status information'), async (argv) => {
569
+ .usage("Usage: $0 provider <subcommand> [options]") // Add usage here
570
+ .command("status", "Check status of all configured AI providers", (y) => y
571
+ .usage("Usage: $0 provider status [options]")
572
+ .option("verbose", {
573
+ type: "boolean",
574
+ alias: "v",
575
+ description: "Show detailed information",
576
+ }) // Default is handled by middleware if NEUROLINK_DEBUG is set
577
+ .example("$0 provider status", "Check all providers")
578
+ .example("$0 provider status --verbose", "Show detailed status information"), async (argv) => {
416
579
  if (argv.verbose && !argv.quiet) {
417
- console.log(chalk.yellow('ℹ️ Verbose mode enabled. Displaying detailed status.\n')); // Added newline
580
+ console.log(chalk.yellow("ℹ️ Verbose mode enabled. Displaying detailed status.\n")); // Added newline
418
581
  }
419
- const spinner = argv.quiet ? null : ora('🔍 Checking AI provider status...\n').start();
582
+ const spinner = argv.quiet
583
+ ? null
584
+ : ora("🔍 Checking AI provider status...\n").start();
420
585
  // Middleware sets argv.verbose if NEUROLINK_DEBUG is true and --verbose is not specified
421
586
  // Removed the spinner.stopAndPersist logic from here as it's handled before spinner start
422
- const providers = ['openai', 'bedrock', 'vertex', 'anthropic', 'azure', 'google-ai', 'huggingface', 'ollama', 'mistral'];
587
+ const providers = [
588
+ "openai",
589
+ "bedrock",
590
+ "vertex",
591
+ "anthropic",
592
+ "azure",
593
+ "google-ai",
594
+ "huggingface",
595
+ "ollama",
596
+ "mistral",
597
+ ];
598
+ // Import hasProviderEnvVars to check environment variables
599
+ const { hasProviderEnvVars } = await import("../lib/utils/providerUtils.js");
423
600
  const results = [];
424
601
  for (const p of providers) {
425
- if (spinner)
602
+ if (spinner) {
426
603
  spinner.text = `Testing ${p}...`;
604
+ }
605
+ // First check if provider has env vars configured
606
+ const hasEnvVars = hasProviderEnvVars(p);
607
+ if (!hasEnvVars && p !== "ollama") {
608
+ // No env vars, don't even try to test
609
+ results.push({
610
+ provider: p,
611
+ status: "not-configured",
612
+ configured: false,
613
+ error: "Missing required environment variables",
614
+ });
615
+ if (spinner) {
616
+ spinner.fail(`${p}: ${chalk.gray("⚪ Not configured")} - Missing environment variables`);
617
+ }
618
+ else if (!argv.quiet) {
619
+ console.log(`${p}: ${chalk.gray("⚪ Not configured")} - Missing environment variables`);
620
+ }
621
+ continue;
622
+ }
623
+ // Special handling for Ollama
624
+ if (p === "ollama") {
625
+ try {
626
+ // First, check if the service is running
627
+ const serviceResponse = await fetch('http://localhost:11434/api/tags', {
628
+ method: 'GET',
629
+ signal: AbortSignal.timeout(2000)
630
+ });
631
+ if (!serviceResponse.ok) {
632
+ throw new Error("Ollama service not responding");
633
+ }
634
+ // Service is running, now check if the default model is available
635
+ const { models } = await serviceResponse.json();
636
+ const defaultOllamaModel = "llama3.2:latest";
637
+ const modelIsAvailable = models.some((m) => m.name === defaultOllamaModel);
638
+ if (modelIsAvailable) {
639
+ results.push({
640
+ provider: p,
641
+ status: "working",
642
+ configured: true,
643
+ authenticated: true,
644
+ responseTime: 0,
645
+ });
646
+ if (spinner) {
647
+ spinner.succeed(`${p}: ${chalk.green("✅ Working")} - Service running and model '${defaultOllamaModel}' is available.`);
648
+ }
649
+ }
650
+ else {
651
+ results.push({
652
+ provider: p,
653
+ status: "failed",
654
+ configured: true,
655
+ authenticated: false,
656
+ error: `Ollama service is running, but model '${defaultOllamaModel}' is not found. Please run 'ollama pull ${defaultOllamaModel}'.`,
657
+ });
658
+ if (spinner) {
659
+ spinner.fail(`${p}: ${chalk.red("❌ Model Not Found")} - Run 'ollama pull ${defaultOllamaModel}'`);
660
+ }
661
+ }
662
+ }
663
+ catch (error) {
664
+ results.push({
665
+ provider: p,
666
+ status: "failed",
667
+ configured: false,
668
+ authenticated: false,
669
+ error: "Ollama is not running. Please start with: ollama serve",
670
+ });
671
+ if (spinner) {
672
+ spinner.fail(`${p}: ${chalk.red("❌ Failed")} - Service not running`);
673
+ }
674
+ }
675
+ continue;
676
+ }
677
+ // Provider has env vars, now test authentication
427
678
  try {
428
679
  const start = Date.now();
429
- await sdk.generateText({ prompt: 'test', provider: p, maxTokens: 1 });
680
+ // Import AIProviderFactory to test providers directly without fallback
681
+ const { AIProviderFactory } = await import("../lib/core/factory.js");
682
+ // Create and test provider directly to avoid automatic fallback
683
+ const provider = await AIProviderFactory.createProvider(p);
684
+ await provider.generateText({
685
+ prompt: "test",
686
+ maxTokens: 1,
687
+ });
430
688
  const duration = Date.now() - start;
431
- results.push({ provider: p, status: 'working', responseTime: duration });
432
- if (spinner)
433
- spinner.succeed(`${p}: ${chalk.green('✅ Working')} (${duration}ms)`);
434
- else if (!argv.quiet)
435
- console.log(`${p}: ${chalk.green('✅ Working')} (${duration}ms)`);
689
+ results.push({
690
+ provider: p,
691
+ status: "working",
692
+ configured: true,
693
+ authenticated: true,
694
+ responseTime: duration,
695
+ });
696
+ if (spinner) {
697
+ spinner.succeed(`${p}: ${chalk.green("✅ Working")} (${duration}ms)`);
698
+ }
699
+ else if (!argv.quiet) {
700
+ console.log(`${p}: ${chalk.green("✅ Working")} (${duration}ms)`);
701
+ }
436
702
  }
437
703
  catch (error) {
438
- results.push({ provider: p, status: 'failed', error: error.message });
439
- if (spinner)
440
- spinner.fail(`${p}: ${chalk.red('❌ Failed')} - ${error.message.split('\n')[0]}`);
441
- else if (!argv.quiet)
442
- console.error(`${p}: ${chalk.red('❌ Failed')} - ${error.message.split('\n')[0]}`);
704
+ const errorMsg = error.message;
705
+ let authStatus = false;
706
+ let statusText = "Failed";
707
+ // Check if it's an authentication error
708
+ if (errorMsg.includes('401') || errorMsg.includes('Unauthorized') ||
709
+ errorMsg.includes('Invalid API') || errorMsg.includes('Authentication') ||
710
+ errorMsg.includes('API key') || errorMsg.includes('not authorized') ||
711
+ errorMsg.includes('ExpiredToken') || errorMsg.includes('expired')) {
712
+ statusText = "Invalid credentials";
713
+ }
714
+ else if (errorMsg.includes('fetch') || errorMsg.includes('blob')) {
715
+ statusText = "Service error";
716
+ }
717
+ results.push({
718
+ provider: p,
719
+ status: "failed",
720
+ configured: true,
721
+ authenticated: authStatus,
722
+ error: errorMsg,
723
+ });
724
+ if (spinner) {
725
+ spinner.fail(`${p}: ${chalk.red("❌ " + statusText)} - ${errorMsg.split("\n")[0]}`);
726
+ }
727
+ else if (!argv.quiet) {
728
+ console.error(`${p}: ${chalk.red("❌ " + statusText)} - ${errorMsg.split("\n")[0]}`);
729
+ }
443
730
  }
444
731
  }
445
- const working = results.filter(r => r.status === 'working').length;
446
- if (spinner)
447
- spinner.info(chalk.blue(`\n📊 Summary: ${working}/${results.length} providers working`));
448
- else if (!argv.quiet)
449
- console.log(chalk.blue(`\n📊 Summary: ${working}/${results.length} providers working`));
732
+ const working = results.filter((r) => r.status === "working").length;
733
+ const configured = results.filter((r) => r.configured).length;
734
+ if (spinner) {
735
+ spinner.info(chalk.blue(`\n📊 Summary: ${working}/${results.length} providers working, ${configured}/${results.length} configured`));
736
+ }
737
+ else if (!argv.quiet) {
738
+ console.log(chalk.blue(`\n📊 Summary: ${working}/${results.length} providers working, ${configured}/${results.length} configured`));
739
+ }
450
740
  if (argv.verbose && !argv.quiet) {
451
- console.log(chalk.blue('\n📋 Detailed Results:'));
741
+ console.log(chalk.blue("\n📋 Detailed Results:"));
452
742
  console.log(JSON.stringify(results, null, 2));
453
743
  }
454
744
  })
455
- .command('list', 'List available AI providers', (y) => y.usage('Usage: $0 provider list'), async () => {
456
- console.log('Available providers: openai, bedrock, vertex, anthropic, azure, google-ai, huggingface, ollama, mistral');
745
+ .command("list", "List available AI providers", (y) => y.usage("Usage: $0 provider list"), async () => {
746
+ console.log("Available providers: openai, bedrock, vertex, anthropic, azure, google-ai, huggingface, ollama, mistral");
457
747
  })
458
- .command('configure <providerName>', 'Display configuration guidance for a provider', (y) => y
459
- .usage('Usage: $0 provider configure <providerName>')
460
- .positional('providerName', {
461
- type: 'string',
462
- choices: ['openai', 'bedrock', 'vertex', 'anthropic', 'azure', 'google-ai', 'huggingface', 'ollama', 'mistral'],
463
- description: 'Name of the provider to configure',
748
+ .command("configure <providerName>", "Display configuration guidance for a provider", (y) => y
749
+ .usage("Usage: $0 provider configure <providerName>")
750
+ .positional("providerName", {
751
+ type: "string",
752
+ choices: [
753
+ "openai",
754
+ "bedrock",
755
+ "vertex",
756
+ "anthropic",
757
+ "azure",
758
+ "google-ai",
759
+ "huggingface",
760
+ "ollama",
761
+ "mistral",
762
+ ],
763
+ description: "Name of the provider to configure",
464
764
  demandOption: true,
465
765
  })
466
- .example('$0 provider configure openai', 'Show OpenAI configuration help'), async (argv) => {
766
+ .example("$0 provider configure openai", "Show OpenAI configuration help"), async (argv) => {
467
767
  console.log(chalk.blue(`\n🔧 Configuration guidance for ${chalk.bold(argv.providerName)}:`));
468
- console.log(chalk.yellow('💡 Set relevant environment variables for API keys and other settings.'));
469
- console.log(chalk.gray(' Refer to the documentation for details: https://github.com/juspay/neurolink#configuration'));
768
+ console.log(chalk.yellow("💡 Set relevant environment variables for API keys and other settings."));
769
+ console.log(chalk.gray(" Refer to the documentation for details: https://github.com/juspay/neurolink#configuration"));
470
770
  })
471
- .demandCommand(1, 'Please specify a provider subcommand (status, list, or configure).');
472
- }
473
- // Base handler for 'provider' removed.
474
- // If no subcommand is provided, yargsProvider.demandCommand should trigger an error,
475
- // which will be caught by the main .fail() handler.
476
- )
771
+ .demandCommand(1, "Please specify a provider subcommand (status, list, or configure).");
772
+ })
477
773
  // Status Command (Standalone, for backward compatibility or direct access)
478
- .command('status', 'Check AI provider connectivity and performance (alias for provider status)', (yargsInstance) => yargsInstance
479
- .usage('Usage: $0 status [options]')
480
- .option('verbose', {
481
- type: 'boolean',
482
- alias: 'v', // Default is handled by middleware if NEUROLINK_DEBUG is set
483
- description: 'Show detailed information'
484
- })
485
- .example('$0 status', 'Check all providers')
486
- .example('$0 status --verbose', 'Show detailed status information'), async (argv) => {
774
+ .command("status", "Check AI provider connectivity and performance (alias for provider status)", (yargsInstance) => yargsInstance
775
+ .usage("Usage: $0 status [options]")
776
+ .option("verbose", {
777
+ // Ensure status alias also defines verbose
778
+ type: "boolean",
779
+ alias: "v", // Default is handled by middleware if NEUROLINK_DEBUG is set
780
+ description: "Show detailed information",
781
+ })
782
+ .example("$0 status", "Check all providers")
783
+ .example("$0 status --verbose", "Show detailed status information"), async (argv) => {
487
784
  // This logic is duplicated from 'provider status' for the alias
488
785
  if (argv.verbose && !argv.quiet) {
489
- console.log(chalk.yellow('ℹ️ Verbose mode enabled. Displaying detailed status.\n')); // Added newline
786
+ console.log(chalk.yellow("ℹ️ Verbose mode enabled. Displaying detailed status.\n")); // Added newline
490
787
  }
491
- const spinner = argv.quiet ? null : ora('🔍 Checking AI provider status...\n').start();
788
+ const spinner = argv.quiet
789
+ ? null
790
+ : ora("🔍 Checking AI provider status...\n").start();
492
791
  // Middleware sets argv.verbose if NEUROLINK_DEBUG is true and --verbose is not specified
493
792
  // Removed the spinner.stopAndPersist logic from here as it's handled before spinner start
494
- const providers = ['openai', 'bedrock', 'vertex', 'anthropic', 'azure', 'google-ai', 'huggingface', 'ollama', 'mistral'];
793
+ const providers = [
794
+ "openai",
795
+ "bedrock",
796
+ "vertex",
797
+ "anthropic",
798
+ "azure",
799
+ "google-ai",
800
+ "huggingface",
801
+ "ollama",
802
+ "mistral",
803
+ ];
495
804
  const results = [];
496
805
  for (const p of providers) {
497
- if (spinner)
806
+ if (spinner) {
498
807
  spinner.text = `Testing ${p}...`;
808
+ }
499
809
  try {
500
810
  const start = Date.now();
501
- await sdk.generateText({ prompt: 'test', provider: p, maxTokens: 1 });
811
+ await sdk.generateText({ prompt: "test", provider: p, maxTokens: 1 });
502
812
  const duration = Date.now() - start;
503
- results.push({ provider: p, status: 'working', responseTime: duration });
504
- if (spinner)
505
- spinner.succeed(`${p}: ${chalk.green('✅ Working')} (${duration}ms)`);
506
- else if (!argv.quiet)
507
- console.log(`${p}: ${chalk.green('✅ Working')} (${duration}ms)`);
813
+ results.push({
814
+ provider: p,
815
+ status: "working",
816
+ responseTime: duration,
817
+ });
818
+ if (spinner) {
819
+ spinner.succeed(`${p}: ${chalk.green("✅ Working")} (${duration}ms)`);
820
+ }
821
+ else if (!argv.quiet) {
822
+ console.log(`${p}: ${chalk.green("✅ Working")} (${duration}ms)`);
823
+ }
508
824
  }
509
825
  catch (error) {
510
- results.push({ provider: p, status: 'failed', error: error.message });
511
- if (spinner)
512
- spinner.fail(`${p}: ${chalk.red('❌ Failed')} - ${error.message.split('\n')[0]}`);
513
- else if (!argv.quiet)
514
- console.error(`${p}: ${chalk.red('❌ Failed')} - ${error.message.split('\n')[0]}`);
826
+ results.push({
827
+ provider: p,
828
+ status: "failed",
829
+ error: error.message,
830
+ });
831
+ if (spinner) {
832
+ spinner.fail(`${p}: ${chalk.red("❌ Failed")} - ${error.message.split("\n")[0]}`);
833
+ }
834
+ else if (!argv.quiet) {
835
+ console.error(`${p}: ${chalk.red("❌ Failed")} - ${error.message.split("\n")[0]}`);
836
+ }
515
837
  }
516
838
  }
517
- const working = results.filter(r => r.status === 'working').length;
518
- if (spinner)
839
+ const working = results.filter((r) => r.status === "working").length;
840
+ if (spinner) {
519
841
  spinner.info(chalk.blue(`\n📊 Summary: ${working}/${results.length} providers working`));
520
- else if (!argv.quiet)
842
+ }
843
+ else if (!argv.quiet) {
521
844
  console.log(chalk.blue(`\n📊 Summary: ${working}/${results.length} providers working`));
845
+ }
522
846
  if (argv.verbose && !argv.quiet) {
523
- console.log(chalk.blue('\n📋 Detailed Results:'));
847
+ console.log(chalk.blue("\n📋 Detailed Results:"));
524
848
  console.log(JSON.stringify(results, null, 2));
525
849
  }
526
850
  })
527
851
  // Configuration Commands Refactored
528
- .command('config <subcommand>', 'Manage NeuroLink configuration', (yargsConfig) => {
852
+ .command("config <subcommand>", "Manage NeuroLink configuration", (yargsConfig) => {
529
853
  yargsConfig
530
- .usage('Usage: $0 config <subcommand> [options]') // Add usage here
531
- .command('setup', 'Interactive setup for NeuroLink configuration', () => { }, // No specific builder options for setup
532
- async (argv) => {
533
- console.log('Config setup: Use interactive prompts. Error: Invalid input, please try again with valid provider names.');
854
+ .usage("Usage: $0 config <subcommand> [options]") // Add usage here
855
+ .command("setup", "Interactive setup for NeuroLink configuration", (y) => y
856
+ .usage("Usage: $0 config setup [options]")
857
+ .option("quiet", {
858
+ type: "boolean",
859
+ alias: "q",
860
+ description: "Suppress progress messages and interactive prompts",
534
861
  })
535
- .command('init', 'Alias for setup: Interactive setup for NeuroLink configuration', () => { }, async (argv) => {
536
- console.log('Config init (setup): Use interactive prompts. Error: Invalid input, please try again with valid provider names.');
862
+ .example("$0 config setup", "Run interactive setup wizard")
863
+ .example("$0 config setup --quiet", "Run setup with minimal output"), async (argv) => {
864
+ const { configSetup } = await import("./utils/complete-setup.js");
865
+ await configSetup(argv.quiet);
537
866
  })
538
- .command('show', 'Show current NeuroLink configuration', () => { }, async (argv) => {
539
- console.log('Config show: Displaying current configuration...');
867
+ .command("init", "Alias for setup: Interactive setup for NeuroLink configuration", (y) => y
868
+ .usage("Usage: $0 config init [options]")
869
+ .option("quiet", {
870
+ type: "boolean",
871
+ alias: "q",
872
+ description: "Suppress progress messages and interactive prompts",
873
+ })
874
+ .example("$0 config init", "Run interactive setup wizard (alias for setup)")
875
+ .example("$0 config init --quiet", "Run setup with minimal output"), async (argv) => {
876
+ const { configInit } = await import("./utils/complete-setup.js");
877
+ await configInit(argv.quiet);
878
+ })
879
+ .command("show", "Show current NeuroLink configuration", () => { }, async (argv) => {
880
+ console.log("Config show: Displaying current configuration...");
540
881
  // Actual show logic here
541
882
  })
542
- .command('set <key> <value>', 'Set a configuration key-value pair', (y) => y
543
- .positional('key', { type: 'string', description: 'Configuration key to set', demandOption: true })
544
- .positional('value', { type: 'string', description: 'Value to set for the key', demandOption: true }), async (argv) => {
883
+ .command("set <key> <value>", "Set a configuration key-value pair", (y) => y
884
+ .positional("key", {
885
+ type: "string",
886
+ description: "Configuration key to set",
887
+ demandOption: true,
888
+ })
889
+ .positional("value", {
890
+ type: "string",
891
+ description: "Value to set for the key",
892
+ demandOption: true,
893
+ }), async (argv) => {
545
894
  console.log(`Config set: Key: ${argv.key}, Value: ${argv.value}`);
546
895
  // Actual set logic here
547
896
  })
548
- .command('import <file>', 'Import configuration from a file', (y) => y.positional('file', { type: 'string', description: 'File path to import from', demandOption: true }), async (argv) => {
897
+ .command("import <file>", "Import configuration from a file", (y) => y.positional("file", {
898
+ type: "string",
899
+ description: "File path to import from",
900
+ demandOption: true,
901
+ }), async (argv) => {
549
902
  console.log(`Config import: Importing from ${argv.file}`);
550
- if (argv.file.includes('invalid-config.json')) {
551
- handleError(new Error('Invalid or unparseable JSON in config file.'), 'Config import');
903
+ if (argv.file.includes("invalid-config.json")) {
904
+ handleError(new Error("Invalid or unparseable JSON in config file."), "Config import");
552
905
  }
553
906
  // Actual import logic here
554
907
  })
555
- .command('export <file>', 'Export current configuration to a file', (y) => y.positional('file', { type: 'string', description: 'File path to export to', demandOption: true }), async (argv) => {
908
+ .command("export <file>", "Export current configuration to a file", (y) => y.positional("file", {
909
+ type: "string",
910
+ description: "File path to export to",
911
+ demandOption: true,
912
+ }), async (argv) => {
556
913
  console.log(`Config export: Exporting to ${argv.file}`);
557
- if (argv.file.includes('read-only-dir')) {
558
- handleError(new Error('Permission denied. Cannot write to read-only directory.'), 'Config export');
914
+ if (argv.file.includes("read-only-dir")) {
915
+ handleError(new Error("Permission denied. Cannot write to read-only directory."), "Config export");
559
916
  }
560
917
  // Actual export logic here
561
918
  })
562
- .command('validate', 'Validate the current configuration', () => { }, async (argv) => {
563
- console.log('Config validate: Validating configuration...');
919
+ .command("validate", "Validate the current configuration", () => { }, async (argv) => {
920
+ console.log("Config validate: Validating configuration...");
564
921
  // Actual validation logic here
565
922
  })
566
- .command('reset', 'Reset NeuroLink configuration to defaults', () => { }, async (argv) => {
567
- console.log('Config reset: Resetting configuration...');
923
+ .command("reset", "Reset NeuroLink configuration to defaults", () => { }, async (argv) => {
924
+ console.log("Config reset: Resetting configuration...");
568
925
  // Actual reset logic here
569
926
  })
570
- .demandCommand(1, 'Please specify a config subcommand (e.g., setup, show, set).')
571
- .example('$0 config setup', 'Run interactive setup')
572
- .example('$0 config set provider openai', 'Set default provider (using key/value)');
573
- }
574
- // Base handler for 'config' removed.
575
- // If no subcommand is provided, yargsConfig.demandCommand should trigger an error,
576
- // which will be caught by the main .fail() handler.
577
- )
927
+ .demandCommand(1, "Please specify a config subcommand (e.g., setup, show, set).")
928
+ .example("$0 config setup", "Run interactive setup")
929
+ .example("$0 config set provider openai", "Set default provider (using key/value)");
930
+ })
578
931
  // Get Best Provider Command
579
- .command('get-best-provider', 'Show the best available AI provider', (yargsInstance) => yargsInstance
580
- .usage('Usage: $0 get-best-provider [options]')
581
- .option('debug', { type: 'boolean', default: false, description: 'Enable debug mode with selection reasoning' })
582
- .example('$0 get-best-provider', 'Get best provider')
583
- .example('$0 get-best-provider --debug', 'Show selection logic'), async (argv) => {
584
- const spinner = argv.quiet ? null : ora('🎯 Finding best provider...').start();
932
+ .command("get-best-provider", "Show the best available AI provider", (yargsInstance) => yargsInstance
933
+ .usage("Usage: $0 get-best-provider [options]")
934
+ .option("debug", {
935
+ type: "boolean",
936
+ default: false,
937
+ description: "Enable debug mode with selection reasoning",
938
+ })
939
+ .example("$0 get-best-provider", "Get best provider")
940
+ .example("$0 get-best-provider --debug", "Show selection logic"), async (argv) => {
941
+ const spinner = argv.quiet
942
+ ? null
943
+ : ora("🎯 Finding best provider...").start();
585
944
  try {
586
945
  const provider = await sdk.getBestProvider();
587
946
  if (spinner) {
@@ -589,7 +948,7 @@ const cli = yargs(args)
589
948
  spinner.succeed(chalk.green(`✅ Best provider selected: ${provider}`));
590
949
  }
591
950
  else {
592
- spinner.succeed(chalk.green('✅ Provider found'));
951
+ spinner.succeed(chalk.green("✅ Provider found"));
593
952
  }
594
953
  }
595
954
  if (argv.debug) {
@@ -603,16 +962,19 @@ const cli = yargs(args)
603
962
  }
604
963
  }
605
964
  catch (error) {
606
- if (spinner && spinner.isSpinning)
965
+ if (spinner && spinner.isSpinning) {
607
966
  spinner.fail();
608
- handleError(error, 'Provider selection');
967
+ }
968
+ handleError(error, "Provider selection");
609
969
  }
610
970
  })
611
- .completion('completion', 'Generate shell completion script');
971
+ .completion("completion", "Generate shell completion script");
612
972
  // Add MCP commands
613
973
  addMCPCommands(cli);
614
974
  // Add Ollama command
615
975
  cli.command(ollamaCommand);
976
+ // Add Agent-Generate command
977
+ cli.command(agentGenerateCommand);
616
978
  // Use an async IIFE to allow top-level await for parseAsync
617
979
  (async () => {
618
980
  try {