maiass 5.9.6 → 5.9.7

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.
package/lib/commit.js CHANGED
@@ -13,6 +13,51 @@ import { logCommit } from './devlog.js';
13
13
  import colors from './colors.js';
14
14
  import chalk from 'chalk';
15
15
 
16
+ /**
17
+ * Simple spinner for AI API calls
18
+ */
19
+ class Spinner {
20
+ constructor(message = 'Working') {
21
+ this.message = message;
22
+ this.frames = ['\u280b', '\u2819', '\u2839', '\u2838', '\u283c', '\u2834', '\u2826', '\u2827', '\u2807', '\u280f'];
23
+ this.frameIndex = 0;
24
+ this.intervalId = null;
25
+ this.isSpinning = false;
26
+ }
27
+
28
+ start() {
29
+ if (this.isSpinning) return;
30
+ this.isSpinning = true;
31
+
32
+ // Hide cursor
33
+ process.stderr.write('\x1B[?25l');
34
+
35
+ this.intervalId = setInterval(() => {
36
+ const frame = this.frames[this.frameIndex];
37
+ process.stderr.write(`\r${colors.BYellow(frame)} ${this.message}...`);
38
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
39
+ }, 100);
40
+ }
41
+
42
+ stop(success = true) {
43
+ if (!this.isSpinning) return;
44
+ this.isSpinning = false;
45
+
46
+ if (this.intervalId) {
47
+ clearInterval(this.intervalId);
48
+ this.intervalId = null;
49
+ }
50
+
51
+ // Clear line and show cursor
52
+ process.stderr.write('\r' + ' '.repeat(100) + '\r');
53
+ process.stderr.write('\x1B[?25h');
54
+
55
+ if (success) {
56
+ process.stderr.write(`${colors.BGreen('\u2713')} Done\n`);
57
+ }
58
+ }
59
+ }
60
+
16
61
  /**
17
62
  * Get color for credit display based on remaining credits (matches bashmaiass)
18
63
  * @param {number} credits - Remaining credits
@@ -373,111 +418,150 @@ ${gitDiff}`;
373
418
  log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] Request body: ${JSON.stringify(requestBody, null, 2)}`);
374
419
  }
375
420
 
376
- const response = await fetch(aiEndpoint, {
377
- method: 'POST',
378
- headers: {
379
- 'Content-Type': 'application/json',
380
- 'Authorization': `Bearer ${maiassToken}`,
381
- 'X-Machine-Fingerprint': generateMachineFingerprint(),
382
- 'X-Client-Name': getClientName(),
383
- 'X-Client-Version': getClientVersion(),
384
- 'X-Subscription-ID': process.env.MAIASS_SUBSCRIPTION_ID || ''
385
- },
386
- body: JSON.stringify(requestBody)
387
- });
421
+ // Set timeout for API call (default 30 seconds)
422
+ const timeout = parseInt(process.env.MAIASS_AI_TIMEOUT || '30') * 1000;
388
423
 
389
424
  if (debugMode) {
390
- log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] Response status: ${response.status} ${response.statusText}`);
391
- log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] Response headers: ${JSON.stringify(Object.fromEntries(response.headers), null, 2)}`);
425
+ log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] API timeout set to ${timeout/1000} seconds`);
392
426
  }
393
-
394
- if (!response.ok) {
395
- const errorText = await response.text();
396
- if (debugMode) {
397
- log.debug(SYMBOLS.WARNING, `[MAIASS DEBUG] Response error body: ${errorText}`);
398
- }
399
- log.error(SYMBOLS.WARNING, `AI API request failed: ${response.status} ${response.statusText}`);
400
- return null;
401
- }
402
-
403
- const data = await response.json();
404
-
405
- if (debugMode) {
406
- log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] Response data: ${JSON.stringify(data, null, 2)}`);
407
- }
408
-
409
- if (data.choices && data.choices.length > 0) {
410
- let suggestion = data.choices[0].message.content.trim();
411
427
 
412
- // Enhanced debug logging: output the suggestion returned by AI
428
+ // Start spinner (unless in debug mode)
429
+ const spinner = !debugMode ? new Spinner('Waiting for AI response') : null;
430
+ if (spinner) spinner.start();
431
+
432
+ try {
433
+ // Fetch with timeout using AbortController
434
+ const controller = new AbortController();
435
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
436
+
437
+ const response = await fetch(aiEndpoint, {
438
+ method: 'POST',
439
+ headers: {
440
+ 'Content-Type': 'application/json',
441
+ 'Authorization': `Bearer ${maiassToken}`,
442
+ 'X-Machine-Fingerprint': generateMachineFingerprint(),
443
+ 'X-Client-Name': getClientName(),
444
+ 'X-Client-Version': getClientVersion(),
445
+ 'X-Subscription-ID': process.env.MAIASS_SUBSCRIPTION_ID || ''
446
+ },
447
+ body: JSON.stringify(requestBody),
448
+ signal: controller.signal
449
+ });
450
+
451
+ clearTimeout(timeoutId);
452
+
453
+ // Stop spinner on success
454
+ if (spinner) spinner.stop(true);
455
+
413
456
  if (debugMode) {
414
- log.debug(SYMBOLS.INFO, '[MAIASS DEBUG] --- AI SUGGESTION RETURNED ---');
415
- log.debug(SYMBOLS.INFO, suggestion);
457
+ log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] Response status: ${response.status} ${response.statusText}`);
458
+ log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] Response headers: ${JSON.stringify(Object.fromEntries(response.headers), null, 2)}`);
459
+ }
460
+
461
+ if (!response.ok) {
462
+ const errorText = await response.text();
463
+ if (debugMode) {
464
+ log.debug(SYMBOLS.WARNING, `[MAIASS DEBUG] Response error body: ${errorText}`);
465
+ }
466
+ log.error(SYMBOLS.WARNING, `AI API request failed: ${response.status} ${response.statusText}`);
467
+ return null;
416
468
  }
417
469
 
418
- // Clean up any quotes that might wrap the entire response
419
- if ((suggestion.startsWith("'") && suggestion.endsWith("'")) ||
420
- (suggestion.startsWith('"') && suggestion.endsWith('"'))) {
421
- suggestion = suggestion.slice(1, -1).trim();
470
+ // Parse JSON response inside the same try block
471
+ const data = await response.json();
472
+
473
+ if (debugMode) {
474
+ log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] Response data: ${JSON.stringify(data, null, 2)}`);
422
475
  }
423
476
 
424
- // Extract credit information from billing data
425
- let creditsUsed, creditsRemaining;
426
- if (data.billing) {
427
- creditsUsed = data.billing.credits_used;
428
- creditsRemaining = data.billing.credits_remaining;
477
+ if (data.choices && data.choices.length > 0) {
478
+ let suggestion = data.choices[0].message.content.trim();
479
+
480
+ // Enhanced debug logging: output the suggestion returned by AI
481
+ if (debugMode) {
482
+ log.debug(SYMBOLS.INFO, '[MAIASS DEBUG] --- AI SUGGESTION RETURNED ---');
483
+ log.debug(SYMBOLS.INFO, suggestion);
484
+ }
429
485
 
430
- // Show warnings if available
431
- if (data.billing.warning) {
432
- log.warning(SYMBOLS.WARNING, data.billing.warning);
486
+ // Clean up any quotes that might wrap the entire response
487
+ if ((suggestion.startsWith("'") && suggestion.endsWith("'")) ||
488
+ (suggestion.startsWith('"') && suggestion.endsWith('"'))) {
489
+ suggestion = suggestion.slice(1, -1).trim();
433
490
  }
434
491
 
435
- // Display proxy messages if available
436
- if (data.messages && Array.isArray(data.messages)) {
437
- data.messages.forEach(message => {
438
- const icon = message.icon || '';
439
- const text = message.text || '';
440
-
441
- switch (message.type) {
442
- case 'error':
443
- log.error(icon, text);
444
- break;
445
- case 'warning':
446
- log.warning(icon, text);
447
- break;
448
- case 'info':
449
- log.info(icon, text);
450
- break;
451
- case 'notice':
452
- log.blue(icon, text);
453
- break;
454
- case 'success':
455
- log.success(icon, text);
456
- break;
457
- default:
458
- log.plain(`${icon} ${text}`);
459
- }
460
- });
492
+ // Extract credit information from billing data
493
+ let creditsUsed, creditsRemaining;
494
+ if (data.billing) {
495
+ creditsUsed = data.billing.credits_used;
496
+ creditsRemaining = data.billing.credits_remaining;
497
+
498
+ // Show warnings if available
499
+ if (data.billing.warning) {
500
+ log.warning(SYMBOLS.WARNING, data.billing.warning);
501
+ }
502
+
503
+ // Display proxy messages if available
504
+ if (data.messages && Array.isArray(data.messages)) {
505
+ data.messages.forEach(message => {
506
+ const icon = message.icon || '';
507
+ const text = message.text || '';
508
+
509
+ switch (message.type) {
510
+ case 'error':
511
+ log.error(icon, text);
512
+ break;
513
+ case 'warning':
514
+ log.warning(icon, text);
515
+ break;
516
+ case 'info':
517
+ log.info(icon, text);
518
+ break;
519
+ case 'notice':
520
+ log.blue(icon, text);
521
+ break;
522
+ case 'success':
523
+ log.success(icon, text);
524
+ break;
525
+ default:
526
+ log.plain(`${icon} ${text}`);
527
+ }
528
+ });
529
+ }
530
+ } else if (data.usage) {
531
+ // Fallback to legacy token display
532
+ const totalTokens = data.usage.total_tokens || 0;
533
+ const promptTokens = data.usage.prompt_tokens || 0;
534
+ const completionTokens = data.usage.completion_tokens || 0;
535
+ log.info(SYMBOLS.INFO, `Total Tokens: ${totalTokens} (${promptTokens} + ${completionTokens})`);
461
536
  }
462
- } else if (data.usage) {
463
- // Fallback to legacy token display
464
- const totalTokens = data.usage.total_tokens || 0;
465
- const promptTokens = data.usage.prompt_tokens || 0;
466
- const completionTokens = data.usage.completion_tokens || 0;
467
- log.info(SYMBOLS.INFO, `Total Tokens: ${totalTokens} (${promptTokens} + ${completionTokens})`);
537
+
538
+ return {
539
+ suggestion,
540
+ creditsUsed,
541
+ creditsRemaining
542
+ };
468
543
  }
469
544
 
470
- return {
471
- suggestion,
472
- creditsUsed,
473
- creditsRemaining
474
- };
475
- }
476
-
477
- if (debugMode) {
478
- log.debug(SYMBOLS.WARNING, '[MAIASS DEBUG] No valid AI response received - no choices in response data');
545
+ if (debugMode) {
546
+ log.debug(SYMBOLS.WARNING, '[MAIASS DEBUG] No valid AI response received - no choices in response data');
547
+ }
548
+ return null;
549
+ } catch (error) {
550
+ // Stop spinner on error
551
+ if (spinner) spinner.stop(false);
552
+
553
+ if (error.name === 'AbortError') {
554
+ log.error(SYMBOLS.WARNING, `AI request timed out after ${timeout/1000} seconds`);
555
+ log.info(SYMBOLS.INFO, 'The AI service might be slow or unresponsive. Try again or increase MAIASS_AI_TIMEOUT.');
556
+ return null;
557
+ }
558
+
559
+ if (debugMode) {
560
+ log.debug(SYMBOLS.WARNING, `[MAIASS DEBUG] AI suggestion error details: ${error.stack || error.message}`);
561
+ }
562
+ log.error(SYMBOLS.WARNING, `AI suggestion failed: ${error.message}`);
563
+ return null;
479
564
  }
480
- return null;
481
565
  } catch (error) {
482
566
  if (debugMode) {
483
567
  log.debug(SYMBOLS.WARNING, `[MAIASS DEBUG] AI suggestion error details: ${error.stack || error.message}`);
@@ -18,6 +18,7 @@ export const MAIASS_VARIABLES = {
18
18
  'MAIASS_AI_MODEL': { default: 'gpt-3.5-turbo', description: 'AI model to use' },
19
19
  'MAIASS_AI_TEMPERATURE': { default: '0.7', description: 'AI temperature setting' },
20
20
  'MAIASS_AI_MAX_CHARACTERS': { default: '8000', description: 'Max characters for AI requests' },
21
+ 'MAIASS_AI_TIMEOUT': { default: '30', description: 'AI request timeout in seconds' },
21
22
  'MAIASS_AI_COMMIT_MESSAGE_STYLE': { default: 'bullet', description: 'Commit message style' },
22
23
 
23
24
  // Version file system
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "maiass",
3
3
  "type": "module",
4
- "version": "5.9.6",
4
+ "version": "5.9.7",
5
5
  "description": "MAIASS - Modular AI-Augmented Semantic Scribe - Intelligent Git workflow automation",
6
6
  "main": "maiass.mjs",
7
7
  "bin": {