taskplane 0.27.0 → 0.28.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 +162 -13
- package/extensions/taskplane/discovery.ts +1818 -1508
- package/extensions/taskplane/execution.ts +33 -2
- package/extensions/taskplane/lane-runner.ts +375 -44
- package/extensions/taskplane/types.ts +25 -1
- package/package.json +1 -1
- package/skills/create-taskplane-task/SKILL.md +58 -0
- package/skills/create-taskplane-task/references/prompt-template.md +39 -0
- package/templates/agents/task-worker-segment.md +44 -0
- package/templates/agents/task-worker.md +429 -429
package/dashboard/public/app.js
CHANGED
|
@@ -699,21 +699,30 @@ function renderLanesTasks(batch, sessions) {
|
|
|
699
699
|
const showPacketHome = !!packetHomeRepo && packetHomeRepo !== (tRepo || lane.repoId || "");
|
|
700
700
|
|
|
701
701
|
// Progress cell
|
|
702
|
+
// TP-174: Prefer V2 snapshot progress (segment-scoped when available)
|
|
703
|
+
// over full STATUS.md counts when the task is actively running on this lane.
|
|
704
|
+
// TP-176: Succeeded tasks always show 100% regardless of sidecar/statusData (#491).
|
|
702
705
|
let progressHtml = "";
|
|
703
|
-
|
|
704
|
-
|
|
706
|
+
const v2p = ls && ls._v2Progress;
|
|
707
|
+
const useV2 = v2p && v2p.total > 0 && ls.taskId === task.taskId;
|
|
708
|
+
if (task.status === "succeeded") {
|
|
709
|
+
// #491 fix: succeeded tasks always show 100%
|
|
705
710
|
progressHtml = `
|
|
706
711
|
<div class="task-progress">
|
|
707
|
-
<div class="task-progress-bar">
|
|
708
|
-
|
|
709
|
-
</div>
|
|
710
|
-
<span class="task-progress-text">${sd.progress}% ${sd.checked}/${sd.total}</span>
|
|
712
|
+
<div class="task-progress-bar"><div class="task-progress-fill pct-hi" style="width:100%"></div></div>
|
|
713
|
+
<span class="task-progress-text">100%</span>
|
|
711
714
|
</div>`;
|
|
712
|
-
} else if (
|
|
715
|
+
} else if (sd || useV2) {
|
|
716
|
+
const displayChecked = useV2 ? v2p.checked : (sd ? sd.checked : 0);
|
|
717
|
+
const displayTotal = useV2 ? v2p.total : (sd ? sd.total : 0);
|
|
718
|
+
const displayProgress = displayTotal > 0 ? Math.round((displayChecked / displayTotal) * 100) : 0;
|
|
719
|
+
const fillClass = pctClass(displayProgress);
|
|
713
720
|
progressHtml = `
|
|
714
721
|
<div class="task-progress">
|
|
715
|
-
<div class="task-progress-bar"
|
|
716
|
-
|
|
722
|
+
<div class="task-progress-bar">
|
|
723
|
+
<div class="task-progress-fill ${fillClass}" style="width:${displayProgress}%"></div>
|
|
724
|
+
</div>
|
|
725
|
+
<span class="task-progress-text">${displayProgress}% ${displayChecked}/${displayTotal}</span>
|
|
717
726
|
</div>`;
|
|
718
727
|
} else if (task.status === "pending") {
|
|
719
728
|
progressHtml = `
|
|
@@ -1881,6 +1890,127 @@ function renderV2Event(evt) {
|
|
|
1881
1890
|
}
|
|
1882
1891
|
}
|
|
1883
1892
|
|
|
1893
|
+
// ── Segment-Scoped STATUS.md Helpers (TP-176) ──────────────────────────────
|
|
1894
|
+
|
|
1895
|
+
/**
|
|
1896
|
+
* Resolve the active segment repoId for a given task.
|
|
1897
|
+
* Uses runtimeLaneSnapshots (active segment) and falls back to
|
|
1898
|
+
* taskSegmentProgress (batch state).
|
|
1899
|
+
* Returns { repoId, segmentInfo } or null if single-segment / unresolvable.
|
|
1900
|
+
*/
|
|
1901
|
+
function resolveActiveSegmentForTask(taskId) {
|
|
1902
|
+
if (!currentData) return null;
|
|
1903
|
+
const batch = currentData.batch;
|
|
1904
|
+
if (!batch) return null;
|
|
1905
|
+
const task = (batch.tasks || []).find(t => t.taskId === taskId);
|
|
1906
|
+
if (!task) return null;
|
|
1907
|
+
const segmentIds = Array.isArray(task.segmentIds) ? task.segmentIds.filter(id => typeof id === "string") : [];
|
|
1908
|
+
if (segmentIds.length <= 1) return null; // single-segment or no segments
|
|
1909
|
+
|
|
1910
|
+
// Try to get active segment from runtime lane snapshots
|
|
1911
|
+
const v2Snapshots = currentData.runtimeLaneSnapshots || {};
|
|
1912
|
+
for (const snap of Object.values(v2Snapshots)) {
|
|
1913
|
+
if (snap && snap.taskId === taskId && snap.segmentId) {
|
|
1914
|
+
const parsed = parseSegmentId(snap.segmentId);
|
|
1915
|
+
if (parsed) {
|
|
1916
|
+
const idx = segmentIds.indexOf(snap.segmentId);
|
|
1917
|
+
return {
|
|
1918
|
+
repoId: parsed.repoId,
|
|
1919
|
+
segmentInfo: {
|
|
1920
|
+
index: idx >= 0 ? idx + 1 : null,
|
|
1921
|
+
total: segmentIds.length,
|
|
1922
|
+
repoId: parsed.repoId,
|
|
1923
|
+
segmentId: snap.segmentId,
|
|
1924
|
+
},
|
|
1925
|
+
};
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
// Fallback: use taskSegmentProgress (batch state)
|
|
1931
|
+
const segmentStatusMap = buildSegmentStatusMap(batch);
|
|
1932
|
+
const info = taskSegmentProgress(task, segmentStatusMap, null);
|
|
1933
|
+
if (info && info.repoId) {
|
|
1934
|
+
return { repoId: info.repoId, segmentInfo: info };
|
|
1935
|
+
}
|
|
1936
|
+
return null;
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
/**
|
|
1940
|
+
* Filter STATUS.md content to show only the active segment's blocks.
|
|
1941
|
+
* Within each `### Step N:` section, removes `#### Segment: <otherRepo>` blocks
|
|
1942
|
+
* and keeps only the block matching `activeRepoId`.
|
|
1943
|
+
* Non-step content (metadata, notes, reviews, etc.) is preserved.
|
|
1944
|
+
*
|
|
1945
|
+
* Returns the filtered markdown string, or the original if no segment markers found.
|
|
1946
|
+
*/
|
|
1947
|
+
function filterStatusMdForSegment(markdown, activeRepoId) {
|
|
1948
|
+
if (!activeRepoId) return markdown;
|
|
1949
|
+
const lines = markdown.split('\n');
|
|
1950
|
+
const result = [];
|
|
1951
|
+
let inStep = false; // inside a ### Step section
|
|
1952
|
+
let inSegmentBlock = false; // inside a #### Segment: <repo> block
|
|
1953
|
+
let segmentMatch = false; // current segment block matches active repo
|
|
1954
|
+
let foundAnySegmentHeader = false;
|
|
1955
|
+
|
|
1956
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1957
|
+
const line = lines[i];
|
|
1958
|
+
|
|
1959
|
+
// Detect step headers: ### Step N: ...
|
|
1960
|
+
if (/^###\s+Step\s+\d+/.test(line)) {
|
|
1961
|
+
inStep = true;
|
|
1962
|
+
inSegmentBlock = false;
|
|
1963
|
+
segmentMatch = false;
|
|
1964
|
+
result.push(line);
|
|
1965
|
+
continue;
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
// Detect non-step ### headers (e.g., ### Reviews, ### Notes)
|
|
1969
|
+
if (/^###\s+/.test(line) && !/^###\s+Step\s+\d+/.test(line)) {
|
|
1970
|
+
inStep = false;
|
|
1971
|
+
inSegmentBlock = false;
|
|
1972
|
+
segmentMatch = false;
|
|
1973
|
+
result.push(line);
|
|
1974
|
+
continue;
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
// Inside a step section, detect #### Segment: <repoId> headers
|
|
1978
|
+
if (inStep && /^####\s+Segment:\s*/.test(line)) {
|
|
1979
|
+
foundAnySegmentHeader = true;
|
|
1980
|
+
const segRepo = line.replace(/^####\s+Segment:\s*/, '').trim();
|
|
1981
|
+
inSegmentBlock = true;
|
|
1982
|
+
segmentMatch = (segRepo === activeRepoId);
|
|
1983
|
+
if (segmentMatch) {
|
|
1984
|
+
result.push(line);
|
|
1985
|
+
}
|
|
1986
|
+
continue;
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
// Detect any other #### header (ends current segment block)
|
|
1990
|
+
if (/^####\s+/.test(line)) {
|
|
1991
|
+
inSegmentBlock = false;
|
|
1992
|
+
segmentMatch = false;
|
|
1993
|
+
result.push(line);
|
|
1994
|
+
continue;
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
// If we're in a segment block, only include matching lines
|
|
1998
|
+
if (inSegmentBlock) {
|
|
1999
|
+
if (segmentMatch) {
|
|
2000
|
+
result.push(line);
|
|
2001
|
+
}
|
|
2002
|
+
continue;
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
// Outside segment blocks: keep the line
|
|
2006
|
+
result.push(line);
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
// If no segment headers were found, return original (fallback for single-segment)
|
|
2010
|
+
if (!foundAnySegmentHeader) return markdown;
|
|
2011
|
+
return result.join('\n');
|
|
2012
|
+
}
|
|
2013
|
+
|
|
1884
2014
|
// ── Open STATUS.md viewer ───────────────────────────────────────────────────
|
|
1885
2015
|
|
|
1886
2016
|
function viewStatusMd(taskId) {
|
|
@@ -1897,7 +2027,15 @@ function viewStatusMd(taskId) {
|
|
|
1897
2027
|
autoScrollOn = false;
|
|
1898
2028
|
lastStatusMdText = '';
|
|
1899
2029
|
|
|
1900
|
-
|
|
2030
|
+
// TP-176: Include segment context in title for multi-segment tasks
|
|
2031
|
+
const segData = resolveActiveSegmentForTask(taskId);
|
|
2032
|
+
if (segData && segData.segmentInfo) {
|
|
2033
|
+
const label = segmentProgressText(segData.segmentInfo);
|
|
2034
|
+
$terminalTitle.textContent = `STATUS.md — ${taskId} · ${label}`;
|
|
2035
|
+
} else {
|
|
2036
|
+
$terminalTitle.textContent = `STATUS.md — ${taskId}`;
|
|
2037
|
+
}
|
|
2038
|
+
|
|
1901
2039
|
$autoScrollText.textContent = 'Track progress';
|
|
1902
2040
|
$autoScrollCheckbox.checked = false;
|
|
1903
2041
|
$terminalPanel.style.display = '';
|
|
@@ -1916,11 +2054,22 @@ function pollStatusMd() {
|
|
|
1916
2054
|
return r.text();
|
|
1917
2055
|
})
|
|
1918
2056
|
.then(text => {
|
|
2057
|
+
// TP-176: Apply segment-scoped filtering for multi-segment tasks.
|
|
2058
|
+
// Re-resolve on each poll since the active segment may change.
|
|
2059
|
+
const segData = resolveActiveSegmentForTask(viewerTarget);
|
|
2060
|
+
let displayText = text;
|
|
2061
|
+
if (segData && segData.repoId) {
|
|
2062
|
+
displayText = filterStatusMdForSegment(text, segData.repoId);
|
|
2063
|
+
// Update title with current segment context (may change between polls)
|
|
2064
|
+
const label = segmentProgressText(segData.segmentInfo);
|
|
2065
|
+
$terminalTitle.textContent = `STATUS.md \u2014 ${viewerTarget} \u00b7 ${label}`;
|
|
2066
|
+
}
|
|
2067
|
+
|
|
1919
2068
|
// Diff-and-skip: no change, no DOM update
|
|
1920
|
-
if (
|
|
1921
|
-
lastStatusMdText =
|
|
2069
|
+
if (displayText === lastStatusMdText) return;
|
|
2070
|
+
lastStatusMdText = displayText;
|
|
1922
2071
|
|
|
1923
|
-
const { html, hasLastChecked } = renderStatusMd(
|
|
2072
|
+
const { html, hasLastChecked } = renderStatusMd(displayText);
|
|
1924
2073
|
$terminalBody.innerHTML = html;
|
|
1925
2074
|
|
|
1926
2075
|
// Update tracking highlight
|