great-cto 2.86.0 → 2.87.1

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.
@@ -2,7 +2,7 @@
2
2
  "name": "great_cto",
3
3
  "id": "great_cto",
4
4
  "description": "Engineering process for solo founders and teams up to 50 engineers. Agents do architecture, code review, QA, and security. You make two decisions per feature.",
5
- "version": "2.86.0",
5
+ "version": "2.87.1",
6
6
  "author": {
7
7
  "name": "Great CTO",
8
8
  "url": "https://github.com/avelikiy/great_cto"
@@ -152,6 +152,12 @@
152
152
  "command": "PLUGIN_DIR=$(ls -d ~/.claude/plugins/cache/local/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||'); node \"${PLUGIN_DIR}/scripts/hooks/frozen-gates-guard.mjs\" 2>&1; EXIT=$?; if [ $EXIT -eq 2 ]; then exit 2; fi; exit 0",
153
153
  "timeout": 5,
154
154
  "statusMessage": "Checking frozen gates..."
155
+ },
156
+ {
157
+ "type": "command",
158
+ "command": "PLUGIN_DIR=$(ls -d ~/.claude/plugins/cache/local/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||'); node \"${PLUGIN_DIR}/scripts/hooks/edit-scope-guard.mjs\" 2>&1; EXIT=$?; if [ $EXIT -eq 2 ]; then exit 2; fi; exit 0",
159
+ "timeout": 5,
160
+ "statusMessage": "Checking edit scope..."
155
161
  }
156
162
  ]
157
163
  }
@@ -5,6 +5,25 @@ import { sseClients, bdCache } from './state.mjs';
5
5
  import { getTasks } from './beads.mjs';
6
6
  import { readVerdicts, readPlanCosts, readQAStats, readSecStats } from './verdicts.mjs';
7
7
 
8
+ // Acceptance-oriented metrics (the "verified acceptance" shift): measure the cost
9
+ // of a change that a gate actually APPROVED — not throughput — and how often work
10
+ // was sent back before it landed. Both derive from verdict logs already on disk;
11
+ // no new capture. cost_per_accepted is null when nothing was accepted in the
12
+ // window: dividing a real cost by zero approvals would invent a number, and "no
13
+ // accepted change yet" is the honest reading (same discipline as the null-score
14
+ // oracle). Only two of the five metrics the literature lists are built here on
15
+ // purpose — a solo project's denominator is tiny and the other three would be noise.
16
+ function acceptanceMetrics(verdicts = [], windowCostUsd = null) {
17
+ const isAccepted = (v) => /^APPROVED$/i.test(v.verdict || '');
18
+ const isRework = (v) => /^(CHANGES(_REQUESTED)?|REJECT(ED)?|FAIL(ED)?|BLOCKED)$/i.test(v.verdict || '');
19
+ const accepted = verdicts.filter(isAccepted).length;
20
+ const rework_rounds = verdicts.filter(isRework).length;
21
+ const cost_per_accepted = (accepted > 0 && windowCostUsd != null)
22
+ ? Math.round((windowCostUsd / accepted) * 100) / 100
23
+ : null;
24
+ return { accepted, cost_per_accepted, rework_rounds };
25
+ }
26
+
8
27
  // ── Metrics ────────────────────────────────────────────────────────────────────
9
28
  function getMetrics(cwd = process.cwd(), days = 30) {
10
29
  // `days` controls the window for cost/agents_cost and for "shipped in window".
@@ -245,8 +264,14 @@ function getMetrics(cwd = process.cwd(), days = 30) {
245
264
  // Count tasks completed in selected window (for period-scoped reports)
246
265
  const doneInWindow = done.filter(t => t.closed_at && (now - new Date(t.closed_at).getTime()) <= costWindowMs);
247
266
 
267
+ // Acceptance metrics over the same window as cost, so cost_per_accepted lines
268
+ // up with the cost tile.
269
+ const verdictsInWindow = verdicts.filter(v => v.ts && (now - new Date(v.ts).getTime()) <= costWindowMs);
270
+ const acceptance = acceptanceMetrics(verdictsInWindow, cost.llm_usd);
271
+
248
272
  return {
249
273
  window_days: days,
274
+ acceptance,
250
275
  tasks: {
251
276
  total: tasks.length,
252
277
  done: done.length,
@@ -304,4 +329,5 @@ function getCanonicalAgents() {
304
329
  return set;
305
330
  }
306
331
 
307
- export { getMetrics, getCanonicalAgents };
332
+ export {
333
+ acceptanceMetrics, getMetrics, getCanonicalAgents };
@@ -4174,9 +4174,15 @@ function renderDashboard(m) {
4174
4174
  <div class="metric-trend">${s.trend}</div>
4175
4175
  </div>`).join('');
4176
4176
 
4177
+ const acc = m.acceptance || {};
4177
4178
  const secondary = [
4178
4179
  { v: avgMin ? `${avgMin}` : '—', sub: avgMin ? 'm' : '', label: cycleStat },
4179
4180
  { v: m.qa?.pass_rate != null ? m.qa.pass_rate : '—', sub: m.qa?.pass_rate != null ? '%' : '', label: m.qa?.pass_rate != null ? 'QA pass rate' : 'QA — run /qa', cls: 'green' },
4181
+ // Verified-acceptance metrics: cost of a change a gate actually APPROVED, and
4182
+ // how often work bounced. '—' when nothing was accepted in the window — an
4183
+ // honest gap, not a zero.
4184
+ { v: acc.cost_per_accepted != null ? `$${acc.cost_per_accepted.toFixed(2)}` : '—', sub: '', label: acc.accepted ? `Cost / accepted (${acc.accepted})` : 'Cost / accepted' },
4185
+ { v: acc.rework_rounds ?? 0, sub: '', label: 'Rework rounds', cls: acc.rework_rounds ? 'amber' : 'green' },
4180
4186
  { v: m.security?.blocked ?? 0, sub: '', label: 'Open security blocks', cls: m.security?.blocked ? 'red' : 'green' },
4181
4187
  { v: m.tasks?.in_progress ?? 0, sub: '', label: 'In progress', cls: 'amber' },
4182
4188
  ];
package/dist/adapt.js CHANGED
@@ -541,6 +541,34 @@ export async function runAdapt(args) {
541
541
  if (!meta.aiTools.includes("cursor") && !meta.aiTools.includes("copilot") && !meta.aiTools.includes("windsurf")) {
542
542
  console.log(` Tip: add other AI tools to ai_tools: [] in PROJECT.md to generate .cursorrules, copilot-instructions.md, etc.`);
543
543
  }
544
+ for (const line of nextStepsAfterAdapt(meta.aiTools))
545
+ console.log(line);
544
546
  }
545
547
  return 0;
546
548
  }
549
+ /**
550
+ * What to do AFTER the configs exist.
551
+ *
552
+ * Telemetry showed `adapt` at 69 of 100 recorded runs against 2 for `board`:
553
+ * people run it, come back, run it again. Both of this command's closing lines
554
+ * used to point back at itself ("re-run after editing…", "add more tools…") and
555
+ * nothing pointed forward, so the command taught its own loop.
556
+ *
557
+ * The pointer is tool-aware on purpose. `adapt` is the one command that serves
558
+ * non-Claude-Code users (Cursor / Copilot / Windsurf / Aider), and the agent
559
+ * pipeline is Claude-Code-only — telling a Cursor user to run `/audit` would be
560
+ * a dead end, which is worse than saying nothing. So: pipeline steps only where
561
+ * the pipeline exists; otherwise say plainly what they have and what it would
562
+ * take to get the rest.
563
+ */
564
+ export function nextStepsAfterAdapt(aiTools = []) {
565
+ const hasClaude = aiTools.includes("claude-code");
566
+ const out = ["", "Next:"];
567
+ if (hasClaude) {
568
+ out.push(" 1. Restart Claude Code so it picks up the regenerated CLAUDE.md.", " 2. /audit — full analysis of this codebase (existing project)", " /start — scope and build a new feature (greenfield)", " /inbox — what needs your decision right now", " 3. great-cto board — Kanban + CTO dashboard at localhost:3141");
569
+ }
570
+ else {
571
+ out.push(` Your ${aiTools.join(" / ")} config is live — reload the tool to pick it up.`, " The agent pipeline (architecture review, QA, security gates) runs inside", " Claude Code. To use it here: add claude-code to ai_tools in PROJECT.md,", " then `npx great-cto install`.");
572
+ }
573
+ return out;
574
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "great-cto",
3
- "version": "2.86.0",
3
+ "version": "2.87.1",
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",