maistro 1.0.422 → 1.0.448
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.map +1 -1
- package/dist/app.js +133 -21
- package/dist/app.js.map +1 -1
- package/dist/caffeinate.js +4 -4
- package/dist/caffeinate.js.map +1 -1
- package/dist/claudePath.d.ts +5 -0
- package/dist/claudePath.d.ts.map +1 -1
- package/dist/claudePath.js +8 -0
- package/dist/claudePath.js.map +1 -1
- package/dist/config.d.ts +0 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +65 -140
- package/dist/config.js.map +1 -1
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +3 -8
- package/dist/executor.js.map +1 -1
- package/dist/git.d.ts +0 -6
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +17 -43
- package/dist/git.js.map +1 -1
- package/dist/logger.d.ts +2 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +23 -5
- package/dist/logger.js.map +1 -1
- package/dist/logo.d.ts.map +1 -1
- package/dist/logo.js +26 -1
- package/dist/logo.js.map +1 -1
- package/dist/planner.d.ts +2 -0
- package/dist/planner.d.ts.map +1 -1
- package/dist/planner.js +198 -134
- package/dist/planner.js.map +1 -1
- package/dist/screen.d.ts.map +1 -1
- package/dist/screen.js +8 -1
- package/dist/screen.js.map +1 -1
- package/dist/ui.d.ts +1 -1
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +13 -1
- package/dist/ui.js.map +1 -1
- package/dist/validator.d.ts.map +1 -1
- package/dist/validator.js +6 -8
- package/dist/validator.js.map +1 -1
- package/package.json +1 -1
package/dist/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAiOA,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;IAyG5B;;;OAGG;YACW,iBAAiB;IA8D/B;;OAEG;IACH,OAAO,CAAC,kBAAkB;
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAiOA,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;IAyG5B;;;OAGG;YACW,iBAAiB;IA8D/B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAuC1B;;;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;IAyH7B;;;OAGG;YACW,uBAAuB;IAyErC;;OAEG;YACW,4BAA4B;IAiE1C;;OAEG;YACW,oBAAoB;IAgElC;;;;OAIG;YACW,yBAAyB;IAgIvC;;;OAGG;YACW,qBAAqB;IAmHnC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAW9B;;OAEG;YACW,mBAAmB;IA0BjC;;OAEG;YACW,qBAAqB;IAiDnC;;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;IAqT1B;;;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;IAwPpC;;;;OAIG;YACW,sBAAsB;YA0GtB,UAAU;IA4HxB,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;IAoB3B;;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"}
|
package/dist/app.js
CHANGED
|
@@ -13,7 +13,7 @@ import { VERSION } from './version.js';
|
|
|
13
13
|
import { buildHeaderWithLogo } from './logo.js';
|
|
14
14
|
import { addImageFromFile, addImageFromUrl, addImageFromPaste, getImages, deleteImage, looksLikeImagePath, looksLikeImageUrl, formatImageSize, sanitizeImageId, generatePasteImageId, extractImageUrls, processDescriptionImageUrls, } from './imageManager.js';
|
|
15
15
|
import { InputBox, getDisplayWidth, charIndexAtDisplayColumn } from './inputBox.js';
|
|
16
|
-
import { startCaffeinate, stopCaffeinate, isCaffeinateAvailable } from './caffeinate.js';
|
|
16
|
+
import { startCaffeinate, stopCaffeinate, isCaffeinateAvailable, isCaffeinateRunning } from './caffeinate.js';
|
|
17
17
|
import { getPreventSleep, setPreventSleep, getConfigPath, detectTerminal, getShiftEnterEnabled, setShiftEnterEnabled, configureTerminalShiftEnter, removeTerminalShiftEnter, getNewlineHint, getCmdVPasteEnabled, setCmdVPasteEnabled, configureTerminalCmdVPaste, removeTerminalCmdVPaste, getCmdVPasteInstructions, } from './config.js';
|
|
18
18
|
import { hasClipboardImage, saveClipboardImage, cleanupTempImage } from './clipboard.js';
|
|
19
19
|
import { isGitRepo, ensureEnvInGitignore } from './git.js';
|
|
@@ -371,6 +371,8 @@ export class MaistroApp {
|
|
|
371
371
|
restartAfterUpdate() {
|
|
372
372
|
console.log(`\x1b[32m✓ Updated successfully!\x1b[0m`);
|
|
373
373
|
console.log(`\x1b[90mRestarting maistro...\x1b[0m\n`);
|
|
374
|
+
// Ensure terminal is in a clean state before spawning
|
|
375
|
+
this.ui.disableRawMode();
|
|
374
376
|
// Get the original command arguments (skip node and script path)
|
|
375
377
|
const args = process.argv.slice(2);
|
|
376
378
|
// Spawn new maistro process with same arguments
|
|
@@ -384,9 +386,13 @@ export class MaistroApp {
|
|
|
384
386
|
MAISTRO_SKIP_UPDATE_CHECK: '1',
|
|
385
387
|
},
|
|
386
388
|
});
|
|
387
|
-
// Exit current process
|
|
389
|
+
// Exit current process after a delay to let child fully attach to terminal
|
|
390
|
+
// The 'spawn' event fires early, before stdio is fully inherited.
|
|
391
|
+
// Exiting too quickly can cause EIO errors when the child tries to use stdin.
|
|
388
392
|
child.on('spawn', () => {
|
|
389
|
-
|
|
393
|
+
setTimeout(() => {
|
|
394
|
+
process.exit(0);
|
|
395
|
+
}, 200);
|
|
390
396
|
});
|
|
391
397
|
// If spawn fails, exit with error
|
|
392
398
|
child.on('error', (err) => {
|
|
@@ -1308,7 +1314,7 @@ export class MaistroApp {
|
|
|
1308
1314
|
if (this.executionState?.isExecuting) {
|
|
1309
1315
|
this.renderExecutionSection(termWidth);
|
|
1310
1316
|
}
|
|
1311
|
-
// Show progress bar with total duration
|
|
1317
|
+
// Show progress bar with total duration and caffeinate indicator
|
|
1312
1318
|
// Calculate total duration: completed tasks + current task elapsed (if executing)
|
|
1313
1319
|
const completedDurationMs = tasks.reduce((sum, t) => sum + (t.durationMs || 0), 0);
|
|
1314
1320
|
const currentTaskElapsedMs = this.executionState?.isExecuting
|
|
@@ -1316,7 +1322,8 @@ export class MaistroApp {
|
|
|
1316
1322
|
: 0;
|
|
1317
1323
|
const totalDurationMs = completedDurationMs + currentTaskElapsedMs;
|
|
1318
1324
|
const durationStr = totalDurationMs > 0 ? ` • ${this.formatDuration(totalDurationMs)}` : '';
|
|
1319
|
-
|
|
1325
|
+
const mainCaffeinateIndicator = isCaffeinateRunning() ? ' \x1b[33m☕ caffeinating\x1b[0m' : '';
|
|
1326
|
+
console.log(`${progressBar(status.tasks.completed, status.tasks.total, 30, status.tasks.skipped)}${durationStr}${mainCaffeinateIndicator}\n`);
|
|
1320
1327
|
// Calculate viewport for tasks
|
|
1321
1328
|
const termRows = process.stdout.rows || 24;
|
|
1322
1329
|
// Fixed overhead: header(~7) + progress(2) + position indicator(1) + failed section(variable) + commands(2) + input(4 base) + margins
|
|
@@ -1356,7 +1363,7 @@ export class MaistroApp {
|
|
|
1356
1363
|
}
|
|
1357
1364
|
// Get visible slice of tasks
|
|
1358
1365
|
const visibleTasks = tasks.slice(this.taskScrollOffset, this.taskScrollOffset + maxVisibleTasks);
|
|
1359
|
-
this.displayTasks(visibleTasks, this.taskScrollOffset, selectedIndex, tasks);
|
|
1366
|
+
this.displayTasks(visibleTasks, this.taskScrollOffset, selectedIndex, tasks, this.executionState?.phase);
|
|
1360
1367
|
// Show scroll down indicator
|
|
1361
1368
|
const belowCount = totalTasks - this.taskScrollOffset - visibleTasks.length;
|
|
1362
1369
|
if (belowCount > 0) {
|
|
@@ -1485,12 +1492,31 @@ export class MaistroApp {
|
|
|
1485
1492
|
const availableRows = Math.max(6, termRows - fixedOverhead);
|
|
1486
1493
|
const maxVisibleTasks = Math.min(UI.MAX_VISIBLE_TASKS, Math.max(UI.MIN_VISIBLE_TASKS, Math.floor(availableRows / UI.LINES_PER_TASK)));
|
|
1487
1494
|
// Auto-scroll to first relevant task if no manual selection yet
|
|
1488
|
-
// Priority:
|
|
1495
|
+
// Priority: last completed task (to show summary) > first pending task > last task
|
|
1489
1496
|
const executingTaskIndex = tasks.findIndex(t => t.status === 'in_progress');
|
|
1490
1497
|
const firstPendingIndex = tasks.findIndex(t => t.status === 'pending');
|
|
1491
1498
|
if (this.selectedTaskIndex === -1) {
|
|
1492
|
-
if (executingTaskIndex
|
|
1493
|
-
|
|
1499
|
+
if (executingTaskIndex > 0) {
|
|
1500
|
+
// When a task is executing, select the last completed task before it
|
|
1501
|
+
// This allows users to see the achievement summary
|
|
1502
|
+
let lastCompletedIndex = -1;
|
|
1503
|
+
for (let i = executingTaskIndex - 1; i >= 0; i--) {
|
|
1504
|
+
if (tasks[i].status === 'completed') {
|
|
1505
|
+
lastCompletedIndex = i;
|
|
1506
|
+
break;
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
if (lastCompletedIndex >= 0) {
|
|
1510
|
+
this.selectedTaskIndex = lastCompletedIndex;
|
|
1511
|
+
}
|
|
1512
|
+
else {
|
|
1513
|
+
// No completed task before executing one, select executing task
|
|
1514
|
+
this.selectedTaskIndex = executingTaskIndex;
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
else if (executingTaskIndex === 0) {
|
|
1518
|
+
// First task is executing, select it
|
|
1519
|
+
this.selectedTaskIndex = 0;
|
|
1494
1520
|
}
|
|
1495
1521
|
else if (firstPendingIndex >= 0) {
|
|
1496
1522
|
// Default to first pending task - shows what's next to be done
|
|
@@ -2010,6 +2036,16 @@ export class MaistroApp {
|
|
|
2010
2036
|
}, 50);
|
|
2011
2037
|
return;
|
|
2012
2038
|
}
|
|
2039
|
+
// Partial CSI sequence (ESC [) - wait for final character(s)
|
|
2040
|
+
// This handles cases where escape sequences arrive character-by-character (e.g., in PTY tests)
|
|
2041
|
+
if (key === '\x1b[' || (key.startsWith('\x1b[') && key.length < 4 && !/[A-Za-z~]$/.test(key))) {
|
|
2042
|
+
escapeBuffer = key;
|
|
2043
|
+
escapeTimeout = setTimeout(() => {
|
|
2044
|
+
// Timeout - discard partial sequence
|
|
2045
|
+
escapeBuffer = '';
|
|
2046
|
+
}, 50);
|
|
2047
|
+
return;
|
|
2048
|
+
}
|
|
2013
2049
|
// Clear escape pending states on any other input
|
|
2014
2050
|
if ((escPendingClear || escPendingPause) && key !== '\x1b') {
|
|
2015
2051
|
if (escTimeout) {
|
|
@@ -3156,6 +3192,30 @@ export class MaistroApp {
|
|
|
3156
3192
|
if (!hasOutput && result.response) {
|
|
3157
3193
|
console.log(`${result.response}\n`);
|
|
3158
3194
|
}
|
|
3195
|
+
// Check for structured questions first (from new JSON format)
|
|
3196
|
+
if (result.questions && result.questions.length > 0) {
|
|
3197
|
+
// Use the existing discovery question UI to present questions
|
|
3198
|
+
const answers = {};
|
|
3199
|
+
const totalQuestions = result.questions.length;
|
|
3200
|
+
for (let i = 0; i < result.questions.length; i++) {
|
|
3201
|
+
const question = result.questions[i];
|
|
3202
|
+
const answer = await this.askDiscoveryQuestionRequired(question, i + 1, totalQuestions, '' // No summary needed for plan refinement
|
|
3203
|
+
);
|
|
3204
|
+
if (answer === null) {
|
|
3205
|
+
// User cancelled - return to main view
|
|
3206
|
+
return null;
|
|
3207
|
+
}
|
|
3208
|
+
answers[question.id] = answer;
|
|
3209
|
+
}
|
|
3210
|
+
// Format answers as full text and continue conversation
|
|
3211
|
+
const answerText = Object.entries(answers)
|
|
3212
|
+
.map(([id, value]) => {
|
|
3213
|
+
const q = result.questions.find(q => q.id === id);
|
|
3214
|
+
return `${q?.question || id}: ${value}`;
|
|
3215
|
+
})
|
|
3216
|
+
.join('\n');
|
|
3217
|
+
return this.handleInlineDiscussion(answerText, currentTasks, goal);
|
|
3218
|
+
}
|
|
3159
3219
|
// Return updated tasks if any (or null if no changes)
|
|
3160
3220
|
if (result.updatedTasks && result.updatedTasks.length > 0) {
|
|
3161
3221
|
// Process any image URLs in the updated task descriptions
|
|
@@ -3172,8 +3232,28 @@ export class MaistroApp {
|
|
|
3172
3232
|
await new Promise(r => setTimeout(r, 500));
|
|
3173
3233
|
return tasksWithImages;
|
|
3174
3234
|
}
|
|
3175
|
-
// No task changes -
|
|
3176
|
-
|
|
3235
|
+
// No task changes and no structured questions - allow text input for follow-up
|
|
3236
|
+
if (hasOutput && result.response) {
|
|
3237
|
+
// Fall back to text input
|
|
3238
|
+
console.log(''); // Add spacing
|
|
3239
|
+
const followUp = await this.ui.promptWithEscape('\x1b[90mReply to continue discussion (Enter to go back):\x1b[0m ', '');
|
|
3240
|
+
if (followUp && followUp.trim()) {
|
|
3241
|
+
// User wants to continue the conversation - recursively call with follow-up
|
|
3242
|
+
return this.handleInlineDiscussion(followUp.trim(), currentTasks, goal);
|
|
3243
|
+
}
|
|
3244
|
+
// Empty input or Escape - return to main view
|
|
3245
|
+
}
|
|
3246
|
+
else if (hasOutput) {
|
|
3247
|
+
// No result.response available - simple text input fallback
|
|
3248
|
+
console.log(''); // Add spacing
|
|
3249
|
+
const followUp = await this.ui.promptWithEscape('\x1b[90mReply to continue discussion (Enter to go back):\x1b[0m ', '');
|
|
3250
|
+
if (followUp && followUp.trim()) {
|
|
3251
|
+
return this.handleInlineDiscussion(followUp.trim(), currentTasks, goal);
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3254
|
+
else {
|
|
3255
|
+
await new Promise(r => setTimeout(r, 300));
|
|
3256
|
+
}
|
|
3177
3257
|
return null;
|
|
3178
3258
|
}
|
|
3179
3259
|
/**
|
|
@@ -3377,7 +3457,7 @@ ${detection.projectContext}`;
|
|
|
3377
3457
|
getLogger()?.error('handleInit error', { error: String(err), stack: err instanceof Error ? err.stack : undefined });
|
|
3378
3458
|
}
|
|
3379
3459
|
}
|
|
3380
|
-
displayTasks(tasks, startIndex = 0, selectedIndex = -1, allTasks) {
|
|
3460
|
+
displayTasks(tasks, startIndex = 0, selectedIndex = -1, allTasks, executionPhase) {
|
|
3381
3461
|
const termWidth = process.stdout.columns || 80;
|
|
3382
3462
|
// Helper to get visible length (strip ANSI codes)
|
|
3383
3463
|
const visibleLength = (s) => s.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
@@ -3389,15 +3469,27 @@ ${detection.projectContext}`;
|
|
|
3389
3469
|
? formatBlockedStatus()
|
|
3390
3470
|
: formatTaskStatusIcon(task.status, task.failureType);
|
|
3391
3471
|
// Format status name in brackets
|
|
3472
|
+
// For in_progress tasks, show the current phase (Implementation/Validation/Committing)
|
|
3473
|
+
// Default to 'executing' (Implementation) if no phase is provided but task is in_progress
|
|
3474
|
+
const phaseForStatus = task.status === 'in_progress' ? (executionPhase ?? 'executing') : undefined;
|
|
3392
3475
|
const statusBracket = blocked
|
|
3393
3476
|
? ' \x1b[31m[Blocked]\x1b[0m'
|
|
3394
|
-
: ` [${formatStatusName(task.status, task.failureType)}]`;
|
|
3477
|
+
: ` [${formatStatusName(task.status, task.failureType, phaseForStatus)}]`;
|
|
3478
|
+
// Format task duration for completed and in_progress tasks
|
|
3479
|
+
let durationStr = '';
|
|
3480
|
+
if (task.status === 'completed' && task.durationMs) {
|
|
3481
|
+
durationStr = ` \x1b[90m${this.formatDuration(task.durationMs)}\x1b[0m`;
|
|
3482
|
+
}
|
|
3483
|
+
else if (task.status === 'in_progress' && this.executionState?.taskStartTime) {
|
|
3484
|
+
const elapsed = Date.now() - this.executionState.taskStartTime;
|
|
3485
|
+
durationStr = ` \x1b[36m${this.formatDuration(elapsed)}\x1b[0m`;
|
|
3486
|
+
}
|
|
3395
3487
|
const taskNum = startIndex + i + 1;
|
|
3396
3488
|
const isSelected = (startIndex + i) === selectedIndex;
|
|
3397
3489
|
const cursor = isSelected ? '\x1b[36m▶\x1b[0m ' : ' ';
|
|
3398
3490
|
// Build line template and measure total visible length
|
|
3399
3491
|
const prefix = `${cursor}${statusIcon} ${taskNum}. `;
|
|
3400
|
-
const suffix = statusBracket;
|
|
3492
|
+
const suffix = statusBracket + durationStr;
|
|
3401
3493
|
const prefixLen = visibleLength(prefix);
|
|
3402
3494
|
const suffixLen = visibleLength(suffix);
|
|
3403
3495
|
// Calculate max title length (leave 1 char margin for safety)
|
|
@@ -3410,10 +3502,10 @@ ${detection.projectContext}`;
|
|
|
3410
3502
|
}
|
|
3411
3503
|
// Output the line
|
|
3412
3504
|
if (isSelected) {
|
|
3413
|
-
console.log(`${cursor}${statusIcon} \x1b[1;36m${taskNum}.\x1b[0m \x1b[1;36m${title}\x1b[0m${statusBracket}`);
|
|
3505
|
+
console.log(`${cursor}${statusIcon} \x1b[1;36m${taskNum}.\x1b[0m \x1b[1;36m${title}\x1b[0m${statusBracket}${durationStr}`);
|
|
3414
3506
|
}
|
|
3415
3507
|
else {
|
|
3416
|
-
console.log(`${cursor}${statusIcon} \x1b[1m${taskNum}.\x1b[0m ${title}${statusBracket}`);
|
|
3508
|
+
console.log(`${cursor}${statusIcon} \x1b[1m${taskNum}.\x1b[0m ${title}${statusBracket}${durationStr}`);
|
|
3417
3509
|
}
|
|
3418
3510
|
// Show 5-line result summary preview for selected completed tasks
|
|
3419
3511
|
if (isSelected && task.status === 'completed' && task.resultSummary) {
|
|
@@ -3938,7 +4030,7 @@ ${detection.projectContext}`;
|
|
|
3938
4030
|
validating: '\x1b[33mValidating\x1b[0m',
|
|
3939
4031
|
committing: '\x1b[32mCommitting\x1b[0m',
|
|
3940
4032
|
}[this.executionState.phase];
|
|
3941
|
-
console.log(`${taskBar}
|
|
4033
|
+
console.log(`${taskBar} 📋 \x1b[1mTask ${this.executionState.currentTaskIndex + 1}\x1b[0m ${phaseLabel} \x1b[90m(${taskElapsed})\x1b[0m`);
|
|
3942
4034
|
}
|
|
3943
4035
|
}
|
|
3944
4036
|
// ==================== END EXECUTION STATE MANAGEMENT ====================
|
|
@@ -4130,11 +4222,31 @@ ${detection.projectContext}`;
|
|
|
4130
4222
|
return;
|
|
4131
4223
|
const failed = tasks.filter(t => t.status === 'failed');
|
|
4132
4224
|
const allDone = tasks.every(t => t.status === 'completed' || t.status === 'skipped');
|
|
4133
|
-
// Update selected task to
|
|
4225
|
+
// Update selected task to last completed task (only if user hasn't navigated)
|
|
4226
|
+
// This shows the achievement summary for the most recently completed task
|
|
4227
|
+
// while the next task is executing
|
|
4134
4228
|
if (!userNavigated) {
|
|
4135
|
-
const
|
|
4136
|
-
if (
|
|
4137
|
-
|
|
4229
|
+
const executingTaskIndex = tasks.findIndex(t => t.status === 'in_progress');
|
|
4230
|
+
if (executingTaskIndex > 0) {
|
|
4231
|
+
// Find the last completed task before the executing one
|
|
4232
|
+
let lastCompletedIndex = -1;
|
|
4233
|
+
for (let i = executingTaskIndex - 1; i >= 0; i--) {
|
|
4234
|
+
if (tasks[i].status === 'completed') {
|
|
4235
|
+
lastCompletedIndex = i;
|
|
4236
|
+
break;
|
|
4237
|
+
}
|
|
4238
|
+
}
|
|
4239
|
+
if (lastCompletedIndex >= 0) {
|
|
4240
|
+
this.selectedTaskIndex = lastCompletedIndex;
|
|
4241
|
+
}
|
|
4242
|
+
else {
|
|
4243
|
+
// No completed task before executing one, select the executing task
|
|
4244
|
+
this.selectedTaskIndex = executingTaskIndex;
|
|
4245
|
+
}
|
|
4246
|
+
}
|
|
4247
|
+
else if (executingTaskIndex === 0) {
|
|
4248
|
+
// First task is executing, select it
|
|
4249
|
+
this.selectedTaskIndex = 0;
|
|
4138
4250
|
}
|
|
4139
4251
|
}
|
|
4140
4252
|
// Clear and render
|