maistro 1.2.18 → 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 +12 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +195 -15
- 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/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;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 {
|
|
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';
|
|
@@ -703,19 +704,6 @@ export class MaistroApp {
|
|
|
703
704
|
* Uses full-screen approach for each step
|
|
704
705
|
*/
|
|
705
706
|
async handleDiscovery(goal) {
|
|
706
|
-
// Step 1: Ask if user wants discovery (full-screen)
|
|
707
|
-
const skipChoice = await this.fullScreenMenu({
|
|
708
|
-
title: 'Project Discovery',
|
|
709
|
-
subtitle: 'Would you like to answer a few questions to refine your project plan?',
|
|
710
|
-
items: [
|
|
711
|
-
{ key: 'd', label: 'Discover', description: 'Answer questions to refine the plan (Recommended)' },
|
|
712
|
-
{ key: 's', label: 'Skip', description: 'Go straight to task planning' },
|
|
713
|
-
],
|
|
714
|
-
exitOnDoubleEscape: true, // Allow exit via double-escape during discovery
|
|
715
|
-
});
|
|
716
|
-
if (skipChoice === 's' || skipChoice === null) {
|
|
717
|
-
return null; // Skip discovery
|
|
718
|
-
}
|
|
719
707
|
// Show analyzing screen with spinner (with escape-to-exit support)
|
|
720
708
|
setStatusState('working');
|
|
721
709
|
this.ui.clear();
|
|
@@ -755,6 +743,13 @@ export class MaistroApp {
|
|
|
755
743
|
questions: result.questions.map(q => ({ id: q.id, question: q.question, options: q.options })),
|
|
756
744
|
assumptions: result.assumptions,
|
|
757
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
|
+
});
|
|
758
753
|
// Ask each question one at a time (full-screen for each)
|
|
759
754
|
const answers = {};
|
|
760
755
|
for (let i = 0; i < result.questions.length; i++) {
|
|
@@ -2824,6 +2819,14 @@ export class MaistroApp {
|
|
|
2824
2819
|
this.ui.clear();
|
|
2825
2820
|
this.showHeader();
|
|
2826
2821
|
console.log(`\x1b[1m── Discussion ──\x1b[0m\n\x1b[90mYou: ${userInput.length > 100 ? userInput.slice(0, 97) + '...' : userInput}\x1b[0m\n`);
|
|
2822
|
+
// Run discovery phase for the new request (same as initial project setup)
|
|
2823
|
+
// This detects external dependencies, asks clarifying questions, and prompts for secrets
|
|
2824
|
+
const discoveryResult = await this.handleDiscovery(userInput);
|
|
2825
|
+
let discoveryContext = '';
|
|
2826
|
+
if (discoveryResult && discoveryResult.confirmed) {
|
|
2827
|
+
const context = buildProjectContext(userInput, discoveryResult);
|
|
2828
|
+
discoveryContext = formatContextForDecomposition(context);
|
|
2829
|
+
}
|
|
2827
2830
|
let hasOutput = false;
|
|
2828
2831
|
// Set up abort controller for cancellation
|
|
2829
2832
|
const abortController = new AbortController();
|
|
@@ -2881,9 +2884,13 @@ export class MaistroApp {
|
|
|
2881
2884
|
}
|
|
2882
2885
|
};
|
|
2883
2886
|
process.stdin.on('data', handleCancelKey);
|
|
2887
|
+
// Include discovery context in the planning request if available
|
|
2888
|
+
const enrichedMessage = discoveryContext
|
|
2889
|
+
? `${userInput}\n\nDiscovery Context:\n${discoveryContext}`
|
|
2890
|
+
: userInput;
|
|
2884
2891
|
const result = await planTasks({
|
|
2885
2892
|
goal,
|
|
2886
|
-
userMessage:
|
|
2893
|
+
userMessage: enrichedMessage,
|
|
2887
2894
|
currentTasks,
|
|
2888
2895
|
conversationHistory: [],
|
|
2889
2896
|
cwd: this.projectPath,
|
|
@@ -3077,6 +3084,13 @@ ${detection.projectContext}`;
|
|
|
3077
3084
|
}
|
|
3078
3085
|
console.log(`\n\x1b[32m✓ Plan created with ${tasksWithImages.length} tasks\x1b[0m\n`);
|
|
3079
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
|
+
});
|
|
3080
3094
|
}
|
|
3081
3095
|
catch (err) {
|
|
3082
3096
|
// Re-throw ExitRequestedError to propagate exit signal
|
|
@@ -3185,6 +3199,13 @@ ${detection.projectContext}`;
|
|
|
3185
3199
|
}
|
|
3186
3200
|
console.log(`\n\x1b[32m✓ Project initialized with ${tasksWithImages.length} tasks\x1b[0m\n`);
|
|
3187
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
|
+
});
|
|
3188
3209
|
}
|
|
3189
3210
|
catch (err) {
|
|
3190
3211
|
// Re-throw ExitRequestedError to propagate exit signal
|
|
@@ -3394,6 +3415,20 @@ ${detection.projectContext}`;
|
|
|
3394
3415
|
description: `${terminalInfo.name} - use /paste command instead`,
|
|
3395
3416
|
});
|
|
3396
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
|
+
});
|
|
3397
3432
|
menuItems.push({
|
|
3398
3433
|
key: 'back',
|
|
3399
3434
|
label: 'Back',
|
|
@@ -3530,8 +3565,107 @@ ${detection.projectContext}`;
|
|
|
3530
3565
|
// Show manual instructions for Cmd+V
|
|
3531
3566
|
await this.showCmdVInstructions();
|
|
3532
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();
|
|
3533
3621
|
}
|
|
3534
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
|
+
}
|
|
3535
3669
|
/**
|
|
3536
3670
|
* Show manual Cmd+V configuration instructions
|
|
3537
3671
|
*/
|
|
@@ -4293,8 +4427,54 @@ ${detection.projectContext}`;
|
|
|
4293
4427
|
getLogger()?.error('Execution failed with full error', { error: result.error });
|
|
4294
4428
|
}
|
|
4295
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
|
+
}
|
|
4296
4459
|
// Clear cached orchestrator
|
|
4297
4460
|
this.orchestrator = null;
|
|
4298
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
|
+
}
|
|
4299
4479
|
}
|
|
4300
4480
|
//# sourceMappingURL=app.js.map
|