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.1",
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
- <h1 class="page-title">Ledger</h1>
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "great-cto",
3
- "version": "3.27.1",
3
+ "version": "3.27.2",
4
4
  "description": "One command install for the great_cto Claude Code plugin. Auto-detects your stack, picks the right archetype, bootstraps PROJECT.md.",
5
5
  "keywords": [
6
6
  "claude-code",