great-cto 3.27.1 → 3.27.2
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "great-cto",
|
|
3
3
|
"description": "You already have the agent. This is everything around it. great_cto runs Claude Code as a pipeline of 70 specialist agents \u2014 an independent model checks each stage before the next builds on it, spending caps refuse rather than warn, and three decisions stay yours: what gets built, how, and whether it ships.",
|
|
4
|
-
"version": "3.27.
|
|
4
|
+
"version": "3.27.2",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Alexander Velikiy",
|
|
7
7
|
"url": "https://hashnode.com/@Greatcto"
|
|
@@ -3046,7 +3046,10 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
3046
3046
|
.inbox-row .ttl { font-size: var(--fs-small); }
|
|
3047
3047
|
/* A stalled task is one line: id · reason · title · agent, and the re-run button
|
|
3048
3048
|
sits at the row's right instead of on a second grid row under it. */
|
|
3049
|
-
.inbox-row { grid-template-columns: 110px 1fr auto auto; align-items: center; }
|
|
3049
|
+
.inbox-row { grid-template-columns: 110px 1fr auto auto auto; align-items: center; }
|
|
3050
|
+
.inbox-row .actions:empty { display: none; }
|
|
3051
|
+
.ledger-title-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 12px; }
|
|
3052
|
+
.side-task-actions .gate-btn { flex: 0 0 auto; min-width: 96px; }
|
|
3050
3053
|
.inbox-row .ttl { flex-direction: row; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; }
|
|
3051
3054
|
.inbox-row .ttl > :first-child { display: inline; }
|
|
3052
3055
|
.inbox-row .ttl .why { display: inline-block; font-size: var(--fs-eyebrow); padding: 0 6px; line-height: 18px; white-space: nowrap; }
|
|
@@ -3629,7 +3632,21 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
3629
3632
|
|
|
3630
3633
|
<div class="panel" id="panel-budgets" role="tabpanel">
|
|
3631
3634
|
<div class="metrics-page">
|
|
3632
|
-
<
|
|
3635
|
+
<div class="ledger-title-row">
|
|
3636
|
+
<h1 class="page-title" style="margin:0">Ledger</h1>
|
|
3637
|
+
<!-- The period governs every figure below it, so it sits where the
|
|
3638
|
+
figures start — beside the name, not under the stuck list. -->
|
|
3639
|
+
<div class="period-toolbar" style="margin:0">
|
|
3640
|
+
<span class="period-label">PERIOD</span>
|
|
3641
|
+
<div class="period-chips" id="period-chips">
|
|
3642
|
+
<button class="period-chip" data-period="1">1D</button>
|
|
3643
|
+
<button class="period-chip" data-period="7">7D</button>
|
|
3644
|
+
<button class="period-chip active" data-period="30">30D</button>
|
|
3645
|
+
<button class="period-chip" data-period="90">90D</button>
|
|
3646
|
+
<button class="period-chip" data-period="365">1Y</button>
|
|
3647
|
+
</div>
|
|
3648
|
+
</div>
|
|
3649
|
+
</div>
|
|
3633
3650
|
<!-- Ledger (great_cto-ki1x.7): the one decision this screen supports is
|
|
3634
3651
|
"is something running that I should stop". Two figures, the stage
|
|
3635
3652
|
strip and the stuck list answer it; the caps table below is where
|
|
@@ -3638,16 +3655,6 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
3638
3655
|
<!-- From the old Metrics tab (great_cto-ki1x.16): the period chips, the caps
|
|
3639
3656
|
card and the cost panel keep their ids — the code that fills them did
|
|
3640
3657
|
not move, only the screen that shows them. -->
|
|
3641
|
-
<div class="period-toolbar">
|
|
3642
|
-
<span class="period-label">PERIOD</span>
|
|
3643
|
-
<div class="period-chips" id="period-chips">
|
|
3644
|
-
<button class="period-chip" data-period="1">1D</button>
|
|
3645
|
-
<button class="period-chip" data-period="7">7D</button>
|
|
3646
|
-
<button class="period-chip active" data-period="30">30D</button>
|
|
3647
|
-
<button class="period-chip" data-period="90">90D</button>
|
|
3648
|
-
<button class="period-chip" data-period="365">1Y</button>
|
|
3649
|
-
</div>
|
|
3650
|
-
</div>
|
|
3651
3658
|
<!-- Budgets live HERE, on Metrics, not two clicks deep in the Agents
|
|
3652
3659
|
drill-in. A control the operator could not find is a control they do
|
|
3653
3660
|
not have — and it was invisible on top of that, hidden whenever a
|
|
@@ -4553,6 +4560,11 @@ function renderInboxList(rootId, items, countId, opts = {}) {
|
|
|
4553
4560
|
+ `<button type="button" class="gate-btn" title="Copy a git diff limited to the reviewed files"`
|
|
4554
4561
|
+ ` onclick="event.stopPropagation(); copyDiff(${esc(JSON.stringify(JSON.stringify(t.__files)))})">Copy diff</button></div>`
|
|
4555
4562
|
: '';
|
|
4563
|
+
const unblock = (!canDecide && !t.__synthetic && t.id && (t.raw_status || t.status) === 'blocked')
|
|
4564
|
+
? `<div class="actions" onclick="event.stopPropagation()">`
|
|
4565
|
+
+ `<button type="button" class="gate-btn gate-approve" title="Set the task back to open"`
|
|
4566
|
+
+ ` onclick="event.stopPropagation(); taskDecision('${esc(t.id)}', 'open')">Unblock</button></div>`
|
|
4567
|
+
: '';
|
|
4556
4568
|
const rerun = (!canDecide && !t.__synthetic && t.agent && t.id)
|
|
4557
4569
|
? `<div class="actions" onclick="event.stopPropagation()">`
|
|
4558
4570
|
+ `<button type="button" class="gate-btn" title="Copy the line that re-runs this agent"`
|
|
@@ -4607,7 +4619,7 @@ function renderInboxList(rootId, items, countId, opts = {}) {
|
|
|
4607
4619
|
${canDecide ? gateChip(t) : `<span class="id">${esc(t.id || '')}</span>`}
|
|
4608
4620
|
<span class="ttl">${canDecide ? '' : reasonChip}${esc(canDecide ? String(t.title || '').replace(/^gate:[a-z-]+\s*[—–-]?\s*/, '') : t.title)}${desc}<span class="meta">${meta}</span>${tierNote}</span>
|
|
4609
4621
|
${canDecide ? reviewerCells(t) : ''}
|
|
4610
|
-
${actions}${rerun}${diffBtn}
|
|
4622
|
+
${actions}${unblock}${rerun}${diffBtn}
|
|
4611
4623
|
</div>`;
|
|
4612
4624
|
}).join('');
|
|
4613
4625
|
}
|
|
@@ -7877,11 +7889,22 @@ function openSide(task) {
|
|
|
7877
7889
|
Reject
|
|
7878
7890
|
</button>
|
|
7879
7891
|
</div>` : '';
|
|
7892
|
+
// A task that is not a gate still needs a decision the operator can take
|
|
7893
|
+
// here: a blocked one is unblocked, an open one is started, either is closed.
|
|
7894
|
+
// Before this the drawer showed the status and told the operator to go run
|
|
7895
|
+
// `bd update` — a board that could read the state but not change it.
|
|
7896
|
+
const taskActions = (!task.is_gate && rawStatus !== 'closed') ? `
|
|
7897
|
+
<div class="side-gate-actions side-task-actions">
|
|
7898
|
+
${rawStatus === 'blocked' ? `<button class="gate-btn gate-approve" onclick="taskDecision('${esc(task.id)}', 'open')">Unblock</button>` : ''}
|
|
7899
|
+
${rawStatus === 'open' ? `<button class="gate-btn gate-approve" onclick="taskDecision('${esc(task.id)}', 'in_progress')">Start</button>` : ''}
|
|
7900
|
+
${rawStatus === 'in_progress' || rawStatus === 'open' ? `<button class="gate-btn" onclick="taskDecision('${esc(task.id)}', 'blocked')">Block</button>` : ''}
|
|
7901
|
+
<button class="gate-btn gate-reject" onclick="taskDecision('${esc(task.id)}', 'closed')">Close</button>
|
|
7902
|
+
</div>` : '';
|
|
7880
7903
|
_sideTask = task; // so the doc viewer's "← back" can restore this task
|
|
7881
7904
|
document.getElementById('side-body').innerHTML = `
|
|
7882
7905
|
<div class="side-title">${esc(task.title)}</div>
|
|
7883
7906
|
${desc}
|
|
7884
|
-
${gateActions}
|
|
7907
|
+
${gateActions}${taskActions}
|
|
7885
7908
|
<div class="props">
|
|
7886
7909
|
${rows.map(([k, v]) => `<div class="prop-key">${k}</div><div class="prop-val">${v}</div>`).join('')}
|
|
7887
7910
|
</div>
|
|
@@ -7914,6 +7937,23 @@ function openSide(task) {
|
|
|
7914
7937
|
</div>`).join('')}`;
|
|
7915
7938
|
});
|
|
7916
7939
|
}
|
|
7940
|
+
// One status change, through the endpoint the board has had since the kanban:
|
|
7941
|
+
// POST /api/tasks/:id/status. Closing asks once — it is the only transition the
|
|
7942
|
+
// list stops showing; the rest are visible and reversible from the same drawer.
|
|
7943
|
+
async function taskDecision(id, status) {
|
|
7944
|
+
if (status === 'closed' && !confirm(`Close ${id}? It leaves every list on this board.`)) return;
|
|
7945
|
+
const r = await api(`/api/tasks/${encodeURIComponent(id)}/status${pqs()}`, {
|
|
7946
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ status }),
|
|
7947
|
+
}).catch(() => null);
|
|
7948
|
+
if (!r || r.error) { showToast('✗ ' + esc(r?.error || 'status not changed'), 'error', 4000); return; }
|
|
7949
|
+
const said = { open: 'unblocked', in_progress: 'started', blocked: 'blocked', closed: 'closed' }[status] || status;
|
|
7950
|
+
showToast(`<strong>${esc(id)}</strong> ${said}`, status === 'closed' ? 'error' : 'success', 2500);
|
|
7951
|
+
if (_sideTask && _sideTask.id === id) {
|
|
7952
|
+
if (status === 'closed') closeSide();
|
|
7953
|
+
else openSide({ ..._sideTask, status, raw_status: status });
|
|
7954
|
+
}
|
|
7955
|
+
refreshInbox();
|
|
7956
|
+
}
|
|
7917
7957
|
// ── Documents referenced by a task: extraction + the inline side-panel viewer ──
|
|
7918
7958
|
let _sideTask = null;
|
|
7919
7959
|
function extractDocRefs(task) {
|
package/package.json
CHANGED