great-cto 3.68.1 → 3.69.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.
@@ -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 72 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.68.1",
4
+ "version": "3.69.0",
5
5
  "author": {
6
6
  "name": "Alexander Velikiy",
7
7
  "url": "https://hashnode.com/@Greatcto"
@@ -2,6 +2,7 @@
2
2
  name: coordinator
3
3
  description: "Multi-agent coordinator. Use when a CTO request spans 3+ independent work streams, requires parallel research before implementation, or the task graph is complex enough that sequencing matters. Orchestrates agents across the full DECOMPOSE→CLASSIFY→DISPATCH→MONITOR→SYNTHESIZE→VERIFY lifecycle."
4
4
  model: sonnet
5
+ color: purple
5
6
  authority: autonomous
6
7
  tools: Read, Write, Edit, Bash, Glob, Grep, Agent
7
8
  allowed-tools: Read, Write, Edit, Bash, Glob, Grep, Agent
@@ -10,6 +10,7 @@ import { readVerdicts } from './verdicts.mjs';
10
10
  // dispatcher enforces the same verdict — one definition, so the board cannot
11
11
  // show `within` while the dispatcher holds the stage.
12
12
  import { parseAgentBudgets, judgeAgentBudget } from '../../../scripts/lib/agent-budget.mjs';
13
+ import { classifyAgentGroup } from '../../../scripts/lib/agent-groups.mjs';
13
14
 
14
15
  // ── Agent fleet view (DESIGN-agents-fleet-view §3) ─────────────────────────
15
16
  //
@@ -33,20 +34,7 @@ const AGENTS_DIR = process.env.GREAT_CTO_AGENTS_DIR
33
34
  || path.join(os.homedir(), '.claude', 'agents');
34
35
 
35
36
  function deriveDomain(slug) {
36
- const s = slug.toLowerCase();
37
- if (/architect|adr|design|prompt/.test(s)) return 'arch';
38
- if (/security|sec-|threat|pci|gdpr|hipaa/.test(s)) return 'security';
39
- // `review` used to be in this alternation, and it matched every `*-reviewer`
40
- // slug — so 37 of the 40 agents in `qa` were domain reviewers, the `domain`
41
- // bucket below was permanently empty, and the panel grouped 58% of the fleet
42
- // under one heading. The rule that was supposed to separate them sat two lines
43
- // later and could never be reached.
44
- if (/\bqa\b|test|eval|^code-reviewer$/.test(s)) return 'qa';
45
- if (/devops|deploy|infra|l3|support|oncall/.test(s)) return 'ops';
46
- if (/reviewer$/.test(s)) return 'domain';
47
- if (/pm|plan|product/.test(s)) return 'pm';
48
- if (/learn|memory|continuous/.test(s)) return 'memory';
49
- return 'other';
37
+ return classifyAgentGroup(slug);
50
38
  }
51
39
 
52
40
  // Founder Q#5 — picked: baked-in regex set (start small, expand later).
@@ -0,0 +1,41 @@
1
+ /* Canonical role-group glyphs and classification. Pure classic script: loaded
2
+ * by the browser and imported by the Node bridge; no DOM, fetch or dependencies.
3
+ * The site vendors these exact bytes. Glyphs describe roles, never run status. */
4
+ (function (root) {
5
+ 'use strict';
6
+ const groups = Object.freeze([
7
+ { key: 'pm', label: 'Plan', svg: '<rect x="6" y="4" width="12" height="17" rx="2"/><path d="M9 4h6v3H9zM9 12h6M9 16h4"/>' },
8
+ { key: 'arch', label: 'Architecture', svg: '<path d="M12 3l9 5-9 5-9-5z"/><path d="M3 13l9 5 9-5"/>' },
9
+ { key: 'build', label: 'Build', svg: '<path d="M8 7l-5 5 5 5M16 7l5 5-5 5M14 4l-4 16"/>' },
10
+ { key: 'qa', label: 'Test & review', svg: '<circle cx="12" cy="12" r="9"/><path d="M8 12l3 3 5-6"/>' },
11
+ { key: 'security', label: 'Security', svg: '<path d="M12 3l8 3v6c0 5-3.5 8-8 9-4.5-1-8-4-8-9V6z"/>' },
12
+ { key: 'domain', label: 'Domain reviewers', svg: '<path d="M12 4v16M8 20h8M5 8h14M5 8l-2 6h4zM19 8l-2 6h4z"/>' },
13
+ { key: 'ops', label: 'Ops', svg: '<rect x="4" y="4" width="16" height="6" rx="1"/><rect x="4" y="14" width="16" height="6" rx="1"/><path d="M8 7h.01M8 17h.01"/>' },
14
+ { key: 'memory', label: 'Memory', svg: '<path d="M6 3h12v18l-6-4-6 4z"/>' },
15
+ { key: 'other', label: 'Other', svg: '<circle cx="12" cy="12" r="3"/>' },
16
+ ].map(Object.freeze));
17
+ // Order matters: test/eval before builders; domain reviewers before generic
18
+ // engineers. These are display hints, not pipeline routing or permissions.
19
+ const rules = [
20
+ [/architect|adr|design|prompt|decision-scorer|project-auditor/, 'arch'],
21
+ [/security|sec-|threat|pci|gdpr|hipaa|smart-contract/, 'security'],
22
+ [/\bqa\b|test|eval|performance|^code-reviewer$/, 'qa'],
23
+ [/devops|deploy|infra|l3|support|oncall/, 'ops'],
24
+ [/reviewer$/, 'domain'],
25
+ [/pm|plan|product|coordinator/, 'pm'],
26
+ [/learn|memory|continuous|knowledge/, 'memory'],
27
+ [/^senior-dev$|-engineer$|-builder$|scaffolder|ci-resolver/, 'build'],
28
+ ];
29
+ function classify(slug) {
30
+ const value = typeof slug === 'string' ? slug.toLowerCase() : '';
31
+ return rules.find(([pattern]) => pattern.test(value))?.[1] || 'other';
32
+ }
33
+ const group = key => groups.find(g => g.key === key) || groups[groups.length - 1];
34
+ const label = key => group(key).label;
35
+ function icon(key) {
36
+ const g = group(key);
37
+ // All markup and attributes below come from authored constants, never key.
38
+ return `<svg class="grp-icon" data-agent-group="${g.key}" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">${g.svg}</svg>`;
39
+ }
40
+ root.GctoAgentGroups = Object.freeze({ groups, classify, label, icon });
41
+ })(globalThis);
@@ -1524,6 +1524,9 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1524
1524
  .bar > i { display: block; height: 100%; background: var(--accent); }
1525
1525
  /* Fleet (great_cto-ki1x.8): saved views in the sidebar, domain groups, AgentRow cells */
1526
1526
  .fleet-group-head { display: flex; align-items: center; gap: 8px; padding: 8px 12px 4px; font-size: var(--fs-eyebrow); letter-spacing: .08em; text-transform: uppercase; color: var(--text2); font-weight: 500; }
1527
+ .fleet-group-head .grp-icon, .agent-row .grp-icon, #agent-drawer-title .grp-icon { width: 14px; height: 14px; flex-shrink: 0; color: var(--text2); vertical-align: -2px; }
1528
+ .agent-row .grp-icon { margin-right: 6px; color: var(--text3); }
1529
+ #agent-drawer-title .grp-icon { margin-right: 6px; }
1527
1530
  .fleet-group-collapsed { display: flex; flex-wrap: wrap; gap: 20px; padding: 10px 12px; font-size: var(--fs-eyebrow); letter-spacing: .08em; text-transform: uppercase; color: var(--text3); }
1528
1531
  .ar-cells { display: grid; grid-template-columns: minmax(200px, 1.4fr) 130px 48px 56px 80px auto; gap: 12px; align-items: center; margin-top: 4px; }
1529
1532
  .ar-posture { display: flex; gap: 6px; flex-wrap: wrap; }
@@ -3395,6 +3398,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
3395
3398
  </style>
3396
3399
  </head>
3397
3400
  <body>
3401
+ <script src="/assets/agent-groups.js"></script>
3398
3402
  <script src="/assets/work-view.js"></script>
3399
3403
  <div class="board">
3400
3404
  <!-- Subtle emerald glow background -->
@@ -9768,7 +9772,7 @@ function renderFleetList() {
9768
9772
  // Groups come from deriveDomain() on the server — one taxonomy, not two. A
9769
9773
  // group with rows in this view is expanded; the rest are one line each with
9770
9774
  // their count, so 70 agents fit a screen without a scroll or a virtualizer.
9771
- const DOMAINS = ['arch', 'security', 'qa', 'ops', 'domain', 'pm', 'memory', 'other'];
9775
+ const DOMAINS = AGENT_GROUPS.map(g => g.key);
9772
9776
  const totals = {}; for (const a of fleetState.agents) totals[a.domain] = (totals[a.domain] || 0) + 1;
9773
9777
  const byDomain = {}; for (const a of filtered) (byDomain[a.domain] = byDomain[a.domain] || []).push(a);
9774
9778
  const order = [...DOMAINS, ...Object.keys(byDomain).filter((d) => !DOMAINS.includes(d))];
@@ -9781,14 +9785,21 @@ function renderFleetList() {
9781
9785
  : `<li class="fleet-group-collapsed"><span class="abs">${esc(uu.state === 'computing' ? 'counting dispatches in the session logs…' : (uu.why || 'dispatches not counted'))}</span></li>`;
9782
9786
  const head = usageLine + `<li class="fleet-head" aria-hidden="true"><span></span><span>Agent</span><span>Posture</span><span>Last verdict</span><span class="tab">Runs</span><span class="tab">Pass</span><span class="tab">30d $</span><span></span></li>`;
9783
9787
  const rows = head + expanded.map((d) => `
9784
- <li class="fleet-group-head"><span>${esc(d)} · ${byDomain[d].length} of ${totals[d] || 0}</span></li>
9788
+ <li class="fleet-group-head">${groupIcon(d)}<span>${esc(groupLabel(d))} · ${byDomain[d].length} of ${totals[d] || 0}</span></li>
9785
9789
  ${byDomain[d].map(agentRowHtml).join('')}`).join('');
9786
9790
  const tail = collapsed.length
9787
- ? `<li class="fleet-group-collapsed">${collapsed.map((d) => `<span title="no ${esc(d)} agent is in this view">${esc(d)} · 0 of ${totals[d]}</span>`).join('')}</li>`
9791
+ ? `<li class="fleet-group-collapsed">${collapsed.map((d) => `<span title="no ${esc(groupLabel(d))} agent is in this view">${esc(groupLabel(d))} · 0 of ${totals[d]}</span>`).join('')}</li>`
9788
9792
  : '';
9789
9793
  el.innerHTML = rows + tail;
9790
9794
  }
9791
9795
 
9796
+ // One glyph per role group, so a reviewer and a builder differ at a glance —
9797
+ // 72 agents share 10 frontmatter colors, and a color says nothing about the role.
9798
+ // Keys are deriveDomain() on the server; order is the pipeline's.
9799
+ const AGENT_GROUPS = window.GctoAgentGroups?.groups || [];
9800
+ function groupLabel(key) { return window.GctoAgentGroups?.label(key) || key; }
9801
+ function groupIcon(key) { return window.GctoAgentGroups?.icon(key) || ''; }
9802
+
9792
9803
  // AgentRow (DESIGN §6.4 / §8.5): slug · posture · last verdict · runs · pass · 30d $.
9793
9804
  // Numbers that can be summed are tabular and right-aligned; identifiers are not.
9794
9805
  // Every absence carries its reason on hover.
@@ -9850,7 +9861,7 @@ function agentRowHtml(a) {
9850
9861
  onclick="openAgentDrawer('${esc(a.slug)}')"
9851
9862
  onkeydown="if(event.key==='Enter'){event.preventDefault();openAgentDrawer('${esc(a.slug)}')}">
9852
9863
  <span class="ar-status" aria-hidden="true"></span>
9853
- <span class="ar-name">${esc(a.slug)} <span class="mono cap muted" style="font-weight:400">${model}</span>${a.retired ? ' <span class="cap" style="color:var(--agent-retired)">retired</span>' : ''}</span>
9864
+ <span class="ar-name">${groupIcon(a.domain)}${esc(a.slug)} <span class="mono cap muted" style="font-weight:400">${model}</span>${a.retired ? ' <span class="cap" style="color:var(--agent-retired)">retired</span>' : ''}</span>
9854
9865
  <span class="ar-posture">${requiredChip(a)}${postureChips(a)}</span>
9855
9866
  <span class="mono cap ar-last">${last}</span>
9856
9867
  <span class="mono cap tab" title="runs in the last 30 days">${runs}</span>
@@ -9979,7 +9990,7 @@ function renderAgentProfile(p, extra = {}) {
9979
9990
  const title = document.getElementById('agent-drawer-title');
9980
9991
  const pill = document.getElementById('agent-drawer-pill');
9981
9992
  if (!body) return;
9982
- title.textContent = p.slug;
9993
+ title.innerHTML = groupIcon(window.GctoAgentGroups?.classify(p.slug)) + esc(p.slug);
9983
9994
  const row = (fleetState.agents || []).find((a) => a.slug === p.slug) || {};
9984
9995
  const tier = extra.tier;
9985
9996
  const tierChip = tier
@@ -0,0 +1,8 @@
1
+ // One implementation for Node readers and the classic-script board. The
2
+ // side-effect import also works in the site's committed CommonJS snapshot.
3
+ import '../../packages/board/public/assets/agent-groups.js';
4
+ const api = globalThis.GctoAgentGroups;
5
+ export const agentGroups = api.groups;
6
+ export const classifyAgentGroup = api.classify;
7
+ export const agentGroupLabel = api.label;
8
+ export const agentGroupIcon = api.icon;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "great-cto",
3
- "version": "3.68.1",
3
+ "version": "3.69.0",
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",