claude-teammate 0.1.145 → 0.1.147
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/package.json +1 -1
- package/src/commands/worker.js +34 -7
- package/src/dashboard/ui.html +93 -142
package/package.json
CHANGED
package/src/commands/worker.js
CHANGED
|
@@ -191,7 +191,8 @@ export async function runWorkerCommand({ projectRoot }) {
|
|
|
191
191
|
lastReviewSuccessAt: null,
|
|
192
192
|
lastReviewError: null,
|
|
193
193
|
reviewPrCount: 0,
|
|
194
|
-
reviewPrs: []
|
|
194
|
+
reviewPrs: [],
|
|
195
|
+
pollerBusy: { jira: false, github: false, draftPr: false, reviewPr: false }
|
|
195
196
|
};
|
|
196
197
|
|
|
197
198
|
const updatePrSubtaskState = async (key, updates) => {
|
|
@@ -250,6 +251,8 @@ export async function runWorkerCommand({ projectRoot }) {
|
|
|
250
251
|
return;
|
|
251
252
|
}
|
|
252
253
|
jiraFlag.value = true;
|
|
254
|
+
state.pollerBusy.jira = true;
|
|
255
|
+
await writeState(runtimePaths.stateFile, state);
|
|
253
256
|
await runPoll("", "Jira", state, async () => {
|
|
254
257
|
const result = await jira.fetchAssignedIssues();
|
|
255
258
|
const processedIssues = [];
|
|
@@ -272,6 +275,8 @@ export async function runWorkerCommand({ projectRoot }) {
|
|
|
272
275
|
logInfo: { issues: result.issues.length, assigned: result.total }
|
|
273
276
|
};
|
|
274
277
|
}, { stateFile: runtimePaths.stateFile, logger, flagRef: jiraFlag });
|
|
278
|
+
state.pollerBusy.jira = false;
|
|
279
|
+
await writeState(runtimePaths.stateFile, state);
|
|
275
280
|
};
|
|
276
281
|
|
|
277
282
|
const pollGitHub = async () => {
|
|
@@ -279,6 +284,8 @@ export async function runWorkerCommand({ projectRoot }) {
|
|
|
279
284
|
return;
|
|
280
285
|
}
|
|
281
286
|
githubFlag.value = true;
|
|
287
|
+
state.pollerBusy.github = true;
|
|
288
|
+
await writeState(runtimePaths.stateFile, state);
|
|
282
289
|
await runPoll("GitHub", "GitHub", state, async () => {
|
|
283
290
|
const repos = filterReposForActiveForge(await listKnownRepos(projectRoot), forgeRegistry);
|
|
284
291
|
const reposByUrl = new Map(repos.map((repo) => [repo.url, repo]));
|
|
@@ -396,6 +403,8 @@ export async function runWorkerCommand({ projectRoot }) {
|
|
|
396
403
|
logInfo: { tracked: trackedIssueCount }
|
|
397
404
|
};
|
|
398
405
|
}, { stateFile: runtimePaths.stateFile, logger, flagRef: githubFlag });
|
|
406
|
+
state.pollerBusy.github = false;
|
|
407
|
+
await writeState(runtimePaths.stateFile, state);
|
|
399
408
|
};
|
|
400
409
|
|
|
401
410
|
const pollDraftPrs = async () => {
|
|
@@ -403,6 +412,8 @@ export async function runWorkerCommand({ projectRoot }) {
|
|
|
403
412
|
return;
|
|
404
413
|
}
|
|
405
414
|
prFlag.value = true;
|
|
415
|
+
state.pollerBusy.draftPr = true;
|
|
416
|
+
await writeState(runtimePaths.stateFile, state);
|
|
406
417
|
const prJira = createJiraClient(values);
|
|
407
418
|
await runPoll("Pr", "Pull request", state, async () => {
|
|
408
419
|
const repos = filterReposForActiveForge(await listKnownRepos(projectRoot), forgeRegistry);
|
|
@@ -529,6 +540,8 @@ export async function runWorkerCommand({ projectRoot }) {
|
|
|
529
540
|
logInfo: { tracked: trackedPrCount }
|
|
530
541
|
};
|
|
531
542
|
}, { stateFile: runtimePaths.stateFile, logger, flagRef: prFlag });
|
|
543
|
+
state.pollerBusy.draftPr = false;
|
|
544
|
+
await writeState(runtimePaths.stateFile, state);
|
|
532
545
|
};
|
|
533
546
|
|
|
534
547
|
const pollReviewPrs = async () => {
|
|
@@ -536,6 +549,8 @@ export async function runWorkerCommand({ projectRoot }) {
|
|
|
536
549
|
return;
|
|
537
550
|
}
|
|
538
551
|
reviewFlag.value = true;
|
|
552
|
+
state.pollerBusy.reviewPr = true;
|
|
553
|
+
await writeState(runtimePaths.stateFile, state);
|
|
539
554
|
await runPoll("Review", "PR review", state, async () => {
|
|
540
555
|
const processedPrs = [];
|
|
541
556
|
const repos = filterReposForActiveForge(await listKnownRepos(projectRoot), forgeRegistry);
|
|
@@ -594,6 +609,8 @@ export async function runWorkerCommand({ projectRoot }) {
|
|
|
594
609
|
logInfo: { reviewed: reviewedPrCount }
|
|
595
610
|
};
|
|
596
611
|
}, { stateFile: runtimePaths.stateFile, logger, flagRef: reviewFlag });
|
|
612
|
+
state.pollerBusy.reviewPr = false;
|
|
613
|
+
await writeState(runtimePaths.stateFile, state);
|
|
597
614
|
};
|
|
598
615
|
|
|
599
616
|
await pollJira();
|
|
@@ -2447,15 +2464,21 @@ export function getLatestClarificationInput(detail, botUser, jira) {
|
|
|
2447
2464
|
}
|
|
2448
2465
|
|
|
2449
2466
|
export function hasNewHumanReplyWhileWaiting(detail, botUser, jira) {
|
|
2450
|
-
const
|
|
2467
|
+
const nonProgressComments = [...detail.comments]
|
|
2451
2468
|
.filter((comment) => !String(comment?.bodyText || "").startsWith(PROGRESS_COMMENT_PREFIX))
|
|
2452
|
-
.sort(compareCommentsNewestFirst)
|
|
2469
|
+
.sort(compareCommentsNewestFirst);
|
|
2453
2470
|
|
|
2454
|
-
|
|
2471
|
+
const latestHumanComment = nonProgressComments.find((comment) => !jira.isBotAuthor(comment.author, botUser));
|
|
2472
|
+
if (!latestHumanComment) {
|
|
2455
2473
|
return false;
|
|
2456
2474
|
}
|
|
2457
2475
|
|
|
2458
|
-
|
|
2476
|
+
const latestBotComment = nonProgressComments.find((comment) => jira.isBotAuthor(comment.author, botUser));
|
|
2477
|
+
if (!latestBotComment) {
|
|
2478
|
+
return true;
|
|
2479
|
+
}
|
|
2480
|
+
|
|
2481
|
+
return getCommentTimestamp(latestHumanComment) > getCommentTimestamp(latestBotComment);
|
|
2459
2482
|
}
|
|
2460
2483
|
|
|
2461
2484
|
export function shouldReuseNoCodeDecision({ issueMemory, latestInputId }) {
|
|
@@ -2467,11 +2490,15 @@ export function shouldReuseNoCodeDecision({ issueMemory, latestInputId }) {
|
|
|
2467
2490
|
}
|
|
2468
2491
|
|
|
2469
2492
|
function compareCommentsNewestFirst(left, right) {
|
|
2470
|
-
const leftTime =
|
|
2471
|
-
const rightTime =
|
|
2493
|
+
const leftTime = getCommentTimestamp(left);
|
|
2494
|
+
const rightTime = getCommentTimestamp(right);
|
|
2472
2495
|
return rightTime - leftTime;
|
|
2473
2496
|
}
|
|
2474
2497
|
|
|
2498
|
+
function getCommentTimestamp(comment) {
|
|
2499
|
+
return Date.parse(comment?.updated || comment?.created || "") || 0;
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2475
2502
|
export function getLatestBlockedTaskRestartComment(detail, issueMemory, botUser, jira) {
|
|
2476
2503
|
if (!isBlockedOrFailedIssueState(detail, issueMemory)) {
|
|
2477
2504
|
return null;
|
package/src/dashboard/ui.html
CHANGED
|
@@ -624,8 +624,8 @@ tr:hover td { background:var(--bg); }
|
|
|
624
624
|
margin-top:5px;
|
|
625
625
|
}
|
|
626
626
|
|
|
627
|
-
/* ──
|
|
628
|
-
.
|
|
627
|
+
/* ── Heartbeat Queues ── */
|
|
628
|
+
.heartbeat-queues {
|
|
629
629
|
background:var(--bg-card);
|
|
630
630
|
border:1px solid var(--border);
|
|
631
631
|
border-radius:var(--r-lg);
|
|
@@ -634,104 +634,90 @@ tr:hover td { background:var(--bg); }
|
|
|
634
634
|
overflow:hidden;
|
|
635
635
|
}
|
|
636
636
|
|
|
637
|
-
.
|
|
637
|
+
.hbq-header {
|
|
638
638
|
display:flex;
|
|
639
639
|
align-items:center;
|
|
640
640
|
gap:10px;
|
|
641
|
-
padding:
|
|
641
|
+
padding:12px 18px;
|
|
642
642
|
border-bottom:1px solid var(--border);
|
|
643
|
-
background:
|
|
643
|
+
background:linear-gradient(135deg, var(--teal-bg), var(--sky-bg));
|
|
644
644
|
}
|
|
645
645
|
|
|
646
|
-
.
|
|
647
|
-
width:32px; height:32px;
|
|
648
|
-
background:var(--teal);
|
|
649
|
-
border-radius:8px;
|
|
650
|
-
display:flex;
|
|
651
|
-
align-items:center;
|
|
652
|
-
justify-content:center;
|
|
653
|
-
color:white;
|
|
646
|
+
.hbq-title {
|
|
654
647
|
font-size:.85rem;
|
|
655
|
-
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
.impl-title {
|
|
659
|
-
font-size:.9rem;
|
|
660
|
-
font-weight:600;
|
|
648
|
+
font-weight:700;
|
|
661
649
|
color:var(--ink-1);
|
|
662
650
|
}
|
|
663
651
|
|
|
664
|
-
.
|
|
665
|
-
font-size:.72rem;
|
|
666
|
-
color:var(--ink-3);
|
|
667
|
-
margin-top:1px;
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
.impl-body { padding:16px 18px; }
|
|
652
|
+
.hbq-body { padding:14px 18px; display:flex; flex-direction:column; gap:10px; }
|
|
671
653
|
|
|
672
|
-
.
|
|
654
|
+
.hbq-row {
|
|
673
655
|
display:flex;
|
|
674
656
|
align-items:center;
|
|
675
657
|
gap:10px;
|
|
676
|
-
margin-bottom:12px;
|
|
677
658
|
}
|
|
678
659
|
|
|
679
|
-
.
|
|
680
|
-
font-size:.
|
|
681
|
-
font-weight:
|
|
660
|
+
.hbq-label {
|
|
661
|
+
font-size:.68rem;
|
|
662
|
+
font-weight:700;
|
|
682
663
|
text-transform:uppercase;
|
|
683
664
|
letter-spacing:.5px;
|
|
684
|
-
color:var(--ink-
|
|
685
|
-
width:
|
|
665
|
+
color:var(--ink-3);
|
|
666
|
+
width:80px;
|
|
686
667
|
flex-shrink:0;
|
|
687
668
|
}
|
|
688
669
|
|
|
689
|
-
.
|
|
670
|
+
.hbq-cards {
|
|
671
|
+
display:flex;
|
|
672
|
+
flex-direction:row;
|
|
673
|
+
flex-wrap:nowrap;
|
|
674
|
+
gap:6px;
|
|
675
|
+
overflow-x:auto;
|
|
690
676
|
flex:1;
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
border-radius:4px;
|
|
694
|
-
overflow:hidden;
|
|
677
|
+
scrollbar-width:none;
|
|
678
|
+
padding-bottom:2px;
|
|
695
679
|
}
|
|
696
680
|
|
|
697
|
-
.
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
681
|
+
.hbq-cards::-webkit-scrollbar { display:none; }
|
|
682
|
+
|
|
683
|
+
.hbq-card {
|
|
684
|
+
font-family:var(--f-mono);
|
|
685
|
+
font-size:.72rem;
|
|
686
|
+
font-weight:600;
|
|
687
|
+
padding:4px 9px;
|
|
688
|
+
border-radius:5px;
|
|
689
|
+
border:1px solid var(--border);
|
|
690
|
+
background:var(--bg);
|
|
691
|
+
color:var(--ink-2);
|
|
692
|
+
white-space:nowrap;
|
|
693
|
+
cursor:pointer;
|
|
694
|
+
flex-shrink:0;
|
|
695
|
+
transition:border-color .15s, color .15s;
|
|
705
696
|
}
|
|
706
697
|
|
|
707
|
-
.
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
background:
|
|
713
|
-
animation:
|
|
698
|
+
.hbq-card:hover { border-color:var(--accent); color:var(--accent); }
|
|
699
|
+
|
|
700
|
+
.hbq-card.active {
|
|
701
|
+
border-color:var(--teal);
|
|
702
|
+
color:var(--teal);
|
|
703
|
+
background:var(--teal-bg);
|
|
704
|
+
animation:hbq-pulse 1.4s ease-in-out infinite;
|
|
714
705
|
}
|
|
715
706
|
|
|
716
|
-
@keyframes
|
|
717
|
-
|
|
718
|
-
|
|
707
|
+
@keyframes hbq-pulse {
|
|
708
|
+
0%, 100% { opacity:1; }
|
|
709
|
+
50% { opacity:.45; }
|
|
719
710
|
}
|
|
720
711
|
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
font-size:.68rem;
|
|
724
|
-
font-weight:600;
|
|
725
|
-
width:80px;
|
|
726
|
-
text-align:right;
|
|
727
|
-
flex-shrink:0;
|
|
712
|
+
@media (prefers-reduced-motion: reduce) {
|
|
713
|
+
.hbq-card.active { animation:none; }
|
|
728
714
|
}
|
|
729
715
|
|
|
730
|
-
.
|
|
731
|
-
font-
|
|
732
|
-
font-size:.72rem;
|
|
716
|
+
.hbq-idle {
|
|
717
|
+
font-size:.68rem;
|
|
733
718
|
color:var(--ink-3);
|
|
734
|
-
|
|
719
|
+
font-style:italic;
|
|
720
|
+
padding:4px 2px;
|
|
735
721
|
}
|
|
736
722
|
|
|
737
723
|
/* ── Log ── */
|
|
@@ -1571,8 +1557,8 @@ tr.clickable:hover td { background:var(--sky-bg) !important; }
|
|
|
1571
1557
|
</div>
|
|
1572
1558
|
</div>
|
|
1573
1559
|
|
|
1574
|
-
<!--
|
|
1575
|
-
<div id="
|
|
1560
|
+
<!-- Heartbeat queue view -->
|
|
1561
|
+
<div id="heartbeat-queues-wrap"></div>
|
|
1576
1562
|
|
|
1577
1563
|
<!-- Worker info -->
|
|
1578
1564
|
<div class="card" id="worker-card">
|
|
@@ -1729,7 +1715,6 @@ let cachedStatus = null;
|
|
|
1729
1715
|
let cachedMemFiles = [];
|
|
1730
1716
|
let activeBoardFilter = null;
|
|
1731
1717
|
let currentMemPath = null;
|
|
1732
|
-
let lastImplTrackerKey = null;
|
|
1733
1718
|
let currentDrawerKey = null;
|
|
1734
1719
|
let currentLogFilter = "all";
|
|
1735
1720
|
|
|
@@ -1798,7 +1783,7 @@ async function loadStatus() {
|
|
|
1798
1783
|
renderStats(data);
|
|
1799
1784
|
renderWorkerDetails(data);
|
|
1800
1785
|
renderIssues(data);
|
|
1801
|
-
|
|
1786
|
+
renderHeartbeatQueues(data);
|
|
1802
1787
|
renderBoardFilters(data);
|
|
1803
1788
|
renderPipeline(data);
|
|
1804
1789
|
renderAwaiting(data);
|
|
@@ -1958,86 +1943,52 @@ function renderIssues(data) {
|
|
|
1958
1943
|
}).join("");
|
|
1959
1944
|
}
|
|
1960
1945
|
|
|
1961
|
-
function
|
|
1962
|
-
const wrap = document.getElementById("
|
|
1946
|
+
function renderHeartbeatQueues(data) {
|
|
1947
|
+
const wrap = document.getElementById("heartbeat-queues-wrap");
|
|
1963
1948
|
const s = data.state || {};
|
|
1964
|
-
const
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
label
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
pr: review.pullRequestUrl,
|
|
1987
|
-
branch: review.branchName
|
|
1988
|
-
});
|
|
1989
|
-
|
|
1990
|
-
if (impl) phases.push({
|
|
1991
|
-
label: "Implementation",
|
|
1992
|
-
status: impl.status,
|
|
1993
|
-
started: impl.startedAt,
|
|
1994
|
-
finished: impl.finishedAt,
|
|
1995
|
-
pr: impl.pullRequestUrl,
|
|
1996
|
-
branch: impl.branchName
|
|
1997
|
-
});
|
|
1998
|
-
|
|
1999
|
-
const mainPhase = impl || review;
|
|
2000
|
-
const dur = mainPhase.finishedAt && mainPhase.startedAt
|
|
2001
|
-
? formatDuration(new Date(mainPhase.finishedAt) - new Date(mainPhase.startedAt))
|
|
2002
|
-
: mainPhase.startedAt ? "In progress · " + formatDuration(Date.now() - new Date(mainPhase.startedAt)) : "—";
|
|
1949
|
+
const busy = s.pollerBusy || {};
|
|
1950
|
+
|
|
1951
|
+
const rows = [
|
|
1952
|
+
{ label: "Jira", items: s.issues || [], busyKey: "jira", type: "jira" },
|
|
1953
|
+
{ label: "GitHub", items: s.githubIssues || [], busyKey: "github", type: "jira" },
|
|
1954
|
+
{ label: "Draft PRs", items: s.draftPrs || [], busyKey: "draftPr", type: "pr" },
|
|
1955
|
+
{ label: "Review PRs", items: s.reviewPrs || [], busyKey: "reviewPr", type: "pr" }
|
|
1956
|
+
];
|
|
1957
|
+
|
|
1958
|
+
const renderCard = (item, isActive, type, idx) => {
|
|
1959
|
+
const activeClass = isActive && idx === 0 ? " active" : "";
|
|
1960
|
+
if (type === "jira") {
|
|
1961
|
+
const key = esc(item.key || "");
|
|
1962
|
+
const onclick = item.key
|
|
1963
|
+
? `onclick='openDrawer(${JSON.stringify(item)}, cachedStatus)'`
|
|
1964
|
+
: "";
|
|
1965
|
+
return `<div class="hbq-card${activeClass}" ${onclick}>${key}</div>`;
|
|
1966
|
+
}
|
|
1967
|
+
const label = item.pullRequestNumber ? esc("#" + item.pullRequestNumber) : "PR";
|
|
1968
|
+
const href = item.pullRequestUrl ? esc(item.pullRequestUrl) : "#";
|
|
1969
|
+
return `<a class="hbq-card${activeClass}" href="${href}" target="_blank" style="text-decoration:none">${label}</a>`;
|
|
1970
|
+
};
|
|
2003
1971
|
|
|
2004
1972
|
wrap.innerHTML = `
|
|
2005
|
-
<div class="
|
|
2006
|
-
<div class="
|
|
2007
|
-
<
|
|
2008
|
-
<div>
|
|
2009
|
-
<div class="impl-title">${esc(mainPhase.branch || "Implementation Tracker")}</div>
|
|
2010
|
-
<div class="impl-sub">
|
|
2011
|
-
${mainPhase.pr ? `<a href="${esc(mainPhase.pr)}" target="_blank">View PR</a> · ` : ""}
|
|
2012
|
-
Duration: ${dur}
|
|
2013
|
-
</div>
|
|
2014
|
-
</div>
|
|
2015
|
-
<span class="tag ${mainPhase.status}" style="margin-left:auto">${esc(mainPhase.status)}</span>
|
|
1973
|
+
<div class="heartbeat-queues">
|
|
1974
|
+
<div class="hbq-header">
|
|
1975
|
+
<span class="hbq-title">Heartbeat Queues</span>
|
|
2016
1976
|
</div>
|
|
2017
|
-
<div class="
|
|
2018
|
-
${
|
|
2019
|
-
const
|
|
2020
|
-
const
|
|
2021
|
-
|
|
1977
|
+
<div class="hbq-body">
|
|
1978
|
+
${rows.map(row => {
|
|
1979
|
+
const isActive = !!busy[row.busyKey];
|
|
1980
|
+
const cards = row.items.length > 0
|
|
1981
|
+
? row.items.map((item, idx) => renderCard(item, isActive, row.type, idx)).join("")
|
|
1982
|
+
: `<span class="hbq-idle">idle</span>`;
|
|
2022
1983
|
return `
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
</div>
|
|
2028
|
-
<div class="impl-phase-status" style="color:${pct===100?'var(--green)':'var(--teal)'}">${esc(ph.status)}</div>
|
|
2029
|
-
</div>`;
|
|
1984
|
+
<div class="hbq-row">
|
|
1985
|
+
<div class="hbq-label">${esc(row.label)}</div>
|
|
1986
|
+
<div class="hbq-cards">${cards}</div>
|
|
1987
|
+
</div>`;
|
|
2030
1988
|
}).join("")}
|
|
2031
1989
|
</div>
|
|
2032
1990
|
</div>
|
|
2033
1991
|
`;
|
|
2034
|
-
|
|
2035
|
-
// animate bars
|
|
2036
|
-
setTimeout(() => {
|
|
2037
|
-
wrap.querySelectorAll(".impl-progress-fill[data-pct]").forEach(el => {
|
|
2038
|
-
el.style.width = el.dataset.pct + "%";
|
|
2039
|
-
});
|
|
2040
|
-
}, 100);
|
|
2041
1992
|
}
|
|
2042
1993
|
|
|
2043
1994
|
/* ── Pipeline ── */
|