maistro 1.2.20 → 1.2.25
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 +12 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +188 -3
- package/dist/app.js.map +1 -1
- package/dist/config.d.ts +21 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +35 -0
- package/dist/config.js.map +1 -1
- package/dist/notifications.d.ts +22 -0
- package/dist/notifications.d.ts.map +1 -0
- package/dist/notifications.js +155 -0
- package/dist/notifications.js.map +1 -0
- package/dist/planner.d.ts +6 -0
- package/dist/planner.d.ts.map +1 -1
- package/dist/planner.js +7 -1
- package/dist/planner.js.map +1 -1
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +6 -9
- package/dist/ui.js.map +1 -1
- package/package.json +1 -1
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":"
|
|
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;IAoJ7B;;;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;IA8NpC;;;;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 {
|
|
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';
|
|
@@ -702,7 +703,7 @@ export class MaistroApp {
|
|
|
702
703
|
* Handle project discovery phase - ask clarifying questions before decomposition
|
|
703
704
|
* Uses full-screen approach for each step
|
|
704
705
|
*/
|
|
705
|
-
async handleDiscovery(goal) {
|
|
706
|
+
async handleDiscovery(goal, existingTasks) {
|
|
706
707
|
// Show analyzing screen with spinner (with escape-to-exit support)
|
|
707
708
|
setStatusState('working');
|
|
708
709
|
this.ui.clear();
|
|
@@ -717,6 +718,9 @@ export class MaistroApp {
|
|
|
717
718
|
const apiPromise = discoverRequirements(goal, {
|
|
718
719
|
cwd: this.projectPath,
|
|
719
720
|
abortSignal: abortController.signal,
|
|
721
|
+
existingTasks: existingTasks
|
|
722
|
+
?.filter(t => t.status !== 'completed')
|
|
723
|
+
.map(t => ({ id: t.id, title: t.title, status: t.status })),
|
|
720
724
|
});
|
|
721
725
|
result = await Promise.race([
|
|
722
726
|
apiPromise,
|
|
@@ -742,6 +746,13 @@ export class MaistroApp {
|
|
|
742
746
|
questions: result.questions.map(q => ({ id: q.id, question: q.question, options: q.options })),
|
|
743
747
|
assumptions: result.assumptions,
|
|
744
748
|
});
|
|
749
|
+
// Notify user that input is needed
|
|
750
|
+
await sendNotification({
|
|
751
|
+
event: 'attention',
|
|
752
|
+
title: 'Discovery: input needed',
|
|
753
|
+
detail: `${result.questions.length} question${result.questions.length > 1 ? 's' : ''} to answer`,
|
|
754
|
+
project: this.getProjectDisplayName(),
|
|
755
|
+
});
|
|
745
756
|
// Ask each question one at a time (full-screen for each)
|
|
746
757
|
const answers = {};
|
|
747
758
|
for (let i = 0; i < result.questions.length; i++) {
|
|
@@ -2813,7 +2824,8 @@ export class MaistroApp {
|
|
|
2813
2824
|
console.log(`\x1b[1m── Discussion ──\x1b[0m\n\x1b[90mYou: ${userInput.length > 100 ? userInput.slice(0, 97) + '...' : userInput}\x1b[0m\n`);
|
|
2814
2825
|
// Run discovery phase for the new request (same as initial project setup)
|
|
2815
2826
|
// This detects external dependencies, asks clarifying questions, and prompts for secrets
|
|
2816
|
-
|
|
2827
|
+
// Pass existing tasks so discovery is aware of the current plan
|
|
2828
|
+
const discoveryResult = await this.handleDiscovery(userInput, currentTasks);
|
|
2817
2829
|
let discoveryContext = '';
|
|
2818
2830
|
if (discoveryResult && discoveryResult.confirmed) {
|
|
2819
2831
|
const context = buildProjectContext(userInput, discoveryResult);
|
|
@@ -3076,6 +3088,13 @@ ${detection.projectContext}`;
|
|
|
3076
3088
|
}
|
|
3077
3089
|
console.log(`\n\x1b[32m✓ Plan created with ${tasksWithImages.length} tasks\x1b[0m\n`);
|
|
3078
3090
|
getLogger()?.info('Plan created and finalized', { taskCount: tasksWithImages.length });
|
|
3091
|
+
// Notify user that plan is ready for review
|
|
3092
|
+
await sendNotification({
|
|
3093
|
+
event: 'attention',
|
|
3094
|
+
title: 'Plan ready for review',
|
|
3095
|
+
detail: `${tasksWithImages.length} tasks created`,
|
|
3096
|
+
project: this.getProjectDisplayName(),
|
|
3097
|
+
});
|
|
3079
3098
|
}
|
|
3080
3099
|
catch (err) {
|
|
3081
3100
|
// Re-throw ExitRequestedError to propagate exit signal
|
|
@@ -3184,6 +3203,13 @@ ${detection.projectContext}`;
|
|
|
3184
3203
|
}
|
|
3185
3204
|
console.log(`\n\x1b[32m✓ Project initialized with ${tasksWithImages.length} tasks\x1b[0m\n`);
|
|
3186
3205
|
getLogger()?.info('Plan created and finalized', { taskCount: tasksWithImages.length });
|
|
3206
|
+
// Notify user that plan is ready for review
|
|
3207
|
+
await sendNotification({
|
|
3208
|
+
event: 'attention',
|
|
3209
|
+
title: 'Plan ready for review',
|
|
3210
|
+
detail: `${tasksWithImages.length} tasks created`,
|
|
3211
|
+
project: this.getProjectDisplayName(),
|
|
3212
|
+
});
|
|
3187
3213
|
}
|
|
3188
3214
|
catch (err) {
|
|
3189
3215
|
// Re-throw ExitRequestedError to propagate exit signal
|
|
@@ -3393,6 +3419,20 @@ ${detection.projectContext}`;
|
|
|
3393
3419
|
description: `${terminalInfo.name} - use /paste command instead`,
|
|
3394
3420
|
});
|
|
3395
3421
|
}
|
|
3422
|
+
// Sound notification toggle
|
|
3423
|
+
const soundEnabled = getSoundNotifications();
|
|
3424
|
+
menuItems.push({
|
|
3425
|
+
key: 'sound',
|
|
3426
|
+
label: `Sound notifications: ${soundEnabled ? '\x1b[32mON\x1b[0m' : '\x1b[90mOFF\x1b[0m'}`,
|
|
3427
|
+
description: 'Play a chime when execution completes or needs attention',
|
|
3428
|
+
});
|
|
3429
|
+
// Webhook notification
|
|
3430
|
+
const webhookUrl = getWebhookUrl();
|
|
3431
|
+
menuItems.push({
|
|
3432
|
+
key: 'webhook',
|
|
3433
|
+
label: `Webhook notifications: ${webhookUrl ? '\x1b[32mConfigured\x1b[0m' : '\x1b[90mOFF\x1b[0m'}`,
|
|
3434
|
+
description: webhookUrl ? 'Update or remove webhook URL' : 'Configure a webhook URL for notifications',
|
|
3435
|
+
});
|
|
3396
3436
|
menuItems.push({
|
|
3397
3437
|
key: 'back',
|
|
3398
3438
|
label: 'Back',
|
|
@@ -3529,8 +3569,107 @@ ${detection.projectContext}`;
|
|
|
3529
3569
|
// Show manual instructions for Cmd+V
|
|
3530
3570
|
await this.showCmdVInstructions();
|
|
3531
3571
|
}
|
|
3572
|
+
if (choice === 'sound') {
|
|
3573
|
+
const newValue = !soundEnabled;
|
|
3574
|
+
setSoundNotifications(newValue);
|
|
3575
|
+
// Loop redraws with updated value
|
|
3576
|
+
}
|
|
3577
|
+
if (choice === 'webhook') {
|
|
3578
|
+
await this.handleWebhookSettings();
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3581
|
+
}
|
|
3582
|
+
/**
|
|
3583
|
+
* Handle webhook URL configuration in settings
|
|
3584
|
+
*/
|
|
3585
|
+
async handleWebhookSettings() {
|
|
3586
|
+
const currentUrl = getWebhookUrl();
|
|
3587
|
+
if (currentUrl) {
|
|
3588
|
+
// Already configured — show sub-menu
|
|
3589
|
+
this.ui.clear();
|
|
3590
|
+
this.showHeader();
|
|
3591
|
+
console.log('\x1b[1m── Settings: Webhook Notifications ──\x1b[0m\n');
|
|
3592
|
+
console.log(`\x1b[90mCurrent URL: ${currentUrl}\x1b[0m\n`);
|
|
3593
|
+
const subMenuItems = [
|
|
3594
|
+
{ key: 'update', label: 'Update URL', description: 'Enter a new webhook URL' },
|
|
3595
|
+
{ key: 'test', label: 'Test notification', description: 'Send a test message to the webhook' },
|
|
3596
|
+
{ key: 'remove', label: 'Remove', description: 'Disable webhook notifications' },
|
|
3597
|
+
{ key: 'cancel', label: 'Cancel', description: 'Return to settings' },
|
|
3598
|
+
];
|
|
3599
|
+
const subChoice = await this.ui.quickPick(subMenuItems, 'Webhook options:', undefined, { showInput: false });
|
|
3600
|
+
if (subChoice === 'update') {
|
|
3601
|
+
await this.promptWebhookUrl();
|
|
3602
|
+
}
|
|
3603
|
+
else if (subChoice === 'test') {
|
|
3604
|
+
this.ui.clear();
|
|
3605
|
+
this.showHeader();
|
|
3606
|
+
console.log('\x1b[1m── Settings: Test Webhook ──\x1b[0m\n');
|
|
3607
|
+
console.log('Sending test notification...\n');
|
|
3608
|
+
const result = await testWebhook(currentUrl);
|
|
3609
|
+
if (result.success) {
|
|
3610
|
+
console.log('\x1b[32m✓ Test notification sent successfully!\x1b[0m');
|
|
3611
|
+
}
|
|
3612
|
+
else {
|
|
3613
|
+
console.log(`\x1b[31m✗ Failed: ${result.error}\x1b[0m`);
|
|
3614
|
+
}
|
|
3615
|
+
console.log('\n\x1b[90mPress any key to continue...\x1b[0m');
|
|
3616
|
+
await this.waitForKeypress();
|
|
3617
|
+
}
|
|
3618
|
+
else if (subChoice === 'remove') {
|
|
3619
|
+
setWebhookUrl(undefined);
|
|
3620
|
+
}
|
|
3621
|
+
}
|
|
3622
|
+
else {
|
|
3623
|
+
// Not configured — prompt for URL
|
|
3624
|
+
await this.promptWebhookUrl();
|
|
3532
3625
|
}
|
|
3533
3626
|
}
|
|
3627
|
+
/**
|
|
3628
|
+
* Prompt user to enter a webhook URL using InputBox
|
|
3629
|
+
*/
|
|
3630
|
+
async promptWebhookUrl() {
|
|
3631
|
+
return new Promise((resolve) => {
|
|
3632
|
+
const inputBox = new InputBox({
|
|
3633
|
+
placeholder: 'https://hooks.slack.com/services/...',
|
|
3634
|
+
hint: 'Enter to save · Esc to cancel',
|
|
3635
|
+
onRedraw: () => {
|
|
3636
|
+
this.ui.clear();
|
|
3637
|
+
this.showHeader();
|
|
3638
|
+
console.log('\x1b[1m── Settings: Webhook URL ──\x1b[0m\n');
|
|
3639
|
+
console.log('Enter your webhook URL for notifications.');
|
|
3640
|
+
console.log('Works with Slack, Discord, or any endpoint that accepts JSON POST.\n');
|
|
3641
|
+
console.log('\x1b[90mSlack: Workspace Settings → Manage Apps → Custom Integrations → Incoming WebHooks\x1b[0m\n');
|
|
3642
|
+
},
|
|
3643
|
+
onSubmit: (value) => {
|
|
3644
|
+
const url = value.trim();
|
|
3645
|
+
if (!url) {
|
|
3646
|
+
resolve();
|
|
3647
|
+
return;
|
|
3648
|
+
}
|
|
3649
|
+
if (!url.startsWith('https://')) {
|
|
3650
|
+
this.ui.clear();
|
|
3651
|
+
this.showHeader();
|
|
3652
|
+
console.log('\x1b[1m── Settings: Webhook URL ──\x1b[0m\n');
|
|
3653
|
+
console.log('\x1b[31m✗ URL must start with https://\x1b[0m');
|
|
3654
|
+
console.log('\n\x1b[90mPress any key to continue...\x1b[0m');
|
|
3655
|
+
this.waitForKeypress().then(() => resolve());
|
|
3656
|
+
return;
|
|
3657
|
+
}
|
|
3658
|
+
setWebhookUrl(url);
|
|
3659
|
+
this.ui.clear();
|
|
3660
|
+
this.showHeader();
|
|
3661
|
+
console.log('\x1b[1m── Settings ──\x1b[0m\n');
|
|
3662
|
+
console.log('\x1b[32m✓ Webhook URL saved!\x1b[0m');
|
|
3663
|
+
console.log('\n\x1b[90mPress any key to continue...\x1b[0m');
|
|
3664
|
+
this.waitForKeypress().then(() => resolve());
|
|
3665
|
+
},
|
|
3666
|
+
onCancel: () => {
|
|
3667
|
+
resolve();
|
|
3668
|
+
},
|
|
3669
|
+
});
|
|
3670
|
+
inputBox.start();
|
|
3671
|
+
});
|
|
3672
|
+
}
|
|
3534
3673
|
/**
|
|
3535
3674
|
* Show manual Cmd+V configuration instructions
|
|
3536
3675
|
*/
|
|
@@ -4292,8 +4431,54 @@ ${detection.projectContext}`;
|
|
|
4292
4431
|
getLogger()?.error('Execution failed with full error', { error: result.error });
|
|
4293
4432
|
}
|
|
4294
4433
|
}
|
|
4434
|
+
// Send notifications (sound + webhook)
|
|
4435
|
+
// Skip notification if no tasks were actually executed this run (all were already done)
|
|
4436
|
+
const tasksExecutedThisRun = result.completedTasks - alreadyCompleted;
|
|
4437
|
+
const project = this.getProjectDisplayName();
|
|
4438
|
+
if (result.success && tasksExecutedThisRun > 0) {
|
|
4439
|
+
await sendNotification({
|
|
4440
|
+
event: 'completed',
|
|
4441
|
+
title: 'All tasks completed',
|
|
4442
|
+
detail: `${result.completedTasks}/${result.totalTasks} tasks completed successfully`,
|
|
4443
|
+
project,
|
|
4444
|
+
});
|
|
4445
|
+
}
|
|
4446
|
+
else if (result.blockedTask) {
|
|
4447
|
+
await sendNotification({
|
|
4448
|
+
event: 'attention',
|
|
4449
|
+
title: `Task failed: ${result.blockedTask.title}`,
|
|
4450
|
+
detail: result.error ? truncateError(result.error) : undefined,
|
|
4451
|
+
project,
|
|
4452
|
+
});
|
|
4453
|
+
}
|
|
4454
|
+
else if (!this.shouldStop) {
|
|
4455
|
+
// User-initiated pause doesn't need a notification
|
|
4456
|
+
await sendNotification({
|
|
4457
|
+
event: 'attention',
|
|
4458
|
+
title: 'Execution failed',
|
|
4459
|
+
detail: result.error ? truncateError(result.error) : undefined,
|
|
4460
|
+
project,
|
|
4461
|
+
});
|
|
4462
|
+
}
|
|
4295
4463
|
// Clear cached orchestrator
|
|
4296
4464
|
this.orchestrator = null;
|
|
4297
4465
|
}
|
|
4466
|
+
/**
|
|
4467
|
+
* Get a display name for the project (folder name, or "projectName (folder)" if they differ)
|
|
4468
|
+
*/
|
|
4469
|
+
getProjectDisplayName() {
|
|
4470
|
+
const folderName = basename(this.projectPath);
|
|
4471
|
+
try {
|
|
4472
|
+
const orchestrator = this.getOrchestrator();
|
|
4473
|
+
const projectName = orchestrator.getProjectName();
|
|
4474
|
+
if (projectName && projectName !== folderName) {
|
|
4475
|
+
return `${projectName} (${folderName})`;
|
|
4476
|
+
}
|
|
4477
|
+
}
|
|
4478
|
+
catch {
|
|
4479
|
+
// Orchestrator may not be loaded yet
|
|
4480
|
+
}
|
|
4481
|
+
return folderName;
|
|
4482
|
+
}
|
|
4298
4483
|
}
|
|
4299
4484
|
//# sourceMappingURL=app.js.map
|