maistro 1.2.20 → 1.2.24

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/dist/app.d.ts CHANGED
@@ -185,6 +185,14 @@ export declare class MaistroApp {
185
185
  * Handle settings command - show settings menu
186
186
  */
187
187
  private handleSettings;
188
+ /**
189
+ * Handle webhook URL configuration in settings
190
+ */
191
+ private handleWebhookSettings;
192
+ /**
193
+ * Prompt user to enter a webhook URL using InputBox
194
+ */
195
+ private promptWebhookUrl;
188
196
  /**
189
197
  * Show manual Cmd+V configuration instructions
190
198
  */
@@ -233,5 +241,9 @@ export declare class MaistroApp {
233
241
  private renderExecutionSection;
234
242
  private handleRun;
235
243
  private executeWithUnifiedView;
244
+ /**
245
+ * Get a display name for the project (folder name, or "projectName (folder)" if they differ)
246
+ */
247
+ private getProjectDisplayName;
236
248
  }
237
249
  //# sourceMappingURL=app.d.ts.map
package/dist/app.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAmOA,qBAAa,UAAU;IACrB,OAAO,CAAC,EAAE,CAAgB;IAC1B,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAS;IAG9B,OAAO,CAAC,cAAc,CAA+B;gBAEzC,WAAW,GAAE,MAAY;IAsBrC,OAAO,CAAC,eAAe;IASvB;;;OAGG;YACW,qBAAqB;IA+CnC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkH5B;;;OAGG;YACW,iBAAiB;IAiE/B;;OAEG;IACH,OAAO,CAAC,eAAe;IAUvB;;;OAGG;IACG,YAAY,CAAC,SAAS,UAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAqFtD;;;OAGG;YACW,iBAAiB;IAwD/B;;OAEG;YACW,sBAAsB;IAsCpC;;;OAGG;YACW,iBAAiB;IAoB/B;;;OAGG;YACW,oBAAoB;IAkClC;;OAEG;YACW,qBAAqB;IA4BnC;;OAEG;YACW,gBAAgB;IAoB9B;;;OAGG;YACW,qBAAqB;IAoEnC;;;OAGG;YACW,eAAe;IAyI7B;;;OAGG;YACW,gBAAgB;IAiD9B;;;OAGG;YACW,uBAAuB;IAyErC;;OAEG;YACW,4BAA4B;IAkE1C;;OAEG;YACW,oBAAoB;IAiElC;;OAEG;IACH,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,UAAU;IAQlB;;;;;;OAMG;IACH,OAAO,CAAC,qBAAqB;IA2J7B,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,gBAAgB,CAAK;IAE7B;;;OAGG;YACW,YAAY;IAyT1B;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IA2CxB;;OAEG;YACW,aAAa;IAsmB3B;;;OAGG;YACW,iBAAiB;IAgI/B;;OAEG;YACW,cAAc;IAwU5B,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,aAAa,CAAS;IAE9B;;OAEG;YACW,gBAAgB;IAkB9B;;OAEG;YACW,gBAAgB;IAe9B;;;OAGG;YACW,WAAW;IAsEzB;;;OAGG;YACW,mBAAmB;IA8BjC;;;OAGG;YACW,cAAc;IAkB5B;;;;OAIG;YACW,6BAA6B;IAqI3C;;;OAGG;YACW,aAAa;IA+B3B;;;;OAIG;YACW,sBAAsB;IA6NpC;;;;OAIG;YACW,sBAAsB;YA2GtB,UAAU;IA8HxB,OAAO,CAAC,YAAY;IAmFpB;;;OAGG;YACW,eAAe;IAqD7B;;OAEG;YACW,cAAc;IAsO5B;;OAEG;YACW,oBAAoB;IAwBlC;;OAEG;YACW,eAAe;IAU7B;;OAEG;YACW,wBAAwB;YAsBxB,gBAAgB;IA4B9B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsD;IAEpF;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAqB3B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAO1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAS1B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAKzB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAqB5B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAQ/B;;OAEG;IACH,OAAO,CAAC,eAAe;IASvB;;OAEG;IACH,OAAO,CAAC,sBAAsB;YAoEhB,SAAS;YAiIT,sBAAsB;CAkcrC"}
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAwOA,qBAAa,UAAU;IACrB,OAAO,CAAC,EAAE,CAAgB;IAC1B,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAS;IAG9B,OAAO,CAAC,cAAc,CAA+B;gBAEzC,WAAW,GAAE,MAAY;IAsBrC,OAAO,CAAC,eAAe;IASvB;;;OAGG;YACW,qBAAqB;IA+CnC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkH5B;;;OAGG;YACW,iBAAiB;IAiE/B;;OAEG;IACH,OAAO,CAAC,eAAe;IAUvB;;;OAGG;IACG,YAAY,CAAC,SAAS,UAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAqFtD;;;OAGG;YACW,iBAAiB;IAwD/B;;OAEG;YACW,sBAAsB;IAsCpC;;;OAGG;YACW,iBAAiB;IAoB/B;;;OAGG;YACW,oBAAoB;IAkClC;;OAEG;YACW,qBAAqB;IA4BnC;;OAEG;YACW,gBAAgB;IAoB9B;;;OAGG;YACW,qBAAqB;IAoEnC;;;OAGG;YACW,eAAe;IAiJ7B;;;OAGG;YACW,gBAAgB;IAiD9B;;;OAGG;YACW,uBAAuB;IAyErC;;OAEG;YACW,4BAA4B;IAkE1C;;OAEG;YACW,oBAAoB;IAiElC;;OAEG;IACH,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,UAAU;IAQlB;;;;;;OAMG;IACH,OAAO,CAAC,qBAAqB;IA2J7B,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,gBAAgB,CAAK;IAE7B;;;OAGG;YACW,YAAY;IAyT1B;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IA2CxB;;OAEG;YACW,aAAa;IAsmB3B;;;OAGG;YACW,iBAAiB;IAgI/B;;OAEG;YACW,cAAc;IAwU5B,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,aAAa,CAAS;IAE9B;;OAEG;YACW,gBAAgB;IAkB9B;;OAEG;YACW,gBAAgB;IAe9B;;;OAGG;YACW,WAAW;IAsEzB;;;OAGG;YACW,mBAAmB;IA8BjC;;;OAGG;YACW,cAAc;IAkB5B;;;;OAIG;YACW,6BAA6B;IAqI3C;;;OAGG;YACW,aAAa;IA+B3B;;;;OAIG;YACW,sBAAsB;IA6NpC;;;;OAIG;YACW,sBAAsB;YAmHtB,UAAU;IAsIxB,OAAO,CAAC,YAAY;IAmFpB;;;OAGG;YACW,eAAe;IAqD7B;;OAEG;YACW,cAAc;IAgQ5B;;OAEG;YACW,qBAAqB;IA0CnC;;OAEG;YACW,gBAAgB;IA4C9B;;OAEG;YACW,oBAAoB;IAwBlC;;OAEG;YACW,eAAe;IAU7B;;OAEG;YACW,wBAAwB;YAsBxB,gBAAgB;IA4B9B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsD;IAEpF;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAqB3B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAO1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAS1B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAKzB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAqB5B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAQ/B;;OAEG;IACH,OAAO,CAAC,eAAe;IASvB;;OAEG;IACH,OAAO,CAAC,sBAAsB;YAoEhB,SAAS;YAiIT,sBAAsB;IA8dpC;;OAEG;IACH,OAAO,CAAC,qBAAqB;CAc9B"}
package/dist/app.js CHANGED
@@ -15,7 +15,8 @@ import { buildHeaderWithLogo } from './logo.js';
15
15
  import { addImageFromFile, addImageFromUrl, addImageFromPaste, getImages, deleteImage, looksLikeImagePath, looksLikeImageUrl, formatImageSize, sanitizeImageId, generatePasteImageId, extractImageUrls, processDescriptionImageUrls, } from './imageManager.js';
16
16
  import { InputBox, getDisplayWidth, charIndexAtDisplayColumn } from './inputBox.js';
17
17
  import { startCaffeinate, stopCaffeinate, isCaffeinateAvailable, isCaffeinateRunning } from './caffeinate.js';
18
- import { getPreventSleep, setPreventSleep, getConfigPath, detectTerminal, getShiftEnterEnabled, setShiftEnterEnabled, configureTerminalShiftEnter, removeTerminalShiftEnter, getNewlineHint, getCmdVPasteEnabled, setCmdVPasteEnabled, configureTerminalCmdVPaste, removeTerminalCmdVPaste, getCmdVPasteInstructions, validateTerminalConfig, } from './config.js';
18
+ import { sendNotification, testWebhook } from './notifications.js';
19
+ import { getPreventSleep, setPreventSleep, getConfigPath, detectTerminal, getShiftEnterEnabled, setShiftEnterEnabled, configureTerminalShiftEnter, removeTerminalShiftEnter, getNewlineHint, getCmdVPasteEnabled, setCmdVPasteEnabled, configureTerminalCmdVPaste, removeTerminalCmdVPaste, getCmdVPasteInstructions, validateTerminalConfig, getSoundNotifications, setSoundNotifications, getWebhookUrl, setWebhookUrl, } from './config.js';
19
20
  import { hasClipboardImage, saveClipboardImage, cleanupTempImage } from './clipboard.js';
20
21
  import { isGitRepo } from './git.js';
21
22
  import { UI, TipsManager } from './constants.js';
@@ -742,6 +743,13 @@ export class MaistroApp {
742
743
  questions: result.questions.map(q => ({ id: q.id, question: q.question, options: q.options })),
743
744
  assumptions: result.assumptions,
744
745
  });
746
+ // Notify user that input is needed
747
+ await sendNotification({
748
+ event: 'attention',
749
+ title: 'Discovery: input needed',
750
+ detail: `${result.questions.length} question${result.questions.length > 1 ? 's' : ''} to answer`,
751
+ project: this.getProjectDisplayName(),
752
+ });
745
753
  // Ask each question one at a time (full-screen for each)
746
754
  const answers = {};
747
755
  for (let i = 0; i < result.questions.length; i++) {
@@ -3076,6 +3084,13 @@ ${detection.projectContext}`;
3076
3084
  }
3077
3085
  console.log(`\n\x1b[32m✓ Plan created with ${tasksWithImages.length} tasks\x1b[0m\n`);
3078
3086
  getLogger()?.info('Plan created and finalized', { taskCount: tasksWithImages.length });
3087
+ // Notify user that plan is ready for review
3088
+ await sendNotification({
3089
+ event: 'attention',
3090
+ title: 'Plan ready for review',
3091
+ detail: `${tasksWithImages.length} tasks created`,
3092
+ project: this.getProjectDisplayName(),
3093
+ });
3079
3094
  }
3080
3095
  catch (err) {
3081
3096
  // Re-throw ExitRequestedError to propagate exit signal
@@ -3184,6 +3199,13 @@ ${detection.projectContext}`;
3184
3199
  }
3185
3200
  console.log(`\n\x1b[32m✓ Project initialized with ${tasksWithImages.length} tasks\x1b[0m\n`);
3186
3201
  getLogger()?.info('Plan created and finalized', { taskCount: tasksWithImages.length });
3202
+ // Notify user that plan is ready for review
3203
+ await sendNotification({
3204
+ event: 'attention',
3205
+ title: 'Plan ready for review',
3206
+ detail: `${tasksWithImages.length} tasks created`,
3207
+ project: this.getProjectDisplayName(),
3208
+ });
3187
3209
  }
3188
3210
  catch (err) {
3189
3211
  // Re-throw ExitRequestedError to propagate exit signal
@@ -3393,6 +3415,20 @@ ${detection.projectContext}`;
3393
3415
  description: `${terminalInfo.name} - use /paste command instead`,
3394
3416
  });
3395
3417
  }
3418
+ // Sound notification toggle
3419
+ const soundEnabled = getSoundNotifications();
3420
+ menuItems.push({
3421
+ key: 'sound',
3422
+ label: `Sound notifications: ${soundEnabled ? '\x1b[32mON\x1b[0m' : '\x1b[90mOFF\x1b[0m'}`,
3423
+ description: 'Play a chime when execution completes or needs attention',
3424
+ });
3425
+ // Webhook notification
3426
+ const webhookUrl = getWebhookUrl();
3427
+ menuItems.push({
3428
+ key: 'webhook',
3429
+ label: `Webhook notifications: ${webhookUrl ? '\x1b[32mConfigured\x1b[0m' : '\x1b[90mOFF\x1b[0m'}`,
3430
+ description: webhookUrl ? 'Update or remove webhook URL' : 'Configure a webhook URL for notifications',
3431
+ });
3396
3432
  menuItems.push({
3397
3433
  key: 'back',
3398
3434
  label: 'Back',
@@ -3529,8 +3565,107 @@ ${detection.projectContext}`;
3529
3565
  // Show manual instructions for Cmd+V
3530
3566
  await this.showCmdVInstructions();
3531
3567
  }
3568
+ if (choice === 'sound') {
3569
+ const newValue = !soundEnabled;
3570
+ setSoundNotifications(newValue);
3571
+ // Loop redraws with updated value
3572
+ }
3573
+ if (choice === 'webhook') {
3574
+ await this.handleWebhookSettings();
3575
+ }
3576
+ }
3577
+ }
3578
+ /**
3579
+ * Handle webhook URL configuration in settings
3580
+ */
3581
+ async handleWebhookSettings() {
3582
+ const currentUrl = getWebhookUrl();
3583
+ if (currentUrl) {
3584
+ // Already configured — show sub-menu
3585
+ this.ui.clear();
3586
+ this.showHeader();
3587
+ console.log('\x1b[1m── Settings: Webhook Notifications ──\x1b[0m\n');
3588
+ console.log(`\x1b[90mCurrent URL: ${currentUrl}\x1b[0m\n`);
3589
+ const subMenuItems = [
3590
+ { key: 'update', label: 'Update URL', description: 'Enter a new webhook URL' },
3591
+ { key: 'test', label: 'Test notification', description: 'Send a test message to the webhook' },
3592
+ { key: 'remove', label: 'Remove', description: 'Disable webhook notifications' },
3593
+ { key: 'cancel', label: 'Cancel', description: 'Return to settings' },
3594
+ ];
3595
+ const subChoice = await this.ui.quickPick(subMenuItems, 'Webhook options:', undefined, { showInput: false });
3596
+ if (subChoice === 'update') {
3597
+ await this.promptWebhookUrl();
3598
+ }
3599
+ else if (subChoice === 'test') {
3600
+ this.ui.clear();
3601
+ this.showHeader();
3602
+ console.log('\x1b[1m── Settings: Test Webhook ──\x1b[0m\n');
3603
+ console.log('Sending test notification...\n');
3604
+ const result = await testWebhook(currentUrl);
3605
+ if (result.success) {
3606
+ console.log('\x1b[32m✓ Test notification sent successfully!\x1b[0m');
3607
+ }
3608
+ else {
3609
+ console.log(`\x1b[31m✗ Failed: ${result.error}\x1b[0m`);
3610
+ }
3611
+ console.log('\n\x1b[90mPress any key to continue...\x1b[0m');
3612
+ await this.waitForKeypress();
3613
+ }
3614
+ else if (subChoice === 'remove') {
3615
+ setWebhookUrl(undefined);
3616
+ }
3617
+ }
3618
+ else {
3619
+ // Not configured — prompt for URL
3620
+ await this.promptWebhookUrl();
3532
3621
  }
3533
3622
  }
3623
+ /**
3624
+ * Prompt user to enter a webhook URL using InputBox
3625
+ */
3626
+ async promptWebhookUrl() {
3627
+ return new Promise((resolve) => {
3628
+ const inputBox = new InputBox({
3629
+ placeholder: 'https://hooks.slack.com/services/...',
3630
+ hint: 'Enter to save · Esc to cancel',
3631
+ onRedraw: () => {
3632
+ this.ui.clear();
3633
+ this.showHeader();
3634
+ console.log('\x1b[1m── Settings: Webhook URL ──\x1b[0m\n');
3635
+ console.log('Enter your webhook URL for notifications.');
3636
+ console.log('Works with Slack, Discord, or any endpoint that accepts JSON POST.\n');
3637
+ console.log('\x1b[90mSlack: Workspace Settings → Manage Apps → Custom Integrations → Incoming WebHooks\x1b[0m\n');
3638
+ },
3639
+ onSubmit: (value) => {
3640
+ const url = value.trim();
3641
+ if (!url) {
3642
+ resolve();
3643
+ return;
3644
+ }
3645
+ if (!url.startsWith('https://')) {
3646
+ this.ui.clear();
3647
+ this.showHeader();
3648
+ console.log('\x1b[1m── Settings: Webhook URL ──\x1b[0m\n');
3649
+ console.log('\x1b[31m✗ URL must start with https://\x1b[0m');
3650
+ console.log('\n\x1b[90mPress any key to continue...\x1b[0m');
3651
+ this.waitForKeypress().then(() => resolve());
3652
+ return;
3653
+ }
3654
+ setWebhookUrl(url);
3655
+ this.ui.clear();
3656
+ this.showHeader();
3657
+ console.log('\x1b[1m── Settings ──\x1b[0m\n');
3658
+ console.log('\x1b[32m✓ Webhook URL saved!\x1b[0m');
3659
+ console.log('\n\x1b[90mPress any key to continue...\x1b[0m');
3660
+ this.waitForKeypress().then(() => resolve());
3661
+ },
3662
+ onCancel: () => {
3663
+ resolve();
3664
+ },
3665
+ });
3666
+ inputBox.start();
3667
+ });
3668
+ }
3534
3669
  /**
3535
3670
  * Show manual Cmd+V configuration instructions
3536
3671
  */
@@ -4292,8 +4427,54 @@ ${detection.projectContext}`;
4292
4427
  getLogger()?.error('Execution failed with full error', { error: result.error });
4293
4428
  }
4294
4429
  }
4430
+ // Send notifications (sound + webhook)
4431
+ // Skip notification if no tasks were actually executed this run (all were already done)
4432
+ const tasksExecutedThisRun = result.completedTasks - alreadyCompleted;
4433
+ const project = this.getProjectDisplayName();
4434
+ if (result.success && tasksExecutedThisRun > 0) {
4435
+ await sendNotification({
4436
+ event: 'completed',
4437
+ title: 'All tasks completed',
4438
+ detail: `${result.completedTasks}/${result.totalTasks} tasks completed successfully`,
4439
+ project,
4440
+ });
4441
+ }
4442
+ else if (result.blockedTask) {
4443
+ await sendNotification({
4444
+ event: 'attention',
4445
+ title: `Task failed: ${result.blockedTask.title}`,
4446
+ detail: result.error ? truncateError(result.error) : undefined,
4447
+ project,
4448
+ });
4449
+ }
4450
+ else if (!this.shouldStop) {
4451
+ // User-initiated pause doesn't need a notification
4452
+ await sendNotification({
4453
+ event: 'attention',
4454
+ title: 'Execution failed',
4455
+ detail: result.error ? truncateError(result.error) : undefined,
4456
+ project,
4457
+ });
4458
+ }
4295
4459
  // Clear cached orchestrator
4296
4460
  this.orchestrator = null;
4297
4461
  }
4462
+ /**
4463
+ * Get a display name for the project (folder name, or "projectName (folder)" if they differ)
4464
+ */
4465
+ getProjectDisplayName() {
4466
+ const folderName = basename(this.projectPath);
4467
+ try {
4468
+ const orchestrator = this.getOrchestrator();
4469
+ const projectName = orchestrator.getProjectName();
4470
+ if (projectName && projectName !== folderName) {
4471
+ return `${projectName} (${folderName})`;
4472
+ }
4473
+ }
4474
+ catch {
4475
+ // Orchestrator may not be loaded yet
4476
+ }
4477
+ return folderName;
4478
+ }
4298
4479
  }
4299
4480
  //# sourceMappingURL=app.js.map