snow-flow 8.5.7 → 8.5.8

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 (56) hide show
  1. package/CLAUDE.md +52 -4
  2. package/README.md +99 -30
  3. package/{OPENCODE-SETUP.md → SNOWCODE-SETUP.md} +47 -21
  4. package/{OPENCODE-TROUBLESHOOTING.md → SNOWCODE-TROUBLESHOOTING.md} +18 -18
  5. package/dist/cli/auth.d.ts.map +1 -1
  6. package/dist/cli/auth.js +526 -280
  7. package/dist/cli/auth.js.map +1 -1
  8. package/dist/cli.d.ts +1 -1
  9. package/dist/cli.d.ts.map +1 -1
  10. package/dist/cli.js +528 -250
  11. package/dist/cli.js.map +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.js +1 -1
  14. package/dist/templates/claude-md-template.d.ts +1 -1
  15. package/dist/templates/claude-md-template.js +1 -1
  16. package/dist/templates/readme-template.d.ts +1 -1
  17. package/dist/templates/readme-template.js +18 -18
  18. package/dist/utils/artifact-local-sync.d.ts +1 -1
  19. package/dist/utils/artifact-local-sync.js +4 -4
  20. package/dist/utils/snow-oauth.d.ts +11 -5
  21. package/dist/utils/snow-oauth.d.ts.map +1 -1
  22. package/dist/utils/snow-oauth.js +337 -90
  23. package/dist/utils/snow-oauth.js.map +1 -1
  24. package/dist/utils/{opencode-output-interceptor.d.ts → snowcode-output-interceptor.d.ts} +7 -7
  25. package/dist/utils/{opencode-output-interceptor.d.ts.map → snowcode-output-interceptor.d.ts.map} +1 -1
  26. package/dist/utils/{opencode-output-interceptor.js → snowcode-output-interceptor.js} +10 -10
  27. package/dist/utils/{opencode-output-interceptor.js.map → snowcode-output-interceptor.js.map} +1 -1
  28. package/package.json +20 -9
  29. package/scripts/{start-opencode.sh → start-snowcode.sh} +28 -28
  30. package/scripts/verify-snowcode-fork.sh +141 -0
  31. package/templates/snowcode-package.json +5 -0
  32. package/THEMES.md +0 -223
  33. package/bin/opencode +0 -17
  34. package/dist/mcp/servicenow-mcp-unified/config/tool-definitions.json +0 -3935
  35. package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_automation_discover.js +0 -164
  36. package/dist/mcp/servicenow-mcp-unified/tools/deployment/snow_artifact_transfer.js +0 -282
  37. package/dist/mcp/servicenow-mcp-unified/tools/filters/snow_build_filter.js +0 -171
  38. package/dist/mcp/servicenow-mcp-unified/tools/formatters/snow_format_value.js +0 -164
  39. package/dist/mcp/servicenow-mcp-unified/tools/knowledge/index.js.bak +0 -45
  40. package/dist/mcp/servicenow-mcp-unified/tools/local-sync/snow_artifact_sync.js +0 -172
  41. package/dist/mcp/servicenow-mcp-unified/tools/system-properties/index.js +0 -36
  42. package/dist/mcp/servicenow-mcp-unified/tools/ui-builder/snow_discover_uib.js +0 -296
  43. package/dist/mcp/servicenow-mcp-unified/tools/workspace/snow_create_ux_component.js +0 -292
  44. package/dist/memory/session-memory.d.ts +0 -80
  45. package/dist/memory/session-memory.d.ts.map +0 -1
  46. package/dist/memory/session-memory.js +0 -468
  47. package/dist/memory/session-memory.js.map +0 -1
  48. package/dist/templates/opencode-agents-template.d.ts +0 -2
  49. package/dist/templates/opencode-agents-template.d.ts.map +0 -1
  50. package/dist/templates/opencode-agents-template.js +0 -469
  51. package/dist/templates/opencode-agents-template.js.map +0 -1
  52. package/scripts/bulk-optimize-tools.js +0 -486
  53. package/scripts/optimize-mcp-tools.ts +0 -410
  54. package/themes/README.md +0 -83
  55. package/themes/servicenow.json +0 -117
  56. /package/{opencode-config.example.json → snowcode-config.example.json} +0 -0
package/dist/cli/auth.js CHANGED
@@ -32,12 +32,9 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
35
  Object.defineProperty(exports, "__esModule", { value: true });
39
36
  exports.registerAuthCommands = registerAuthCommands;
40
- const chalk_1 = __importDefault(require("chalk"));
37
+ const prompts = __importStar(require("@clack/prompts"));
41
38
  const snow_oauth_js_1 = require("../utils/snow-oauth.js");
42
39
  const servicenow_client_js_1 = require("../utils/servicenow-client.js");
43
40
  const logger_js_1 = require("../utils/logger.js");
@@ -51,44 +48,44 @@ function registerAuthCommands(program) {
51
48
  .option('-p, --provider <provider>', 'Provider to list models for (anthropic, openai, google, ollama)')
52
49
  .action(async (options) => {
53
50
  const { getAllProviderModels, getProviderModels } = await Promise.resolve().then(() => __importStar(require('../utils/dynamic-models.js')));
54
- console.log(chalk_1.default.blue('\n🤖 Available LLM Models\n'));
51
+ prompts.log.step('Available LLM Models');
55
52
  if (options.provider) {
56
53
  // List models for specific provider
57
- console.log(chalk_1.default.cyan(`${options.provider.toUpperCase()}:\n`));
54
+ prompts.log.info(`${options.provider.toUpperCase()}:`);
58
55
  const models = await getProviderModels(options.provider);
59
56
  if (models.length > 0) {
60
57
  models.forEach((model, i) => {
61
- console.log(` ${i + 1}. ${chalk_1.default.white(model.name)}`);
62
- console.log(` ${chalk_1.default.dim('ID:')} ${chalk_1.default.yellow(model.value)}`);
58
+ prompts.log.message(` ${i + 1}. ${model.name}`);
59
+ prompts.log.message(` ID: ${model.value}`);
63
60
  if (model.contextWindow) {
64
- console.log(` ${chalk_1.default.dim('Context:')} ${chalk_1.default.green(model.contextWindow.toLocaleString() + ' tokens')}`);
61
+ prompts.log.message(` Context: ${model.contextWindow.toLocaleString()} tokens`);
65
62
  }
66
63
  console.log();
67
64
  });
68
65
  }
69
66
  else {
70
- console.log(chalk_1.default.yellow(' No models available for this provider\n'));
67
+ prompts.log.warn(' No models available for this provider');
71
68
  }
72
69
  }
73
70
  else {
74
71
  // List all providers
75
72
  const allModels = await getAllProviderModels();
76
73
  for (const [provider, models] of Object.entries(allModels)) {
77
- console.log(chalk_1.default.cyan(`${provider.toUpperCase()}:\n`));
74
+ prompts.log.info(`${provider.toUpperCase()}:`);
78
75
  if (models.length > 0) {
79
76
  models.forEach((model, i) => {
80
- console.log(` ${i + 1}. ${chalk_1.default.white(model.name)}`);
81
- console.log(` ${chalk_1.default.dim('ID:')} ${chalk_1.default.yellow(model.value)}`);
77
+ prompts.log.message(` ${i + 1}. ${model.name}`);
78
+ prompts.log.message(` ID: ${model.value}`);
82
79
  console.log();
83
80
  });
84
81
  }
85
82
  else {
86
- console.log(chalk_1.default.yellow(' No models available\n'));
83
+ prompts.log.warn(' No models available');
87
84
  }
88
85
  }
89
86
  }
90
- console.log(chalk_1.default.dim('💡 Tip: Use --provider to see models for a specific provider'));
91
- console.log(chalk_1.default.dim('Example: snow-flow auth models --provider anthropic\n'));
87
+ prompts.log.message('Tip: Use --provider to see models for a specific provider');
88
+ prompts.log.message('Example: snow-flow auth models --provider anthropic');
92
89
  });
93
90
  auth
94
91
  .command('login')
@@ -99,49 +96,46 @@ function registerAuthCommands(program) {
99
96
  const path = require('path');
100
97
  const inquirer = require('inquirer');
101
98
  console.log(); // Empty line for spacing
102
- // Step 1: LLM Provider Authentication (OpenCode-style)
103
- console.log(chalk_1.default.blue('╭────────────────────────────────────────────╮'));
104
- console.log(chalk_1.default.blue('│ Add LLM credential │'));
105
- console.log(chalk_1.default.blue('╰────────────────────────────────────────────╯\n'));
99
+ // Step 0: Check if we need LLM authentication
106
100
  let provider = process.env.DEFAULT_LLM_PROVIDER;
107
- // Check if ANY provider API key is already configured
101
+ // Check if ANY provider API key is configured
108
102
  const hasApiKey = (process.env.ANTHROPIC_API_KEY && process.env.ANTHROPIC_API_KEY.trim() !== '') ||
109
103
  (process.env.OPENAI_API_KEY && process.env.OPENAI_API_KEY.trim() !== '') ||
110
104
  (process.env.GOOGLE_API_KEY && process.env.GOOGLE_API_KEY.trim() !== '') ||
111
105
  (process.env.GROQ_API_KEY && process.env.GROQ_API_KEY.trim() !== '') ||
112
106
  (process.env.MISTRAL_API_KEY && process.env.MISTRAL_API_KEY.trim() !== '');
113
- // Simplified flow - always ask for provider and API key
107
+ // Only do SnowCode auth if no API key is configured
114
108
  if (!hasApiKey) {
115
- // Check if opencode is installed
109
+ // Check if snowcode is installed
116
110
  try {
117
- execSync('which opencode', { stdio: 'ignore' });
111
+ execSync('which snowcode', { stdio: 'ignore' });
118
112
  }
119
113
  catch {
120
- console.error(chalk_1.default.red(' OpenCode is not installed'));
121
- console.log(chalk_1.default.yellow('Please install OpenCode first: ') + chalk_1.default.cyan('npm install -g opencode-ai'));
122
- console.log(chalk_1.default.blue('Or configure an API key in .env: ') + chalk_1.default.cyan('ANTHROPIC_API_KEY=your-key'));
114
+ prompts.log.error('SnowCode is not installed');
115
+ prompts.log.warn('Please install SnowCode first: npm install -g @groeimetai/snowcode');
116
+ prompts.log.info('Or configure an API key in .env: ANTHROPIC_API_KEY=your-key');
123
117
  return;
124
118
  }
125
- console.log(chalk_1.default.blue('🔐 Authenticating with Anthropic...'));
126
- // Fix common OpenCode directory issue (agents vs agent) in ALL possible directories
119
+ prompts.intro('Starting authentication');
120
+ // Fix common SnowCode directory issue (agents vs agent) in ALL possible directories
127
121
  try {
128
122
  const fs = require('fs');
129
123
  const path = require('path');
130
124
  const directoriesToFix = [
131
- // Fix 1: Global ~/.opencode directory
132
- process.env.HOME + '/.opencode',
125
+ // Fix 1: Global ~/.snowcode directory
126
+ process.env.HOME + '/.snowcode',
133
127
  // Fix 2: Current working directory
134
- path.join(process.cwd(), '.opencode'),
128
+ path.join(process.cwd(), '.snowcode'),
135
129
  // Fix 3: Parent directory (in case we're in a subdirectory)
136
- path.join(process.cwd(), '..', '.opencode'),
130
+ path.join(process.cwd(), '..', '.snowcode'),
137
131
  // Fix 4: Snow-flow package directory (for development)
138
- path.join(__dirname, '..', '..', '.opencode')
132
+ path.join(__dirname, '..', '..', '.snowcode')
139
133
  ];
140
- for (const opencodeDir of directoriesToFix) {
141
- const agentsDir = path.join(opencodeDir, 'agents');
142
- const agentDir = path.join(opencodeDir, 'agent');
134
+ for (const snowcodeDir of directoriesToFix) {
135
+ const agentsDir = path.join(snowcodeDir, 'agents');
136
+ const agentDir = path.join(snowcodeDir, 'agent');
143
137
  if (fs.existsSync(agentsDir) && !fs.existsSync(agentDir)) {
144
- console.log(chalk_1.default.dim(` Fixing OpenCode directory structure in ${opencodeDir}...`));
138
+ prompts.log.message(` Fixing SnowCode directory structure in ${snowcodeDir}...`);
145
139
  try {
146
140
  fs.renameSync(agentsDir, agentDir);
147
141
  }
@@ -152,265 +146,514 @@ function registerAuthCommands(program) {
152
146
  }
153
147
  }
154
148
  catch (dirError) {
155
- // Ignore directory fix errors - OpenCode will handle it
156
- console.log(chalk_1.default.dim(' (Directory fix skipped - will auto-correct)'));
149
+ // Ignore directory fix errors - SnowCode will handle it
150
+ prompts.log.message(' (Directory fix skipped - will auto-correct)');
157
151
  }
158
152
  try {
159
- // Run opencode auth login interactively
160
- execSync('opencode auth login', { stdio: 'inherit' });
161
- console.log(chalk_1.default.green('✅ LLM authentication completed\n'));
162
- // Ask which provider they used and save it
163
- if (!provider || provider.trim() === '') {
164
- const { selectedProvider } = await inquirer.prompt([{
165
- type: 'list',
166
- name: 'selectedProvider',
167
- message: 'Which provider did you just authenticate with?',
168
- choices: [
169
- { name: 'Anthropic (Claude)', value: 'anthropic' },
170
- { name: 'OpenAI (GPT)', value: 'openai' },
171
- { name: 'Google (Gemini)', value: 'google' },
172
- { name: 'Ollama (Local)', value: 'ollama' },
173
- { name: 'Other', value: 'other' }
174
- ]
175
- }]);
176
- if (selectedProvider !== 'other') {
177
- // Dynamically fetch latest models from provider APIs
178
- const { getProviderModels } = await Promise.resolve().then(() => __importStar(require('../utils/dynamic-models.js')));
179
- console.log(chalk_1.default.dim(` Fetching latest ${selectedProvider} models...`));
180
- const models = await getProviderModels(selectedProvider);
181
- // Ask for preferred model
182
- let selectedModel = '';
183
- if (models && models.length > 0) {
184
- const choices = models.map(m => ({
185
- name: m.name,
186
- value: m.value
187
- }));
188
- const { chosenModel } = await inquirer.prompt([{
189
- type: 'list',
190
- name: 'chosenModel',
191
- message: `Which ${selectedProvider} model do you want to use by default?`,
192
- choices: choices
193
- }]);
194
- selectedModel = chosenModel;
195
- }
196
- else {
197
- console.log(chalk_1.default.yellow(` ⚠️ Could not fetch models for ${selectedProvider}, skipping model selection`));
198
- }
199
- // Save provider AND model to .env file
200
- const envPath = path.join(process.cwd(), '.env');
201
- let envContent = '';
202
- try {
203
- envContent = fs.readFileSync(envPath, 'utf8');
204
- }
205
- catch {
206
- // .env doesn't exist yet, use .env.example as template
207
- const examplePath = path.join(process.cwd(), '.env.example');
208
- if (fs.existsSync(examplePath)) {
209
- envContent = fs.readFileSync(examplePath, 'utf8');
210
- }
211
- }
212
- // Update DEFAULT_LLM_PROVIDER in .env content
213
- if (envContent.includes('DEFAULT_LLM_PROVIDER=')) {
214
- envContent = envContent.replace(/DEFAULT_LLM_PROVIDER=.*/g, `DEFAULT_LLM_PROVIDER=${selectedProvider}`);
215
- }
216
- else {
217
- envContent += `\nDEFAULT_LLM_PROVIDER=${selectedProvider}\n`;
218
- }
219
- // Update DEFAULT_MODEL in .env content
220
- if (selectedModel) {
221
- if (envContent.includes('DEFAULT_MODEL=')) {
222
- envContent = envContent.replace(/DEFAULT_MODEL=.*/g, `DEFAULT_MODEL=${selectedModel}`);
223
- }
224
- else {
225
- envContent += `DEFAULT_MODEL=${selectedModel}\n`;
226
- }
227
- }
228
- fs.writeFileSync(envPath, envContent);
229
- console.log(chalk_1.default.green(`✅ Provider saved: ${selectedProvider}`));
230
- if (selectedModel) {
231
- console.log(chalk_1.default.green(`✅ Default model saved: ${selectedModel}\n`));
232
- }
233
- else {
234
- console.log();
235
- }
236
- provider = selectedProvider;
237
- process.env.DEFAULT_LLM_PROVIDER = provider;
238
- if (selectedModel) {
239
- process.env.DEFAULT_MODEL = selectedModel;
240
- }
241
- }
153
+ // Run SnowCode auth login - prefer local installation (has platform binaries)
154
+ const localSnowCode = path.join(process.cwd(), 'node_modules', '@groeimetai', 'snowcode', 'bin', 'snowcode');
155
+ let snowcodeCommand = 'snowcode'; // fallback to global
156
+ if (fs.existsSync(localSnowCode)) {
157
+ prompts.log.message(' Using local SnowCode installation (with platform binaries)');
158
+ snowcodeCommand = `"${localSnowCode}"`;
159
+ }
160
+ else {
161
+ prompts.log.message(' Using global SnowCode installation');
242
162
  }
163
+ execSync(`${snowcodeCommand} auth login`, { stdio: 'inherit' });
243
164
  }
244
165
  catch (error) {
245
- console.error(chalk_1.default.red('\n❌ LLM authentication failed'));
246
- // Check if it's the known OpenCode directory bug
166
+ prompts.log.error('Authentication failed');
167
+ // Check if it's the bun dependency error
247
168
  const errorMsg = error?.message || error?.toString() || '';
248
- if (errorMsg.includes('agents') && errorMsg.includes('agent')) {
249
- console.log(chalk_1.default.yellow('\n⚠️ OpenCode directory issue detected'));
250
- console.log(chalk_1.default.blue(' Run this fix: ') + chalk_1.default.cyan('mv ~/.opencode/agents ~/.opencode/agent'));
251
- console.log(chalk_1.default.blue(' Then try: ') + chalk_1.default.cyan('snow-flow auth login'));
169
+ if (errorMsg.includes('Cannot find package \'bun\'') || errorMsg.includes('ERR_MODULE_NOT_FOUND')) {
170
+ prompts.log.warn('SnowCode dependency issue detected');
171
+ prompts.log.message(' This is a known issue with SnowCode versions 0.15.18 and earlier');
172
+ prompts.log.message(' Please update SnowCode to the latest version:');
173
+ prompts.log.message(' npm update -g @groeimetai/snowcode');
174
+ prompts.log.message(' Alternatively, use an API key instead:');
175
+ prompts.log.message(' 1. Add to .env: ANTHROPIC_API_KEY=your-api-key');
176
+ prompts.log.message(' 2. Then run: snow-flow auth login');
177
+ }
178
+ else if (errorMsg.includes('agents') && errorMsg.includes('agent')) {
179
+ prompts.log.warn('SnowCode directory issue detected');
180
+ prompts.log.message(' Run this fix: mv ~/.snowcode/agents ~/.snowcode/agent');
181
+ prompts.log.message(' Then try: snow-flow auth login');
252
182
  }
253
183
  else {
254
- console.log(chalk_1.default.yellow('💡 You can try again later or use an API key instead'));
255
- console.log(chalk_1.default.blue(' Add to .env: ') + chalk_1.default.cyan('ANTHROPIC_API_KEY=your-api-key'));
184
+ prompts.log.warn('You can try again later or use an API key instead');
185
+ prompts.log.message(' Add to .env: ANTHROPIC_API_KEY=your-api-key');
256
186
  }
257
187
  return;
258
188
  }
259
189
  }
260
- // Step 2: ServiceNow OAuth authentication
261
- console.log(chalk_1.default.blue('🔐 Authenticating with ServiceNow...'));
262
- const oauth = new snow_oauth_js_1.ServiceNowOAuth();
190
+ // ServiceNow setup - continue the flow (maintain @clack/prompts styling)
263
191
  // Read credentials from .env file
264
192
  let instance = process.env.SNOW_INSTANCE;
265
- let clientId = process.env.SNOW_CLIENT_ID;
266
- let clientSecret = process.env.SNOW_CLIENT_SECRET;
267
- // Validate credentials (check if they exist AND are valid)
268
- const credentialsValid = instance && instance.trim() !== '' && instance.includes('.service-now.com') &&
269
- clientId && clientId.trim() !== '' && clientId.length >= 32 &&
270
- clientSecret && clientSecret.trim() !== '' && clientSecret.length >= 32;
271
- // If credentials are missing or invalid, ask user interactively
272
- if (!credentialsValid) {
273
- if (instance || clientId || clientSecret) {
274
- console.log(chalk_1.default.yellow('\n⚠️ Invalid ServiceNow OAuth credentials detected in .env'));
275
- if (instance && !instance.includes('.service-now.com')) {
276
- console.log(chalk_1.default.red(' ❌ Instance URL must be a .service-now.com domain'));
277
- }
278
- if (clientId && clientId.length < 32) {
279
- console.log(chalk_1.default.red(' ❌ Client ID too short (expected 32+ characters)'));
280
- }
281
- if (clientSecret && clientSecret.length < 32) {
282
- console.log(chalk_1.default.red(' ❌ Client Secret too short (expected 32+ characters)'));
283
- }
193
+ let authMethod = process.env.SNOW_AUTH_METHOD;
194
+ // Check if we have complete credentials for either method
195
+ const hasOAuthCreds = process.env.SNOW_CLIENT_ID && process.env.SNOW_CLIENT_SECRET;
196
+ const hasBasicCreds = process.env.SNOW_USERNAME && process.env.SNOW_PASSWORD;
197
+ const hasCompleteCredentials = instance && instance.includes('.service-now.com') &&
198
+ ((authMethod === 'oauth' && hasOAuthCreds) || (authMethod === 'basic' && hasBasicCreds));
199
+ // If no complete credentials, ask for auth method FIRST
200
+ if (!hasCompleteCredentials) {
201
+ const method = await prompts.select({
202
+ message: 'Choose authentication method',
203
+ options: [
204
+ { value: 'oauth', label: 'OAuth 2.0', hint: 'recommended' },
205
+ { value: 'basic', label: 'Basic Auth', hint: 'username/password' }
206
+ ]
207
+ });
208
+ if (prompts.isCancel(method)) {
209
+ prompts.cancel('Setup cancelled');
210
+ process.exit(0);
284
211
  }
285
- else {
286
- console.log(chalk_1.default.yellow('\n⚠️ ServiceNow OAuth credentials not found in .env'));
287
- }
288
- console.log(chalk_1.default.dim(' You need to set up OAuth in ServiceNow first\n'));
289
- const { setupNow } = await inquirer.prompt([{
290
- type: 'confirm',
291
- name: 'setupNow',
292
- message: 'Do you want to enter your ServiceNow OAuth credentials now?',
293
- default: true
294
- }]);
295
- if (!setupNow) {
296
- console.log(chalk_1.default.yellow('\n💡 To set up OAuth credentials manually:'));
297
- console.log(' 1. Log into ServiceNow as admin');
298
- console.log(' 2. Navigate to: System OAuth > Application Registry');
299
- console.log(' 3. Create a new OAuth application');
300
- console.log(' 4. Add these to your .env file:');
301
- console.log(' SNOW_INSTANCE=your-instance.service-now.com');
302
- console.log(' SNOW_CLIENT_ID=your-client-id');
303
- console.log(' SNOW_CLIENT_SECRET=your-client-secret');
304
- console.log('\n Then run: snow-flow auth login');
305
- return;
212
+ authMethod = method;
213
+ // Ask for ServiceNow instance (common for both methods)
214
+ instance = await prompts.text({
215
+ message: 'ServiceNow instance',
216
+ placeholder: 'dev12345.service-now.com',
217
+ defaultValue: instance || '',
218
+ validate: (value) => {
219
+ if (!value || value.trim() === '')
220
+ return 'Instance URL is required';
221
+ const cleaned = value.replace(/^https?:\/\//, '').replace(/\/$/, '');
222
+ if (!cleaned.includes('.service-now.com')) {
223
+ return 'Must be a ServiceNow domain (e.g., dev12345.service-now.com)';
224
+ }
225
+ }
226
+ });
227
+ if (prompts.isCancel(instance)) {
228
+ prompts.cancel('Setup cancelled');
229
+ process.exit(0);
306
230
  }
307
- console.log(chalk_1.default.blue('\n📋 ServiceNow OAuth Setup'));
308
- console.log(chalk_1.default.dim(' Need help? See: https://docs.servicenow.com/oauth\n'));
309
- const credentials = await inquirer.prompt([
310
- {
311
- type: 'input',
312
- name: 'instance',
313
- message: 'ServiceNow instance (e.g., dev12345.service-now.com):',
314
- default: instance,
315
- validate: (input) => {
316
- if (!input || input.trim() === '') {
317
- return 'Instance URL is required';
318
- }
319
- // Remove https:// and trailing slash if present
320
- const cleaned = input.replace(/^https?:\/\//, '').replace(/\/$/, '');
321
- if (!cleaned.includes('.service-now.com')) {
322
- return 'Instance must be a ServiceNow domain (e.g., dev12345.service-now.com)';
323
- }
324
- return true;
325
- },
326
- filter: (input) => input.replace(/^https?:\/\//, '').replace(/\/$/, '')
327
- },
328
- {
329
- type: 'input',
330
- name: 'clientId',
331
- message: 'OAuth Client ID:',
332
- default: clientId,
333
- validate: (input) => {
334
- if (!input || input.trim() === '') {
231
+ instance = instance.replace(/^https?:\/\//, '').replace(/\/$/, '');
232
+ }
233
+ // OAuth 2.0 Flow
234
+ if (authMethod === 'oauth') {
235
+ const oauth = new snow_oauth_js_1.ServiceNowOAuth();
236
+ let clientId = process.env.SNOW_CLIENT_ID;
237
+ let clientSecret = process.env.SNOW_CLIENT_SECRET;
238
+ // Check if OAuth credentials need to be collected
239
+ const needsCredentials = !clientId || !clientSecret || clientId.length < 32 || clientSecret.length < 32;
240
+ // If credentials are missing, prompt for them
241
+ if (needsCredentials) {
242
+ // OAuth Client ID
243
+ clientId = await prompts.text({
244
+ message: 'OAuth Client ID',
245
+ placeholder: '32-character hex string from ServiceNow',
246
+ defaultValue: clientId || '',
247
+ validate: (value) => {
248
+ if (!value || value.trim() === '')
335
249
  return 'Client ID is required';
336
- }
337
- if (input.length < 32) {
338
- return 'Client ID seems too short. Expected a 32-character hex string from ServiceNow';
339
- }
340
- return true;
250
+ if (value.length < 32)
251
+ return 'Client ID too short (expected 32+ characters)';
341
252
  }
342
- },
343
- {
344
- type: 'password',
345
- name: 'clientSecret',
346
- message: 'OAuth Client Secret:',
347
- mask: '*',
348
- validate: (input) => {
349
- if (!input || input.trim() === '') {
253
+ });
254
+ if (prompts.isCancel(clientId)) {
255
+ prompts.cancel('Setup cancelled');
256
+ process.exit(0);
257
+ }
258
+ // OAuth Client Secret
259
+ clientSecret = await prompts.password({
260
+ message: 'OAuth Client Secret',
261
+ validate: (value) => {
262
+ if (!value || value.trim() === '')
350
263
  return 'Client Secret is required';
351
- }
352
- if (input.length < 32) {
353
- return 'Client Secret too short. Expected 32+ character random string from ServiceNow';
354
- }
355
- return true;
264
+ if (value.length < 32)
265
+ return 'Client Secret too short (expected 32+ characters)';
356
266
  }
267
+ });
268
+ if (prompts.isCancel(clientSecret)) {
269
+ prompts.cancel('Setup cancelled');
270
+ process.exit(0);
357
271
  }
358
- ]);
359
- instance = credentials.instance;
360
- clientId = credentials.clientId;
361
- clientSecret = credentials.clientSecret;
362
- // Save to .env file
363
- const envPath = path.join(process.cwd(), '.env');
364
- let envContent = '';
365
- try {
366
- envContent = fs.readFileSync(envPath, 'utf8');
367
- }
368
- catch {
369
- // .env doesn't exist yet, use .env.example as template
370
- const examplePath = path.join(process.cwd(), '.env.example');
371
- if (fs.existsSync(examplePath)) {
372
- envContent = fs.readFileSync(examplePath, 'utf8');
272
+ // Save to .env file
273
+ const envPath = path.join(process.cwd(), '.env');
274
+ let envContent = '';
275
+ try {
276
+ envContent = fs.readFileSync(envPath, 'utf8');
277
+ }
278
+ catch {
279
+ const examplePath = path.join(process.cwd(), '.env.example');
280
+ if (fs.existsSync(examplePath)) {
281
+ envContent = fs.readFileSync(examplePath, 'utf8');
282
+ }
283
+ }
284
+ // Update credentials
285
+ const updates = [
286
+ { key: 'SNOW_INSTANCE', value: instance },
287
+ { key: 'SNOW_AUTH_METHOD', value: 'oauth' },
288
+ { key: 'SNOW_CLIENT_ID', value: clientId },
289
+ { key: 'SNOW_CLIENT_SECRET', value: clientSecret }
290
+ ];
291
+ for (const { key, value } of updates) {
292
+ if (envContent.includes(`${key}=`)) {
293
+ envContent = envContent.replace(new RegExp(`${key}=.*`, 'g'), `${key}=${value}`);
294
+ }
295
+ else {
296
+ envContent += `\n${key}=${value}\n`;
297
+ }
373
298
  }
299
+ fs.writeFileSync(envPath, envContent);
300
+ process.env.SNOW_INSTANCE = instance;
301
+ process.env.SNOW_AUTH_METHOD = 'oauth';
302
+ process.env.SNOW_CLIENT_ID = clientId;
303
+ process.env.SNOW_CLIENT_SECRET = clientSecret;
374
304
  }
375
- // Update credentials in .env content
376
- const updates = [
377
- { key: 'SNOW_INSTANCE', value: instance },
378
- { key: 'SNOW_CLIENT_ID', value: clientId },
379
- { key: 'SNOW_CLIENT_SECRET', value: clientSecret }
380
- ];
381
- for (const { key, value } of updates) {
382
- if (envContent.includes(`${key}=`)) {
383
- envContent = envContent.replace(new RegExp(`${key}=.*`, 'g'), `${key}=${value}`);
305
+ // Start OAuth flow with localhost callback server
306
+ const result = await oauth.authenticate(instance, clientId, clientSecret);
307
+ if (result.success) {
308
+ prompts.log.success('ServiceNow authentication successful');
309
+ // Test connection
310
+ const client = new servicenow_client_js_1.ServiceNowClient();
311
+ const testResult = await client.testConnection();
312
+ if (testResult.success) {
313
+ prompts.log.success(`Logged in as: ${testResult.data.name} (${testResult.data.user_name})`);
384
314
  }
385
- else {
386
- envContent += `\n${key}=${value}\n`;
315
+ // 🔧 Auto-refresh MCP configuration with new credentials
316
+ try {
317
+ const { setupMCPConfig } = await Promise.resolve().then(() => __importStar(require('../cli.js')));
318
+ const spinner2 = prompts.spinner();
319
+ spinner2.start('Updating MCP configuration');
320
+ await setupMCPConfig(process.cwd(), instance, clientId, clientSecret, true);
321
+ spinner2.stop('MCP servers ready for SnowCode/Claude Code');
322
+ }
323
+ catch (error) {
324
+ prompts.log.warn('Could not update MCP config - run "snow-flow init" to set up');
387
325
  }
326
+ prompts.outro('Setup complete!');
327
+ }
328
+ else {
329
+ prompts.cancel(result.error || 'Unknown error');
330
+ process.exit(1);
388
331
  }
389
- fs.writeFileSync(envPath, envContent);
390
- console.log(chalk_1.default.green('\n✅ Credentials saved to .env file\n'));
391
- // Reload environment variables
392
- process.env.SNOW_INSTANCE = instance;
393
- process.env.SNOW_CLIENT_ID = clientId;
394
- process.env.SNOW_CLIENT_SECRET = clientSecret;
395
332
  }
396
- // Start OAuth flow (this opens browser automatically)
397
- const result = await oauth.authenticate(instance, clientId, clientSecret);
398
- if (result.success) {
399
- console.log(chalk_1.default.green('✅ ServiceNow authentication successful!'));
400
- // Test connection
333
+ // Basic Auth Flow
334
+ else if (authMethod === 'basic') {
335
+ let username = process.env.SNOW_USERNAME;
336
+ let password = process.env.SNOW_PASSWORD;
337
+ // Check if Basic Auth credentials need to be collected
338
+ const needsCredentials = !username || !password || username.trim() === '' || password.trim() === '';
339
+ if (needsCredentials) {
340
+ // Username
341
+ username = await prompts.text({
342
+ message: 'ServiceNow username',
343
+ placeholder: 'admin',
344
+ defaultValue: username || '',
345
+ validate: (value) => {
346
+ if (!value || value.trim() === '')
347
+ return 'Username is required';
348
+ }
349
+ });
350
+ if (prompts.isCancel(username)) {
351
+ prompts.cancel('Setup cancelled');
352
+ process.exit(0);
353
+ }
354
+ // Password
355
+ password = await prompts.password({
356
+ message: 'ServiceNow password',
357
+ validate: (value) => {
358
+ if (!value || value.trim() === '')
359
+ return 'Password is required';
360
+ }
361
+ });
362
+ if (prompts.isCancel(password)) {
363
+ prompts.cancel('Setup cancelled');
364
+ process.exit(0);
365
+ }
366
+ // Save to .env file
367
+ const envPath = path.join(process.cwd(), '.env');
368
+ let envContent = '';
369
+ try {
370
+ envContent = fs.readFileSync(envPath, 'utf8');
371
+ }
372
+ catch {
373
+ const examplePath = path.join(process.cwd(), '.env.example');
374
+ if (fs.existsSync(examplePath)) {
375
+ envContent = fs.readFileSync(examplePath, 'utf8');
376
+ }
377
+ }
378
+ // Update credentials
379
+ const updates = [
380
+ { key: 'SNOW_INSTANCE', value: instance },
381
+ { key: 'SNOW_AUTH_METHOD', value: 'basic' },
382
+ { key: 'SNOW_USERNAME', value: username },
383
+ { key: 'SNOW_PASSWORD', value: password }
384
+ ];
385
+ for (const { key, value } of updates) {
386
+ if (envContent.includes(`${key}=`)) {
387
+ envContent = envContent.replace(new RegExp(`${key}=.*`, 'g'), `${key}=${value}`);
388
+ }
389
+ else {
390
+ envContent += `\n${key}=${value}\n`;
391
+ }
392
+ }
393
+ fs.writeFileSync(envPath, envContent);
394
+ process.env.SNOW_INSTANCE = instance;
395
+ process.env.SNOW_AUTH_METHOD = 'basic';
396
+ process.env.SNOW_USERNAME = username;
397
+ process.env.SNOW_PASSWORD = password;
398
+ }
399
+ // Test Basic Auth connection
400
+ const spinner = prompts.spinner();
401
+ spinner.start('Authenticating with ServiceNow');
401
402
  const client = new servicenow_client_js_1.ServiceNowClient();
402
403
  const testResult = await client.testConnection();
403
404
  if (testResult.success) {
404
- console.log(chalk_1.default.green(`✅ Logged in as: ${testResult.data.name} (${testResult.data.user_name})`));
405
+ spinner.stop('ServiceNow authentication successful');
406
+ prompts.log.success(`Logged in as: ${testResult.data.name} (${testResult.data.user_name})`);
407
+ // 🔧 Auto-refresh MCP configuration with new credentials
408
+ try {
409
+ const { setupMCPConfig } = await Promise.resolve().then(() => __importStar(require('../cli.js')));
410
+ const spinner2 = prompts.spinner();
411
+ spinner2.start('Updating MCP configuration');
412
+ await setupMCPConfig(process.cwd(), instance, username, password, true);
413
+ spinner2.stop('MCP servers ready for SnowCode/Claude Code');
414
+ }
415
+ catch (error) {
416
+ prompts.log.warn('Could not update MCP config - run "snow-flow init" to set up');
417
+ }
418
+ prompts.outro('Setup complete!');
419
+ }
420
+ else {
421
+ spinner.stop('Authentication failed');
422
+ prompts.cancel(testResult.error || 'Invalid credentials');
423
+ process.exit(1);
405
424
  }
406
- console.log(chalk_1.default.blue('\n🎉 Ready to start developing!'));
407
- console.log(chalk_1.default.cyan(' snow-flow swarm "create incident dashboard"'));
408
- console.log(chalk_1.default.dim(' or: ') + chalk_1.default.cyan('opencode\n'));
409
425
  }
410
- else {
411
- console.error(chalk_1.default.red(`\n❌ ServiceNow authentication failed: ${result.error}`));
412
- process.exit(1);
426
+ // ═══════════════════════════════════════════════════════════════════
427
+ // 🚀 ENTERPRISE FEATURES SETUP (Optional)
428
+ // ═══════════════════════════════════════════════════════════════════
429
+ console.log(); // Spacing
430
+ prompts.log.step('Enterprise Features (Optional)');
431
+ const hasEnterprise = await prompts.confirm({
432
+ message: 'Do you have a Snow-Flow Enterprise license?',
433
+ initialValue: false
434
+ });
435
+ if (prompts.isCancel(hasEnterprise) || !hasEnterprise) {
436
+ prompts.log.info('Skipping enterprise setup - you can add it later with "snow-flow auth login"');
437
+ prompts.outro('Setup complete!');
438
+ return;
439
+ }
440
+ // Enterprise license key
441
+ const licenseKey = await prompts.text({
442
+ message: 'Enterprise license key',
443
+ placeholder: 'SNOW-ENT-YOURCOMPANY-20261231-ABC123',
444
+ validate: (value) => {
445
+ if (!value || value.trim() === '')
446
+ return 'License key is required';
447
+ if (!value.startsWith('SNOW-'))
448
+ return 'Invalid format (should start with SNOW-)';
449
+ const parts = value.split('-');
450
+ if (parts.length !== 5)
451
+ return 'Invalid format (expected: SNOW-TIER-ORG-DATE-HASH)';
452
+ }
453
+ });
454
+ if (prompts.isCancel(licenseKey)) {
455
+ prompts.log.info('Enterprise setup skipped');
456
+ prompts.outro('Setup complete!');
457
+ return;
458
+ }
459
+ // License server URL (with sensible default)
460
+ const licenseServerUrl = await prompts.text({
461
+ message: 'Enterprise license server URL',
462
+ placeholder: 'https://license.snow-flow.dev',
463
+ defaultValue: 'https://license.snow-flow.dev',
464
+ validate: (value) => {
465
+ if (!value || value.trim() === '')
466
+ return 'URL is required';
467
+ if (!value.startsWith('https://'))
468
+ return 'Must be HTTPS URL';
469
+ }
470
+ });
471
+ if (prompts.isCancel(licenseServerUrl)) {
472
+ prompts.log.info('Enterprise setup skipped');
473
+ prompts.outro('Setup complete!');
474
+ return;
475
+ }
476
+ // Ask which integrations to configure
477
+ const integrations = await prompts.multiselect({
478
+ message: 'Configure integrations (optional)',
479
+ options: [
480
+ { value: 'jira', label: 'Jira', hint: 'Atlassian Jira Cloud' },
481
+ { value: 'azdo', label: 'Azure DevOps', hint: 'Microsoft Azure DevOps' },
482
+ { value: 'confluence', label: 'Confluence', hint: 'Atlassian Confluence' }
483
+ ],
484
+ required: false
485
+ });
486
+ const enterpriseEnv = {
487
+ SNOW_LICENSE_KEY: licenseKey,
488
+ SNOW_ENTERPRISE_URL: licenseServerUrl
489
+ };
490
+ // Jira credentials
491
+ if (integrations && integrations.includes('jira')) {
492
+ prompts.log.message('Jira Configuration');
493
+ const jiraHost = await prompts.text({
494
+ message: 'Jira host',
495
+ placeholder: 'yourcompany.atlassian.net',
496
+ validate: (v) => v && v.includes('.') ? undefined : 'Invalid host'
497
+ });
498
+ if (!prompts.isCancel(jiraHost)) {
499
+ const jiraEmail = await prompts.text({
500
+ message: 'Jira email',
501
+ placeholder: 'you@company.com',
502
+ validate: (v) => v && v.includes('@') ? undefined : 'Invalid email'
503
+ });
504
+ if (!prompts.isCancel(jiraEmail)) {
505
+ const jiraToken = await prompts.password({
506
+ message: 'Jira API token',
507
+ validate: (v) => v && v.length > 10 ? undefined : 'Token required'
508
+ });
509
+ if (!prompts.isCancel(jiraToken)) {
510
+ enterpriseEnv.JIRA_BASE_URL = `https://${jiraHost}`;
511
+ enterpriseEnv.JIRA_EMAIL = jiraEmail;
512
+ enterpriseEnv.JIRA_API_TOKEN = jiraToken;
513
+ prompts.log.success('Jira configured');
514
+ }
515
+ }
516
+ }
517
+ }
518
+ // Azure DevOps credentials
519
+ if (integrations && integrations.includes('azdo')) {
520
+ prompts.log.message('Azure DevOps Configuration');
521
+ const azdoOrg = await prompts.text({
522
+ message: 'Azure DevOps organization',
523
+ placeholder: 'yourcompany',
524
+ validate: (v) => v && v.length > 0 ? undefined : 'Organization required'
525
+ });
526
+ if (!prompts.isCancel(azdoOrg)) {
527
+ const azdoPat = await prompts.password({
528
+ message: 'Azure DevOps Personal Access Token (PAT)',
529
+ validate: (v) => v && v.length > 20 ? undefined : 'PAT required'
530
+ });
531
+ if (!prompts.isCancel(azdoPat)) {
532
+ enterpriseEnv.AZDO_ORG_URL = `https://dev.azure.com/${azdoOrg}`;
533
+ enterpriseEnv.AZDO_PAT = azdoPat;
534
+ prompts.log.success('Azure DevOps configured');
535
+ }
536
+ }
537
+ }
538
+ // Confluence credentials
539
+ if (integrations && integrations.includes('confluence')) {
540
+ prompts.log.message('Confluence Configuration');
541
+ const confluenceHost = await prompts.text({
542
+ message: 'Confluence host',
543
+ placeholder: 'yourcompany.atlassian.net',
544
+ validate: (v) => v && v.includes('.') ? undefined : 'Invalid host'
545
+ });
546
+ if (!prompts.isCancel(confluenceHost)) {
547
+ const confluenceEmail = await prompts.text({
548
+ message: 'Confluence email',
549
+ placeholder: 'you@company.com',
550
+ validate: (v) => v && v.includes('@') ? undefined : 'Invalid email'
551
+ });
552
+ if (!prompts.isCancel(confluenceEmail)) {
553
+ const confluenceToken = await prompts.password({
554
+ message: 'Confluence API token',
555
+ validate: (v) => v && v.length > 10 ? undefined : 'Token required'
556
+ });
557
+ if (!prompts.isCancel(confluenceToken)) {
558
+ enterpriseEnv.CONFLUENCE_BASE_URL = `https://${confluenceHost}`;
559
+ enterpriseEnv.CONFLUENCE_EMAIL = confluenceEmail;
560
+ enterpriseEnv.CONFLUENCE_API_TOKEN = confluenceToken;
561
+ prompts.log.success('Confluence configured');
562
+ }
563
+ }
564
+ }
565
+ }
566
+ // Save enterprise credentials to .env
567
+ const envPath = path.join(process.cwd(), '.env');
568
+ let envContent = '';
569
+ try {
570
+ envContent = fs.readFileSync(envPath, 'utf8');
571
+ }
572
+ catch {
573
+ // File doesn't exist, will create
574
+ }
575
+ for (const [key, value] of Object.entries(enterpriseEnv)) {
576
+ if (envContent.includes(`${key}=`)) {
577
+ envContent = envContent.replace(new RegExp(`${key}=.*`, 'g'), `${key}=${value}`);
578
+ }
579
+ else {
580
+ envContent += `\n${key}=${value}\n`;
581
+ }
582
+ process.env[key] = value;
583
+ }
584
+ fs.writeFileSync(envPath, envContent);
585
+ prompts.log.success('Enterprise credentials saved to .env');
586
+ // Configure enterprise MCP proxy
587
+ const spinner3 = prompts.spinner();
588
+ spinner3.start('Configuring enterprise MCP proxy');
589
+ try {
590
+ // Find the enterprise proxy path
591
+ const enterpriseProxyPath = path.join(process.cwd(), '..', 'snow-flow-enterprise', 'mcp-proxy', 'dist', 'enterprise-proxy.js');
592
+ // Check if proxy exists
593
+ if (!fs.existsSync(enterpriseProxyPath)) {
594
+ spinner3.stop('Enterprise proxy not found - will use remote server only');
595
+ prompts.log.warn('Enterprise proxy not built yet');
596
+ prompts.log.message(' Run: cd ../snow-flow-enterprise/mcp-proxy && npm run build');
597
+ }
598
+ else {
599
+ // Configure for SnowCode (prioritize SnowCode config)
600
+ const snowcodeConfigPath = path.join(process.env.HOME || '', '.snowcode', 'config.json');
601
+ const snowcodeConfigDirPath = path.join(process.env.HOME || '', '.snowcode');
602
+ if (!fs.existsSync(snowcodeConfigDirPath)) {
603
+ fs.mkdirSync(snowcodeConfigDirPath, { recursive: true });
604
+ }
605
+ let snowcodeConfig = { mcpServers: {} };
606
+ if (fs.existsSync(snowcodeConfigPath)) {
607
+ try {
608
+ snowcodeConfig = JSON.parse(fs.readFileSync(snowcodeConfigPath, 'utf8'));
609
+ }
610
+ catch {
611
+ snowcodeConfig = { mcpServers: {} };
612
+ }
613
+ }
614
+ if (!snowcodeConfig.mcpServers) {
615
+ snowcodeConfig.mcpServers = {};
616
+ }
617
+ // Add enterprise MCP server
618
+ snowcodeConfig.mcpServers['snow-flow-enterprise'] = {
619
+ command: 'node',
620
+ args: [enterpriseProxyPath],
621
+ env: enterpriseEnv
622
+ };
623
+ fs.writeFileSync(snowcodeConfigPath, JSON.stringify(snowcodeConfig, null, 2));
624
+ spinner3.stop('Enterprise MCP proxy configured for SnowCode');
625
+ prompts.log.success(`Config saved: ${snowcodeConfigPath}`);
626
+ // Also try Claude Code config
627
+ const claudeConfigPath = path.join(process.env.HOME || '', '.claude', 'settings.json');
628
+ if (fs.existsSync(path.dirname(claudeConfigPath))) {
629
+ try {
630
+ let claudeConfig = { mcpServers: {} };
631
+ if (fs.existsSync(claudeConfigPath)) {
632
+ claudeConfig = JSON.parse(fs.readFileSync(claudeConfigPath, 'utf8'));
633
+ }
634
+ if (!claudeConfig.mcpServers) {
635
+ claudeConfig.mcpServers = {};
636
+ }
637
+ claudeConfig.mcpServers['snow-flow-enterprise'] = {
638
+ command: 'node',
639
+ args: [enterpriseProxyPath],
640
+ env: enterpriseEnv
641
+ };
642
+ fs.writeFileSync(claudeConfigPath, JSON.stringify(claudeConfig, null, 2));
643
+ prompts.log.success('Also configured for Claude Code');
644
+ }
645
+ catch {
646
+ // Claude Code config optional
647
+ }
648
+ }
649
+ }
650
+ }
651
+ catch (error) {
652
+ spinner3.stop('Enterprise proxy configuration failed');
653
+ prompts.log.warn(`Error: ${error.message}`);
654
+ prompts.log.message('You can configure manually later');
413
655
  }
656
+ prompts.outro('🎉 Enterprise setup complete! Restart your AI coding assistant to use enterprise features.');
414
657
  });
415
658
  auth
416
659
  .command('logout')
@@ -418,45 +661,48 @@ function registerAuthCommands(program) {
418
661
  .action(async () => {
419
662
  const oauth = new snow_oauth_js_1.ServiceNowOAuth();
420
663
  await oauth.logout();
421
- console.log(chalk_1.default.green('Logged out successfully'));
664
+ prompts.log.success('Logged out successfully');
422
665
  });
423
666
  auth
424
667
  .command('status')
425
668
  .description('Show ServiceNow authentication status')
426
669
  .action(async () => {
427
670
  const oauth = new snow_oauth_js_1.ServiceNowOAuth();
428
- console.log(chalk_1.default.blue('\n📊 ServiceNow Authentication Status:'));
671
+ prompts.log.step('ServiceNow Authentication Status');
429
672
  const isAuthenticated = await oauth.isAuthenticated();
430
673
  const credentials = await oauth.loadCredentials();
431
674
  if (isAuthenticated && credentials) {
432
- console.log(' ├── Status: Authenticated');
433
- console.log(` ├── Instance: ${credentials.instance}`);
434
- console.log(' ├── Method: OAuth 2.0');
435
- console.log(` ├── Client ID: ${credentials.clientId}`);
675
+ prompts.log.success('Status: Authenticated');
676
+ prompts.log.info(`Instance: ${credentials.instance}`);
677
+ prompts.log.info('Method: OAuth 2.0');
678
+ prompts.log.info(`Client ID: ${credentials.clientId}`);
436
679
  if (credentials.expiresAt) {
437
680
  const expiresAt = new Date(credentials.expiresAt);
438
- console.log(` └── Token expires: ${expiresAt.toLocaleString()}`);
681
+ prompts.log.info(`Token expires: ${expiresAt.toLocaleString()}`);
439
682
  }
440
683
  // Test connection
684
+ const spinner = prompts.spinner();
685
+ spinner.start('Testing connection');
441
686
  const client = new servicenow_client_js_1.ServiceNowClient();
442
687
  const testResult = await client.testConnection();
443
688
  if (testResult.success) {
444
- console.log(`\n🔍 Connection test: ✅ Success`);
689
+ spinner.stop('Connection test successful');
445
690
  if (testResult.data.message) {
446
- console.log(` ${testResult.data.message}`);
691
+ prompts.log.message(` ${testResult.data.message}`);
447
692
  }
448
- console.log(`🌐 Instance: ${testResult.data.email || credentials.instance}`);
693
+ prompts.log.info(`Instance: ${testResult.data.email || credentials.instance}`);
449
694
  }
450
695
  else {
451
- console.log(`\n🔍 Connection test: ❌ Failed`);
452
- console.log(` Error: ${testResult.error}`);
696
+ spinner.stop('Connection test failed');
697
+ prompts.log.error(`Error: ${testResult.error}`);
453
698
  }
454
699
  }
455
700
  else {
456
- console.log(' ├── Status: Not authenticated');
457
- console.log(' ├── Instance: Not configured');
458
- console.log(' └── Method: Not set');
459
- console.log('\n💡 Create .env file and run "snow-flow auth login"');
701
+ prompts.log.error('Status: Not authenticated');
702
+ prompts.log.message('Instance: Not configured');
703
+ prompts.log.message('Method: Not set');
704
+ prompts.log.message('');
705
+ prompts.log.info('Create .env file and run "snow-flow auth login"');
460
706
  }
461
707
  });
462
708
  }