taskplane 0.7.1 → 0.8.0
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/dashboard/public/app.js +43 -18
- package/extensions/task-runner.ts +346 -127
- package/extensions/taskplane/config-loader.ts +1 -1
- package/extensions/taskplane/config-schema.ts +7 -5
- package/extensions/taskplane/engine.ts +6 -6
- package/extensions/taskplane/extension.ts +207 -5
- package/extensions/taskplane/merge.ts +21 -21
- package/extensions/taskplane/messages.ts +4 -4
- package/extensions/taskplane/resume.ts +6 -6
- package/extensions/taskplane/supervisor.ts +141 -9
- package/extensions/taskplane/types.ts +4 -4
- package/extensions/taskplane/worktree.ts +13 -0
- package/package.json +1 -1
- package/templates/agents/local/task-reviewer.md +1 -1
- package/templates/agents/local/task-worker.md +4 -3
- package/templates/agents/task-merger.md +215 -215
- package/templates/agents/task-reviewer.md +1 -1
- package/templates/agents/task-worker.md +18 -13
- package/templates/config/task-orchestrator.yaml +1 -1
- package/templates/config/task-runner.yaml +3 -3
package/dashboard/public/app.js
CHANGED
|
@@ -17,9 +17,11 @@ function formatDuration(ms) {
|
|
|
17
17
|
return `${m}m ${String(s).padStart(2, "0")}s`;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
function relativeTime(
|
|
21
|
-
if (!
|
|
22
|
-
const
|
|
20
|
+
function relativeTime(epochOrIso) {
|
|
21
|
+
if (!epochOrIso) return "";
|
|
22
|
+
const ts = typeof epochOrIso === "string" ? new Date(epochOrIso).getTime() : epochOrIso;
|
|
23
|
+
if (isNaN(ts)) return "";
|
|
24
|
+
const diff = Date.now() - ts;
|
|
23
25
|
if (diff < 60000) return `${Math.floor(diff / 1000)}s ago`;
|
|
24
26
|
if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;
|
|
25
27
|
return `${Math.floor(diff / 3600000)}h ago`;
|
|
@@ -308,9 +310,17 @@ function renderSummary(batch) {
|
|
|
308
310
|
let batchChecked = 0, batchTotal = 0;
|
|
309
311
|
const waveStats = wavePlan.map((taskIds, waveIdx) => {
|
|
310
312
|
let wChecked = 0, wTotal = 0;
|
|
313
|
+
let allSucceeded = taskIds.length > 0;
|
|
311
314
|
for (const tid of taskIds) {
|
|
312
315
|
const t = taskMap.get(tid);
|
|
313
|
-
if (t
|
|
316
|
+
if (!t || t.status !== "succeeded") allSucceeded = false;
|
|
317
|
+
if (t && t.status === "succeeded" && t.statusData) {
|
|
318
|
+
// Succeeded task with statusData: count as fully done even if
|
|
319
|
+
// STATUS.md checkboxes weren't all ticked before .DONE was created
|
|
320
|
+
const total = t.statusData.total || 1;
|
|
321
|
+
wChecked += total;
|
|
322
|
+
wTotal += total;
|
|
323
|
+
} else if (t && t.statusData) {
|
|
314
324
|
wChecked += t.statusData.checked || 0;
|
|
315
325
|
wTotal += t.statusData.total || 0;
|
|
316
326
|
} else if (t && t.status === "succeeded") {
|
|
@@ -322,7 +332,7 @@ function renderSummary(batch) {
|
|
|
322
332
|
}
|
|
323
333
|
batchChecked += wChecked;
|
|
324
334
|
batchTotal += wTotal;
|
|
325
|
-
return { waveIdx, taskIds, checked: wChecked, total: wTotal };
|
|
335
|
+
return { waveIdx, taskIds, checked: wChecked, total: wTotal, allSucceeded };
|
|
326
336
|
});
|
|
327
337
|
|
|
328
338
|
const overallPct = batchTotal > 0 ? Math.round((batchChecked / batchTotal) * 100) : 0;
|
|
@@ -333,15 +343,19 @@ function renderSummary(batch) {
|
|
|
333
343
|
for (const ws of waveStats) {
|
|
334
344
|
const segWidthPct = batchTotal > 0 ? (ws.total / batchTotal) * 100 : (100 / waveStats.length);
|
|
335
345
|
const fillPct = ws.total > 0 ? (ws.checked / ws.total) * 100 : 0;
|
|
336
|
-
const
|
|
337
|
-
const
|
|
346
|
+
const checkboxDone = ws.checked === ws.total && ws.total > 0;
|
|
347
|
+
const pastWave = ws.waveIdx < currentWaveIdx;
|
|
348
|
+
const batchDone = batch.phase === "completed" || batch.phase === "merging";
|
|
349
|
+
const isDone = checkboxDone || pastWave || batchDone || ws.allSucceeded;
|
|
350
|
+
const isCurrent = ws.waveIdx === currentWaveIdx && (batch.phase === "executing" || batch.phase === "merging");
|
|
338
351
|
const isFuture = ws.waveIdx > currentWaveIdx && batch.phase === "executing";
|
|
339
352
|
|
|
340
353
|
const fillClass = isDone ? "pct-hi" : fillPct > 50 ? "pct-mid" : fillPct > 0 ? "pct-low" : "pct-0";
|
|
354
|
+
const fillWidth = isDone ? 100 : fillPct;
|
|
341
355
|
const segClass = isCurrent ? "wave-seg-current" : isFuture ? "wave-seg-future" : "";
|
|
342
356
|
|
|
343
357
|
barHtml += `<div class="wave-seg ${segClass}" style="width:${segWidthPct.toFixed(1)}%" title="W${ws.waveIdx + 1}: ${ws.checked}/${ws.total} checkboxes (${ws.taskIds.join(', ')})">`;
|
|
344
|
-
barHtml += ` <div class="wave-seg-fill ${fillClass}" style="width:${
|
|
358
|
+
barHtml += ` <div class="wave-seg-fill ${fillClass}" style="width:${fillWidth.toFixed(1)}%"></div>`;
|
|
345
359
|
barHtml += ` <span class="wave-seg-label">W${ws.waveIdx + 1}</span>`;
|
|
346
360
|
barHtml += `</div>`;
|
|
347
361
|
}
|
|
@@ -699,17 +713,16 @@ function renderNoBatch() {
|
|
|
699
713
|
if ($mergePanel) $mergePanel.style.display = "none";
|
|
700
714
|
if ($errorsPanel) $errorsPanel.style.display = "none";
|
|
701
715
|
|
|
702
|
-
//
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
// No history yet — show placeholder in the history panel
|
|
716
|
+
// Show a placeholder while history loads. loadHistoryList() (called in
|
|
717
|
+
// render() just before this) is async — the fresh list may not be
|
|
718
|
+
// available yet. The loadHistoryList callback will replace this with
|
|
719
|
+
// the actual latest entry once it resolves.
|
|
720
|
+
if (!viewingHistoryId) {
|
|
708
721
|
$historyBody.innerHTML = `
|
|
709
722
|
<div class="no-batch">
|
|
710
723
|
<div class="no-batch-icon">⏳</div>
|
|
711
|
-
<div class="no-batch-title">
|
|
712
|
-
<div class="no-batch-hint"
|
|
724
|
+
<div class="no-batch-title">Batch complete</div>
|
|
725
|
+
<div class="no-batch-hint">Loading history…</div>
|
|
713
726
|
</div>`;
|
|
714
727
|
$historyPanel.style.display = "";
|
|
715
728
|
}
|
|
@@ -1393,10 +1406,22 @@ function loadHistoryList() {
|
|
|
1393
1406
|
.then(list => {
|
|
1394
1407
|
historyList = list || [];
|
|
1395
1408
|
renderHistoryDropdown();
|
|
1396
|
-
//
|
|
1397
|
-
|
|
1409
|
+
// Auto-select the latest history entry when no live batch is running.
|
|
1410
|
+
// Always update the view here — renderNoBatch() shows a placeholder
|
|
1411
|
+
// while this async fetch completes, so we need to replace it with
|
|
1412
|
+
// the actual latest entry. This fixes #20 where the stale cached
|
|
1413
|
+
// historyList caused the previous batch to be shown.
|
|
1414
|
+
if (noBatchRendered && historyList.length > 0) {
|
|
1398
1415
|
viewHistoryEntry(historyList[0].batchId);
|
|
1399
1416
|
$historySelect.value = historyList[0].batchId;
|
|
1417
|
+
} else if (noBatchRendered && historyList.length === 0) {
|
|
1418
|
+
$historyBody.innerHTML = `
|
|
1419
|
+
<div class="no-batch">
|
|
1420
|
+
<div class="no-batch-icon">⏳</div>
|
|
1421
|
+
<div class="no-batch-title">No batch running</div>
|
|
1422
|
+
<div class="no-batch-hint">.pi/batch-state.json not found<br>Start an orchestrator batch to see the dashboard.</div>
|
|
1423
|
+
</div>`;
|
|
1424
|
+
$historyPanel.style.display = "";
|
|
1400
1425
|
}
|
|
1401
1426
|
})
|
|
1402
1427
|
.catch(() => {});
|