great-cto 3.63.4 → 3.64.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.
- package/board/.claude-plugin/plugin.json +1 -1
- package/board/agents/accounting-reviewer.md +241 -0
- package/board/agents/adtech-privacy-reviewer.md +152 -0
- package/board/agents/ai-eval-engineer.md +294 -0
- package/board/agents/ai-prompt-architect.md +211 -0
- package/board/agents/ai-security-reviewer.md +192 -0
- package/board/agents/api-platform-reviewer.md +176 -0
- package/board/agents/app-scaffolder.md +109 -0
- package/board/agents/architect.md +995 -0
- package/board/agents/auth-engineer.md +139 -0
- package/board/agents/ci-resolver.md +134 -0
- package/board/agents/cli-reviewer.md +173 -0
- package/board/agents/cmmc-reviewer.md +128 -0
- package/board/agents/cms-reviewer.md +242 -0
- package/board/agents/code-reviewer.md +234 -0
- package/board/agents/connector-builder.md +144 -0
- package/board/agents/continuous-learner.md +301 -0
- package/board/agents/coordinator.md +483 -0
- package/board/agents/data-platform-reviewer.md +202 -0
- package/board/agents/db-migration-reviewer.md +289 -0
- package/board/agents/decision-scorer.md +209 -0
- package/board/agents/design-advisor.md +328 -0
- package/board/agents/devops.md +840 -0
- package/board/agents/devtools-reviewer.md +198 -0
- package/board/agents/dpdpa-reviewer.md +137 -0
- package/board/agents/e2e-test-engineer.md +73 -0
- package/board/agents/edtech-reviewer.md +176 -0
- package/board/agents/enterprise-saas-reviewer.md +195 -0
- package/board/agents/firmware-reviewer.md +187 -0
- package/board/agents/game-reviewer.md +173 -0
- package/board/agents/gdpr-reviewer.md +143 -0
- package/board/agents/geo-routing-engineer.md +144 -0
- package/board/agents/gov-reviewer.md +178 -0
- package/board/agents/growth-engineer.md +242 -0
- package/board/agents/healthcare-reviewer.md +138 -0
- package/board/agents/hr-ai-reviewer.md +194 -0
- package/board/agents/infra-provisioner.md +148 -0
- package/board/agents/infra-reviewer.md +173 -0
- package/board/agents/insurance-reviewer.md +295 -0
- package/board/agents/integrations-engineer.md +190 -0
- package/board/agents/knowledge-extractor.md +215 -0
- package/board/agents/l3-support.md +727 -0
- package/board/agents/legal-reviewer.md +248 -0
- package/board/agents/library-reviewer.md +213 -0
- package/board/agents/marketplace-reviewer.md +257 -0
- package/board/agents/mcp-server-reviewer.md +268 -0
- package/board/agents/media-pipeline-engineer.md +140 -0
- package/board/agents/migration-import-engineer.md +169 -0
- package/board/agents/mlops-reviewer.md +200 -0
- package/board/agents/mobile-app-builder.md +152 -0
- package/board/agents/mobile-store-reviewer.md +152 -0
- package/board/agents/msp-reviewer.md +217 -0
- package/board/agents/oracle-reviewer.md +203 -0
- package/board/agents/pci-reviewer.md +134 -0
- package/board/agents/performance-engineer.md +301 -0
- package/board/agents/pm.md +676 -0
- package/board/agents/procurement-reviewer.md +200 -0
- package/board/agents/product-owner.md +379 -0
- package/board/agents/project-auditor.md +730 -0
- package/board/agents/qa-engineer.md +939 -0
- package/board/agents/quant-researcher.md +199 -0
- package/board/agents/rcm-reviewer.md +211 -0
- package/board/agents/regulated-reviewer.md +309 -0
- package/board/agents/security-officer.md +1014 -0
- package/board/agents/senior-dev.md +732 -0
- package/board/agents/smart-contract-auditor.md +336 -0
- package/board/agents/streaming-reviewer.md +224 -0
- package/board/agents/subscription-billing-engineer.md +187 -0
- package/board/agents/tax-reviewer.md +240 -0
- package/board/agents/us-ai-reviewer.md +124 -0
- package/board/agents/us-privacy-reviewer.md +126 -0
- package/board/agents/voice-ai-reviewer.md +228 -0
- package/board/agents/web-store-reviewer.md +242 -0
- package/board/packages/board/public/index.html +50 -12
- package/dist/board-path.js +18 -10
- package/package.json +1 -1
|
@@ -3480,6 +3480,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
3480
3480
|
<div class="nav-views-label" id="nav-views-label" hidden>Views</div>
|
|
3481
3481
|
<!-- Fleet filters share the row pattern, but stay buttons, not destinations. -->
|
|
3482
3482
|
<div class="nav-views" hidden>
|
|
3483
|
+
<div class="nav-view-item" id="nav-view-project" role="button" tabindex="0" onclick="setFleetView('project')" onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();setFleetView('project')}">Project focus</div>
|
|
3483
3484
|
<div class="nav-view-item" id="nav-view-needs" role="button" tabindex="0" onclick="setFleetView('needs')" onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();setFleetView('needs')}">Needs attention</div>
|
|
3484
3485
|
<div class="nav-view-item" id="nav-view-expensive" role="button" tabindex="0" onclick="setFleetView('expensive')" onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();setFleetView('expensive')}">Expensive grants</div>
|
|
3485
3486
|
<div class="nav-view-item" id="nav-view-never" role="button" tabindex="0" onclick="setFleetView('never')" onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();setFleetView('never')}">Never observed</div>
|
|
@@ -3795,6 +3796,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
3795
3796
|
<section aria-label="Fleet summary" class="fleet-title-row">
|
|
3796
3797
|
<h1 class="page-title" id="fleet-view-title" style="margin:0">Fleet</h1>
|
|
3797
3798
|
</section>
|
|
3799
|
+
<p id="fleet-focus-note" class="muted" role="status"></p>
|
|
3798
3800
|
|
|
3799
3801
|
<!-- §3.1 Fleet controls -->
|
|
3800
3802
|
<section aria-label="Fleet controls" class="fleet-controls-row">
|
|
@@ -9463,13 +9465,10 @@ const fleetState = {
|
|
|
9463
9465
|
agents: [],
|
|
9464
9466
|
summary: null,
|
|
9465
9467
|
legacy_count: 0,
|
|
9466
|
-
//
|
|
9467
|
-
// doing work on most projects; showing all 35 by default is noise.
|
|
9468
|
-
// User flips activity to `all` to see the full installation.
|
|
9468
|
+
// View membership is distinct from optional activity filters.
|
|
9469
9469
|
filters: { domain: 'all', activity: 'all', health: 'all' },
|
|
9470
|
-
//
|
|
9471
|
-
|
|
9472
|
-
view: 'needs',
|
|
9470
|
+
// Presentation only: dispatch and required-reviewer policy are unchanged.
|
|
9471
|
+
view: 'project',
|
|
9473
9472
|
search: '',
|
|
9474
9473
|
sort: 'runs_30d',
|
|
9475
9474
|
drawerSlug: null,
|
|
@@ -9479,15 +9478,32 @@ const fleetState = {
|
|
|
9479
9478
|
// Reviewers this project requires (/api/required-reviewers) — gate:ship refuses
|
|
9480
9479
|
// while one has no verdict.
|
|
9481
9480
|
required: null,
|
|
9481
|
+
requiredScope: null,
|
|
9482
|
+
requiredRequest: 0,
|
|
9483
|
+
rosterRequest: 0,
|
|
9484
|
+
rosterScope: null,
|
|
9482
9485
|
};
|
|
9486
|
+
const FLEET_CORE_ROLES = new Set([
|
|
9487
|
+
'product-owner', 'architect', 'pm', 'senior-dev', 'code-reviewer',
|
|
9488
|
+
'qa-engineer', 'security-officer', 'devops',
|
|
9489
|
+
]);
|
|
9490
|
+
function fleetRequirementsKnown() {
|
|
9491
|
+
return fleetState.requiredScope === pqs() && fleetState.required?.state === 'read'
|
|
9492
|
+
&& Array.isArray(fleetState.required.reviewers);
|
|
9493
|
+
}
|
|
9483
9494
|
function requiredOf(slug) {
|
|
9484
9495
|
const r = fleetState.required;
|
|
9485
|
-
return
|
|
9496
|
+
return fleetRequirementsKnown() ? r.reviewers.find((x) => x.agent === slug) || null : null;
|
|
9486
9497
|
}
|
|
9487
9498
|
async function refreshRequiredReviewers() {
|
|
9488
|
-
const
|
|
9489
|
-
|
|
9499
|
+
const scope = pqs(), request = ++fleetState.requiredRequest;
|
|
9500
|
+
fleetState.required = null;
|
|
9501
|
+
fleetState.requiredScope = null;
|
|
9502
|
+
renderFleetList();
|
|
9503
|
+
const r = await api(`/api/required-reviewers${scope}`);
|
|
9504
|
+
if (request !== fleetState.requiredRequest || scope !== pqs()) return;
|
|
9490
9505
|
fleetState.required = r;
|
|
9506
|
+
fleetState.requiredScope = scope;
|
|
9491
9507
|
renderFleetList();
|
|
9492
9508
|
}
|
|
9493
9509
|
|
|
@@ -9507,10 +9523,16 @@ async function refreshAgentUsage() {
|
|
|
9507
9523
|
}
|
|
9508
9524
|
|
|
9509
9525
|
async function refreshAgentsInstalled() {
|
|
9526
|
+
const scope = pqs(), request = ++fleetState.rosterRequest;
|
|
9527
|
+
if (fleetState.rosterScope !== scope) {
|
|
9528
|
+
fleetState.agents = [];
|
|
9529
|
+
fleetState.summary = null;
|
|
9530
|
+
fleetState.rosterScope = scope;
|
|
9531
|
+
}
|
|
9510
9532
|
refreshAgentUsage();
|
|
9511
9533
|
refreshRequiredReviewers();
|
|
9512
|
-
const data = await api(`/api/agents-installed${
|
|
9513
|
-
if (!data) return;
|
|
9534
|
+
const data = await api(`/api/agents-installed${scope}`);
|
|
9535
|
+
if (!data || request !== fleetState.rosterRequest || scope !== pqs()) return;
|
|
9514
9536
|
fleetState.agents = data.agents || [];
|
|
9515
9537
|
fleetState.summary = data.summary || null;
|
|
9516
9538
|
// If this project has no verdicts at all, show all agents (not just 'used').
|
|
@@ -9534,6 +9556,7 @@ async function refreshAgentsInstalled() {
|
|
|
9534
9556
|
|
|
9535
9557
|
|
|
9536
9558
|
const FLEET_VIEWS = [
|
|
9559
|
+
['project', 'Project focus'],
|
|
9537
9560
|
['needs', 'Needs attention'], ['expensive', 'Expensive grants'], ['never', 'Never observed'], ['all', 'All agents'], ['retired', 'Retired'],
|
|
9538
9561
|
];
|
|
9539
9562
|
// What "needs attention" means, in one place: failing, an unjudged posture, a
|
|
@@ -9548,6 +9571,11 @@ function agentNeedsAttention(a) {
|
|
|
9548
9571
|
|| String(a.budget?.state || '').startsWith('over');
|
|
9549
9572
|
}
|
|
9550
9573
|
function inFleetView(a, view) {
|
|
9574
|
+
if (view === 'project') {
|
|
9575
|
+
// An unavailable policy is not evidence that a specialist is unnecessary.
|
|
9576
|
+
if (!fleetRequirementsKnown()) return !a.retired;
|
|
9577
|
+
return !!requiredOf(a.slug) || (!a.retired && FLEET_CORE_ROLES.has(a.slug));
|
|
9578
|
+
}
|
|
9551
9579
|
if (view === 'retired') return !!a.retired;
|
|
9552
9580
|
if (a.retired) return false;
|
|
9553
9581
|
if (view === 'needs') return agentNeedsAttention(a);
|
|
@@ -9568,6 +9596,16 @@ function setFleetView(view) {
|
|
|
9568
9596
|
// a count, because "2" would read as two new items.
|
|
9569
9597
|
function renderFleetViews() {
|
|
9570
9598
|
const agents = fleetState.agents || [];
|
|
9599
|
+
const note = document.getElementById('fleet-focus-note');
|
|
9600
|
+
if (note) {
|
|
9601
|
+
note.hidden = fleetState.view !== 'project';
|
|
9602
|
+
const attention = agents.filter(a => inFleetView(a, 'needs')).length;
|
|
9603
|
+
const missing = fleetRequirementsKnown()
|
|
9604
|
+
? fleetState.required.reviewers.filter(r => !agents.some(a => a.slug === r.agent)).map(r => r.agent) : [];
|
|
9605
|
+
note.textContent = fleetRequirementsKnown()
|
|
9606
|
+
? `Core roles and this project's required reviewers. ${missing.length} required reviewer(s) missing from the installed catalog${missing.length ? ': ' + missing.join(', ') : ''}. ${attention} installed agent(s) need attention across the full catalog; open Needs attention. Display only, not an execution plan or proof of expertise.`
|
|
9607
|
+
: 'Project reviewer requirements are unknown; showing the installed catalog rather than hiding specialists. Display only; execution policy is unchanged.';
|
|
9608
|
+
}
|
|
9571
9609
|
for (const [key, label] of FLEET_VIEWS) {
|
|
9572
9610
|
const el = document.getElementById(`nav-view-${key}`);
|
|
9573
9611
|
if (!el) continue;
|
|
@@ -9601,7 +9639,7 @@ function applyFleetFilters(agents) {
|
|
|
9601
9639
|
if (activity === 'idle' && (a.runs_30d > 0 || a.runs_total === 0)) return false;
|
|
9602
9640
|
if (activity === 'never' && a.runs_total > 0) return false;
|
|
9603
9641
|
if (activity === 'retired' && !a.retired) return false;
|
|
9604
|
-
if (activity !== 'retired' && a.retired) return false;
|
|
9642
|
+
if (activity !== 'retired' && a.retired && !(fleetState.view === 'project' && requiredOf(a.slug))) return false;
|
|
9605
9643
|
if (health === 'ok' && a.health === 'failing') return false;
|
|
9606
9644
|
if (health === 'failing' && a.health !== 'failing') return false;
|
|
9607
9645
|
return true;
|
package/dist/board-path.js
CHANGED
|
@@ -8,7 +8,8 @@ import { fileURLToPath } from "node:url";
|
|
|
8
8
|
* Locate the board server.mjs. Order:
|
|
9
9
|
* 1. dev layouts (running from a repo checkout)
|
|
10
10
|
* 2. installed plugin cache — ANY marketplace under ~/.claude/plugins/cache,
|
|
11
|
-
* newest 5 versions each
|
|
11
|
+
* newest 5 versions each, globally ordered by version across marketplaces
|
|
12
|
+
* and both plugin directory names (not just the "local" marketplace)
|
|
12
13
|
* 3. the copy bundled into the npm package by scripts/bundle-board.mjs —
|
|
13
14
|
* guaranteed fallback so a fresh `npm i -g great-cto` can run the board
|
|
14
15
|
* without the plugin installed
|
|
@@ -25,8 +26,8 @@ import { fileURLToPath } from "node:url";
|
|
|
25
26
|
* beats the bundle. When the newest installed plugin is OLDER than the running
|
|
26
27
|
* CLI, the bundled board — which ships inside that CLI and is therefore exactly
|
|
27
28
|
* as new as it is — wins. An equal or newer plugin still wins, because a plugin
|
|
28
|
-
* install is the richer one: it carries
|
|
29
|
-
*
|
|
29
|
+
* install is the richer one: it carries skills and commands in addition to the
|
|
30
|
+
* public role data now included in the npm board runtime.
|
|
30
31
|
*
|
|
31
32
|
* @param cliVersion the running CLI's version. Omitted → previous behaviour, so
|
|
32
33
|
* a caller that cannot determine it never gets a silently different answer.
|
|
@@ -44,8 +45,7 @@ export function findBoardServerPath(baseDir, home, cliVersion) {
|
|
|
44
45
|
const pa = a.split(".").map(Number), pb = b.split(".").map(Number);
|
|
45
46
|
return (pb[0] - pa[0]) || (pb[1] - pa[1]) || (pb[2] - pa[2]) || 0;
|
|
46
47
|
};
|
|
47
|
-
const
|
|
48
|
-
let newestPlugin = null;
|
|
48
|
+
const plugins = [];
|
|
49
49
|
const cacheRoot = join(home ?? homedir(), ".claude", "plugins", "cache");
|
|
50
50
|
if (fsExistsSync(cacheRoot)) {
|
|
51
51
|
try {
|
|
@@ -58,17 +58,25 @@ export function findBoardServerPath(baseDir, home, cliVersion) {
|
|
|
58
58
|
const pluginBase = join(cacheRoot, marketplace, dirName);
|
|
59
59
|
if (!fsExistsSync(pluginBase))
|
|
60
60
|
continue;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
try {
|
|
62
|
+
const versions = readdirSync(pluginBase).filter(v => /^\d/.test(v)).sort(byVer);
|
|
63
|
+
for (const version of versions.slice(0, 5)) {
|
|
64
|
+
const path = join(pluginBase, version, "packages", "board", "server.mjs");
|
|
65
|
+
// A partial installation must not advertise freshness on behalf
|
|
66
|
+
// of an older board that would actually be selected.
|
|
67
|
+
if (fsExistsSync(path))
|
|
68
|
+
plugins.push({ version, path });
|
|
69
|
+
}
|
|
64
70
|
}
|
|
65
|
-
|
|
66
|
-
newestPlugin = versions[0];
|
|
71
|
+
catch { /* one unreadable cache must not hide other installations */ }
|
|
67
72
|
}
|
|
68
73
|
}
|
|
69
74
|
}
|
|
70
75
|
catch { /* ignore */ }
|
|
71
76
|
}
|
|
77
|
+
plugins.sort((a, b) => byVer(a.version, b.version) || a.path.localeCompare(b.path));
|
|
78
|
+
const pluginCandidates = plugins.map(plugin => plugin.path);
|
|
79
|
+
const newestPlugin = plugins[0]?.version;
|
|
72
80
|
// bundled copy (dist → ../board/packages/board/server.mjs)
|
|
73
81
|
const bundled = join(here, "..", "board", "packages", "board", "server.mjs");
|
|
74
82
|
// A plugin OLDER than the running CLI does not get to answer for it.
|
package/package.json
CHANGED