@girardmedia/bootspring 1.2.0 → 2.0.3
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/README.md +107 -14
- package/bin/bootspring.js +166 -27
- package/cli/agent.js +189 -17
- package/cli/analyze.js +499 -0
- package/cli/audit.js +557 -0
- package/cli/auth.js +495 -38
- package/cli/billing.js +302 -0
- package/cli/build.js +695 -0
- package/cli/business.js +109 -26
- package/cli/checkpoint-utils.js +168 -0
- package/cli/checkpoint.js +639 -0
- package/cli/cloud-sync.js +447 -0
- package/cli/content.js +198 -0
- package/cli/context.js +1 -1
- package/cli/deploy.js +543 -0
- package/cli/fundraise.js +112 -50
- package/cli/github-cmd.js +435 -0
- package/cli/health.js +477 -0
- package/cli/init.js +84 -13
- package/cli/legal.js +107 -95
- package/cli/log.js +2 -2
- package/cli/loop.js +976 -73
- package/cli/manager.js +711 -0
- package/cli/metrics.js +480 -0
- package/cli/monitor.js +812 -0
- package/cli/onboard.js +521 -0
- package/cli/orchestrator.js +12 -24
- package/cli/prd.js +594 -0
- package/cli/preseed-start.js +1483 -0
- package/cli/preseed.js +2302 -0
- package/cli/project.js +436 -0
- package/cli/quality.js +233 -0
- package/cli/security.js +913 -0
- package/cli/seed.js +1441 -5
- package/cli/skill.js +273 -211
- package/cli/suggest.js +989 -0
- package/cli/switch.js +453 -0
- package/cli/visualize.js +527 -0
- package/cli/watch.js +769 -0
- package/cli/workspace.js +607 -0
- package/core/analyze-workflow.js +1134 -0
- package/core/api-client.js +535 -22
- package/core/audit-workflow.js +1350 -0
- package/core/build-orchestrator.js +480 -0
- package/core/build-state.js +577 -0
- package/core/checkpoint-engine.js +408 -0
- package/core/config.js +1109 -26
- package/core/context-loader.js +21 -1
- package/core/deploy-workflow.js +836 -0
- package/core/entitlements.js +93 -22
- package/core/github-sync.js +610 -0
- package/core/index.js +8 -1
- package/core/ingest.js +1111 -0
- package/core/metrics-engine.js +768 -0
- package/core/onboard-workflow.js +1007 -0
- package/core/preseed-workflow.js +934 -0
- package/core/preseed.js +1617 -0
- package/core/project-context.js +325 -0
- package/core/project-state.js +694 -0
- package/core/r2-sync.js +583 -0
- package/core/scaffold.js +525 -7
- package/core/session.js +258 -0
- package/core/task-extractor.js +758 -0
- package/core/telemetry.js +28 -6
- package/core/tier-enforcement.js +737 -0
- package/core/utils.js +38 -14
- package/generators/questionnaire.js +15 -12
- package/generators/sections/ai.js +7 -7
- package/generators/sections/content.js +300 -0
- package/generators/sections/index.js +3 -0
- package/generators/sections/plugins.js +7 -6
- package/generators/templates/build-planning.template.js +596 -0
- package/generators/templates/content.template.js +819 -0
- package/generators/templates/index.js +2 -1
- package/hooks/git-autopilot.js +1250 -0
- package/hooks/index.js +9 -0
- package/intelligence/agent-collab.js +2057 -0
- package/intelligence/auto-suggest.js +634 -0
- package/intelligence/content-gen.js +1589 -0
- package/intelligence/cross-project.js +1647 -0
- package/intelligence/index.js +184 -0
- package/intelligence/learning/insights.json +517 -7
- package/intelligence/learning/pattern-learner.js +1008 -14
- package/intelligence/memory/decision-tracker.js +1431 -31
- package/intelligence/memory/decisions.jsonl +0 -0
- package/intelligence/orchestrator.js +2896 -1
- package/intelligence/prd.js +92 -1
- package/intelligence/recommendation-weights.json +14 -2
- package/intelligence/recommendations.js +463 -9
- package/intelligence/workflow-composer.js +1451 -0
- package/marketplace/index.d.ts +324 -0
- package/marketplace/index.js +1921 -0
- package/mcp/contracts/mcp-contract.v1.json +342 -4
- package/mcp/registry.js +680 -3
- package/mcp/response-formatter.js +23 -0
- package/mcp/tools/assist-tool.js +78 -4
- package/mcp/tools/autopilot-tool.js +408 -0
- package/mcp/tools/content-tool.js +571 -0
- package/mcp/tools/dashboard-tool.js +251 -5
- package/mcp/tools/mvp-tool.js +344 -0
- package/mcp/tools/plugin-tool.js +23 -1
- package/mcp/tools/prd-tool.js +579 -0
- package/mcp/tools/seed-tool.js +447 -0
- package/mcp/tools/skill-tool.js +43 -14
- package/mcp/tools/suggest-tool.js +147 -0
- package/package.json +15 -6
- package/agents/README.md +0 -93
- package/agents/ai-integration-expert/context.md +0 -386
- package/agents/api-expert/context.md +0 -416
- package/agents/architecture-expert/context.md +0 -454
- package/agents/auth-expert/context.md +0 -399
- package/agents/backend-expert/context.md +0 -483
- package/agents/business-strategy-expert/context.md +0 -180
- package/agents/code-review-expert/context.md +0 -365
- package/agents/competitive-analysis-expert/context.md +0 -239
- package/agents/data-modeling-expert/context.md +0 -352
- package/agents/database-expert/context.md +0 -250
- package/agents/devops-expert/context.md +0 -446
- package/agents/email-expert/context.md +0 -379
- package/agents/financial-expert/context.md +0 -213
- package/agents/frontend-expert/context.md +0 -364
- package/agents/fundraising-expert/context.md +0 -257
- package/agents/growth-expert/context.md +0 -249
- package/agents/index.js +0 -140
- package/agents/investor-relations-expert/context.md +0 -266
- package/agents/legal-expert/context.md +0 -284
- package/agents/marketing-expert/context.md +0 -236
- package/agents/monitoring-expert/context.md +0 -362
- package/agents/operations-expert/context.md +0 -279
- package/agents/partnerships-expert/context.md +0 -286
- package/agents/payment-expert/context.md +0 -340
- package/agents/performance-expert/context.md +0 -377
- package/agents/private-equity-expert/context.md +0 -246
- package/agents/railway-expert/context.md +0 -284
- package/agents/research-expert/context.md +0 -245
- package/agents/sales-expert/context.md +0 -241
- package/agents/security-expert/context.md +0 -343
- package/agents/testing-expert/context.md +0 -414
- package/agents/ui-ux-expert/context.md +0 -448
- package/agents/vercel-expert/context.md +0 -426
- package/skills/index.js +0 -787
- package/skills/patterns/README.md +0 -163
- package/skills/patterns/ai/agents.md +0 -281
- package/skills/patterns/ai/claude.md +0 -138
- package/skills/patterns/ai/embeddings.md +0 -150
- package/skills/patterns/ai/rag.md +0 -266
- package/skills/patterns/ai/streaming.md +0 -170
- package/skills/patterns/ai/structured-output.md +0 -162
- package/skills/patterns/ai/tools.md +0 -154
- package/skills/patterns/analytics/tracking.md +0 -220
- package/skills/patterns/api/errors.md +0 -296
- package/skills/patterns/api/graphql.md +0 -440
- package/skills/patterns/api/middleware.md +0 -279
- package/skills/patterns/api/openapi.md +0 -285
- package/skills/patterns/api/rate-limiting.md +0 -231
- package/skills/patterns/api/route-handler.md +0 -217
- package/skills/patterns/api/server-action.md +0 -249
- package/skills/patterns/api/versioning.md +0 -443
- package/skills/patterns/api/webhooks.md +0 -247
- package/skills/patterns/auth/clerk.md +0 -132
- package/skills/patterns/auth/mfa.md +0 -313
- package/skills/patterns/auth/nextauth.md +0 -140
- package/skills/patterns/auth/oauth.md +0 -237
- package/skills/patterns/auth/rbac.md +0 -152
- package/skills/patterns/auth/session-management.md +0 -367
- package/skills/patterns/auth/session.md +0 -120
- package/skills/patterns/database/audit.md +0 -177
- package/skills/patterns/database/migrations.md +0 -177
- package/skills/patterns/database/pagination.md +0 -230
- package/skills/patterns/database/pooling.md +0 -357
- package/skills/patterns/database/prisma.md +0 -180
- package/skills/patterns/database/relations.md +0 -187
- package/skills/patterns/database/seeding.md +0 -246
- package/skills/patterns/database/soft-delete.md +0 -153
- package/skills/patterns/database/transactions.md +0 -162
- package/skills/patterns/deployment/ci-cd.md +0 -231
- package/skills/patterns/deployment/docker.md +0 -188
- package/skills/patterns/deployment/monitoring.md +0 -387
- package/skills/patterns/deployment/vercel.md +0 -160
- package/skills/patterns/email/resend.md +0 -143
- package/skills/patterns/email/templates.md +0 -245
- package/skills/patterns/email/transactional.md +0 -503
- package/skills/patterns/email/verification.md +0 -176
- package/skills/patterns/files/download.md +0 -243
- package/skills/patterns/files/upload.md +0 -239
- package/skills/patterns/i18n/nextintl.md +0 -188
- package/skills/patterns/logging/structured.md +0 -292
- package/skills/patterns/notifications/email-queue.md +0 -248
- package/skills/patterns/notifications/push.md +0 -279
- package/skills/patterns/payments/checkout.md +0 -303
- package/skills/patterns/payments/invoices.md +0 -287
- package/skills/patterns/payments/portal.md +0 -245
- package/skills/patterns/payments/stripe.md +0 -272
- package/skills/patterns/payments/subscriptions.md +0 -300
- package/skills/patterns/payments/usage.md +0 -279
- package/skills/patterns/performance/caching.md +0 -276
- package/skills/patterns/performance/code-splitting.md +0 -233
- package/skills/patterns/performance/edge.md +0 -254
- package/skills/patterns/performance/isr.md +0 -266
- package/skills/patterns/performance/lazy-loading.md +0 -281
- package/skills/patterns/realtime/sse.md +0 -327
- package/skills/patterns/realtime/websockets.md +0 -336
- package/skills/patterns/search/filtering.md +0 -329
- package/skills/patterns/search/fulltext.md +0 -260
- package/skills/patterns/security/audit-logging.md +0 -444
- package/skills/patterns/security/csrf.md +0 -234
- package/skills/patterns/security/headers.md +0 -252
- package/skills/patterns/security/sanitization.md +0 -258
- package/skills/patterns/security/secrets.md +0 -261
- package/skills/patterns/security/validation.md +0 -268
- package/skills/patterns/security/xss.md +0 -229
- package/skills/patterns/seo/metadata.md +0 -252
- package/skills/patterns/state/context.md +0 -349
- package/skills/patterns/state/react-query.md +0 -313
- package/skills/patterns/state/url-state.md +0 -482
- package/skills/patterns/state/zustand.md +0 -262
- package/skills/patterns/testing/api.md +0 -259
- package/skills/patterns/testing/component.md +0 -233
- package/skills/patterns/testing/coverage.md +0 -207
- package/skills/patterns/testing/fixtures.md +0 -225
- package/skills/patterns/testing/integration.md +0 -436
- package/skills/patterns/testing/mocking.md +0 -177
- package/skills/patterns/testing/playwright.md +0 -162
- package/skills/patterns/testing/snapshot.md +0 -175
- package/skills/patterns/testing/vitest.md +0 -307
- package/skills/patterns/ui/accordions.md +0 -395
- package/skills/patterns/ui/cards.md +0 -299
- package/skills/patterns/ui/dropdowns.md +0 -476
- package/skills/patterns/ui/empty-states.md +0 -320
- package/skills/patterns/ui/forms.md +0 -405
- package/skills/patterns/ui/inputs.md +0 -319
- package/skills/patterns/ui/layouts.md +0 -282
- package/skills/patterns/ui/loading.md +0 -291
- package/skills/patterns/ui/modals.md +0 -338
- package/skills/patterns/ui/navigation.md +0 -374
- package/skills/patterns/ui/tables.md +0 -407
- package/skills/patterns/ui/toasts.md +0 -300
- package/skills/patterns/ui/tooltips.md +0 -396
- package/skills/patterns/utils/dates.md +0 -435
- package/skills/patterns/utils/errors.md +0 -451
- package/skills/patterns/utils/formatting.md +0 -345
- package/skills/patterns/utils/validation.md +0 -434
- package/templates/bootspring.config.js +0 -83
- package/templates/business/business-model-canvas.md +0 -246
- package/templates/business/business-plan.md +0 -266
- package/templates/business/competitive-analysis.md +0 -312
- package/templates/fundraising/data-room-checklist.md +0 -300
- package/templates/fundraising/investor-research.md +0 -243
- package/templates/fundraising/pitch-deck-outline.md +0 -253
- package/templates/legal/gdpr-checklist.md +0 -339
- package/templates/legal/privacy-policy.md +0 -285
- package/templates/legal/terms-of-service.md +0 -222
- package/templates/mcp.json +0 -9
package/cli/metrics.js
ADDED
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootspring Metrics Command
|
|
3
|
+
* Collect and display project metrics
|
|
4
|
+
*
|
|
5
|
+
* @package bootspring
|
|
6
|
+
* @command metrics
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const config = require('../core/config');
|
|
10
|
+
const utils = require('../core/utils');
|
|
11
|
+
const metricsEngine = require('../core/metrics-engine');
|
|
12
|
+
const projectState = require('../core/project-state');
|
|
13
|
+
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Display Functions
|
|
16
|
+
// ============================================================================
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Create a visual bar
|
|
20
|
+
*/
|
|
21
|
+
function createBar(value, max = 100, width = 20) {
|
|
22
|
+
const filled = Math.round((value / max) * width);
|
|
23
|
+
const empty = width - filled;
|
|
24
|
+
const bar = '█'.repeat(filled) + '░'.repeat(empty);
|
|
25
|
+
return bar;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get color based on score
|
|
30
|
+
*/
|
|
31
|
+
function getScoreColor(score) {
|
|
32
|
+
if (score >= 80) return utils.COLORS.green;
|
|
33
|
+
if (score >= 60) return utils.COLORS.yellow;
|
|
34
|
+
if (score >= 40) return utils.COLORS.magenta;
|
|
35
|
+
return utils.COLORS.red;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Display metrics overview
|
|
40
|
+
*/
|
|
41
|
+
function displayMetrics(results, options = {}) {
|
|
42
|
+
console.log(`
|
|
43
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}📊 Project Metrics${utils.COLORS.reset}
|
|
44
|
+
${utils.COLORS.dim}Collected: ${new Date(results.timestamp).toLocaleString()}${utils.COLORS.reset}
|
|
45
|
+
`);
|
|
46
|
+
|
|
47
|
+
// Overall score
|
|
48
|
+
const scoreColor = getScoreColor(results.overallScore);
|
|
49
|
+
console.log(`${utils.COLORS.bold}Overall Score${utils.COLORS.reset}`);
|
|
50
|
+
console.log(` ${scoreColor}${utils.COLORS.bold}${results.overallScore}%${utils.COLORS.reset} ${createBar(results.overallScore)}`);
|
|
51
|
+
console.log();
|
|
52
|
+
|
|
53
|
+
// Category breakdown
|
|
54
|
+
console.log(`${utils.COLORS.bold}Categories${utils.COLORS.reset}`);
|
|
55
|
+
for (const [category, data] of Object.entries(results.categories)) {
|
|
56
|
+
const catColor = getScoreColor(data.average);
|
|
57
|
+
const catLabel = category.charAt(0).toUpperCase() + category.slice(1);
|
|
58
|
+
console.log(` ${catLabel.padEnd(12)} ${catColor}${createBar(data.average, 100, 15)}${utils.COLORS.reset} ${data.average}%`);
|
|
59
|
+
}
|
|
60
|
+
console.log();
|
|
61
|
+
|
|
62
|
+
// Individual metrics
|
|
63
|
+
if (!options.compact) {
|
|
64
|
+
console.log(`${utils.COLORS.bold}Metrics${utils.COLORS.reset}`);
|
|
65
|
+
|
|
66
|
+
for (const [category, data] of Object.entries(results.categories)) {
|
|
67
|
+
console.log(`\n ${utils.COLORS.cyan}${category.toUpperCase()}${utils.COLORS.reset}`);
|
|
68
|
+
|
|
69
|
+
for (const metric of data.metrics) {
|
|
70
|
+
const color = getScoreColor(metric.score);
|
|
71
|
+
const bar = createBar(metric.score, 100, 10);
|
|
72
|
+
const icon = metric.icon || '•';
|
|
73
|
+
console.log(` ${icon} ${metric.label.padEnd(20)} ${color}${bar}${utils.COLORS.reset} ${metric.score}%`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Display metrics dashboard (compact view)
|
|
81
|
+
*/
|
|
82
|
+
function displayDashboard(projectRoot) {
|
|
83
|
+
const state = projectState.loadState(projectRoot);
|
|
84
|
+
|
|
85
|
+
if (!state?.metrics) {
|
|
86
|
+
console.log(`${utils.COLORS.dim}No metrics collected yet. Run 'bootspring metrics collect' first.${utils.COLORS.reset}`);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const m = state.metrics;
|
|
91
|
+
const color = getScoreColor(m.overallScore);
|
|
92
|
+
|
|
93
|
+
console.log(`
|
|
94
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}📊 Metrics Dashboard${utils.COLORS.reset}
|
|
95
|
+
|
|
96
|
+
${utils.COLORS.bold}Score${utils.COLORS.reset} ${color}${m.overallScore}%${utils.COLORS.reset}
|
|
97
|
+
${utils.COLORS.dim}Last updated: ${new Date(m.lastCollected).toLocaleString()}${utils.COLORS.reset}
|
|
98
|
+
`);
|
|
99
|
+
|
|
100
|
+
// Categories
|
|
101
|
+
for (const [cat, data] of Object.entries(m.categories)) {
|
|
102
|
+
const catColor = getScoreColor(data.score);
|
|
103
|
+
const catLabel = cat.charAt(0).toUpperCase() + cat.slice(1);
|
|
104
|
+
console.log(`${catLabel.padEnd(12)} ${catColor}${data.score}%${utils.COLORS.reset}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Trend
|
|
108
|
+
if (m.history && m.history.length > 1) {
|
|
109
|
+
const trend = m.history[0].score - m.history[1].score;
|
|
110
|
+
const trendIcon = trend > 0 ? '↑' : trend < 0 ? '↓' : '→';
|
|
111
|
+
const trendColor = trend > 0 ? utils.COLORS.green : trend < 0 ? utils.COLORS.red : utils.COLORS.dim;
|
|
112
|
+
console.log(`\n${utils.COLORS.bold}Trend${utils.COLORS.reset} ${trendColor}${trendIcon} ${Math.abs(trend)}%${utils.COLORS.reset}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Display metrics comparison with previous
|
|
118
|
+
*/
|
|
119
|
+
function displayComparison(current, previous) {
|
|
120
|
+
console.log(`
|
|
121
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}📊 Metrics Comparison${utils.COLORS.reset}
|
|
122
|
+
`);
|
|
123
|
+
|
|
124
|
+
console.log(`${utils.COLORS.bold}Overall${utils.COLORS.reset}`);
|
|
125
|
+
const diff = current.overallScore - previous.overallScore;
|
|
126
|
+
const diffColor = diff > 0 ? utils.COLORS.green : diff < 0 ? utils.COLORS.red : utils.COLORS.dim;
|
|
127
|
+
const diffIcon = diff > 0 ? '↑' : diff < 0 ? '↓' : '→';
|
|
128
|
+
console.log(` ${previous.overallScore}% → ${current.overallScore}% ${diffColor}(${diffIcon}${Math.abs(diff)})${utils.COLORS.reset}`);
|
|
129
|
+
console.log();
|
|
130
|
+
|
|
131
|
+
console.log(`${utils.COLORS.bold}By Metric${utils.COLORS.reset}`);
|
|
132
|
+
for (const [metricId, currentMetric] of Object.entries(current.metrics)) {
|
|
133
|
+
const prevMetric = previous.metrics[metricId];
|
|
134
|
+
if (!prevMetric) continue;
|
|
135
|
+
|
|
136
|
+
const metricDef = metricsEngine.METRICS[metricId];
|
|
137
|
+
const label = metricDef?.label || metricId;
|
|
138
|
+
const metricDiff = currentMetric.score - prevMetric.score;
|
|
139
|
+
|
|
140
|
+
if (Math.abs(metricDiff) >= 5) {
|
|
141
|
+
const mDiffColor = metricDiff > 0 ? utils.COLORS.green : utils.COLORS.red;
|
|
142
|
+
const mDiffIcon = metricDiff > 0 ? '↑' : '↓';
|
|
143
|
+
console.log(` ${label.padEnd(20)} ${mDiffColor}${mDiffIcon}${Math.abs(metricDiff)}%${utils.COLORS.reset}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Generate badges markdown
|
|
150
|
+
*/
|
|
151
|
+
function generateBadges(metrics) {
|
|
152
|
+
const badges = [];
|
|
153
|
+
|
|
154
|
+
// Overall score badge
|
|
155
|
+
const score = metrics.overallScore;
|
|
156
|
+
const color = score >= 80 ? 'brightgreen' : score >= 60 ? 'yellow' : score >= 40 ? 'orange' : 'red';
|
|
157
|
+
badges.push(``);
|
|
158
|
+
|
|
159
|
+
// Category badges
|
|
160
|
+
for (const [cat, data] of Object.entries(metrics.categories)) {
|
|
161
|
+
const catColor = data.average >= 80 ? 'brightgreen' : data.average >= 60 ? 'yellow' : 'orange';
|
|
162
|
+
badges.push(``);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return badges.join(' ');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Sync metrics to remote dashboard
|
|
170
|
+
* Sends raw metric values - server applies weights and calculates scores
|
|
171
|
+
*/
|
|
172
|
+
async function syncToRemote(projectRoot, metrics) {
|
|
173
|
+
const cfg = config.load();
|
|
174
|
+
const auth = require('../core/auth');
|
|
175
|
+
const session = require('../core/session');
|
|
176
|
+
|
|
177
|
+
// Get API base URL
|
|
178
|
+
const apiBase = cfg.apiBase || cfg.api?.baseUrl || 'https://bootspring.com';
|
|
179
|
+
|
|
180
|
+
// Get project ID from session
|
|
181
|
+
const currentProject = session.getCurrentProject();
|
|
182
|
+
const projectId = currentProject?.id || cfg.projectId || cfg.project?.id;
|
|
183
|
+
if (!projectId) {
|
|
184
|
+
console.log(`${utils.COLORS.dim}No project selected. Run 'bootspring switch <project>' first.${utils.COLORS.reset}`);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Get auth
|
|
189
|
+
const apiKey = auth.getApiKey() || cfg.api?.token;
|
|
190
|
+
if (!apiKey) {
|
|
191
|
+
console.log(`${utils.COLORS.dim}Not authenticated. Run 'bootspring auth login' first.${utils.COLORS.reset}`);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const spinner = utils.createSpinner('Syncing to dashboard...').start();
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
// Extract raw metric values only - no weights or calculations
|
|
199
|
+
// Server will apply the scoring algorithms
|
|
200
|
+
const rawMetrics = {
|
|
201
|
+
quality: {
|
|
202
|
+
test_coverage: metrics.metrics?.test_coverage?.score,
|
|
203
|
+
lint_score: metrics.metrics?.lint_score?.score,
|
|
204
|
+
type_coverage: metrics.metrics?.type_coverage?.score
|
|
205
|
+
},
|
|
206
|
+
security: {
|
|
207
|
+
dependency_audit: metrics.metrics?.dependency_audit?.score,
|
|
208
|
+
secrets_scan: metrics.metrics?.secrets_scan?.score,
|
|
209
|
+
gitignore_coverage: metrics.metrics?.gitignore_coverage?.score,
|
|
210
|
+
env_security: metrics.metrics?.env_security?.score
|
|
211
|
+
},
|
|
212
|
+
documentation: {
|
|
213
|
+
readme_quality: metrics.metrics?.readme_quality?.score,
|
|
214
|
+
api_docs: metrics.metrics?.api_docs?.score,
|
|
215
|
+
inline_comments: metrics.metrics?.inline_comments?.score
|
|
216
|
+
},
|
|
217
|
+
performance: {
|
|
218
|
+
bundle_size: metrics.metrics?.bundle_size?.score,
|
|
219
|
+
load_time: metrics.metrics?.load_time?.score
|
|
220
|
+
},
|
|
221
|
+
progress: {
|
|
222
|
+
todo_completion: metrics.metrics?.todo_completion?.score,
|
|
223
|
+
checkpoint_progress: metrics.metrics?.checkpoint_progress?.score,
|
|
224
|
+
commit_frequency: metrics.metrics?.commit_frequency?.score
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// Send raw data to server for evaluation
|
|
229
|
+
const response = await fetch(`${apiBase}/api/projects/${projectId}/metrics/evaluate`, {
|
|
230
|
+
method: 'POST',
|
|
231
|
+
headers: {
|
|
232
|
+
'Content-Type': 'application/json',
|
|
233
|
+
'x-api-key': apiKey
|
|
234
|
+
},
|
|
235
|
+
body: JSON.stringify({ rawMetrics }),
|
|
236
|
+
redirect: 'follow'
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
if (response.ok) {
|
|
240
|
+
const result = await response.json();
|
|
241
|
+
spinner.succeed('Metrics synced to dashboard');
|
|
242
|
+
|
|
243
|
+
// Display server-calculated results
|
|
244
|
+
console.log(`
|
|
245
|
+
${utils.COLORS.bold}Server Evaluation:${utils.COLORS.reset}
|
|
246
|
+
Overall Score: ${result.overallScore}% (${result.overallGrade?.grade || 'N/A'})
|
|
247
|
+
Last Sync: ${result.lastSyncAt ? new Date(result.lastSyncAt).toLocaleString() : 'Now'}
|
|
248
|
+
`);
|
|
249
|
+
|
|
250
|
+
// Show category breakdown
|
|
251
|
+
if (result.categories) {
|
|
252
|
+
console.log(`${utils.COLORS.bold}Category Scores:${utils.COLORS.reset}`);
|
|
253
|
+
for (const [catId, catData] of Object.entries(result.categories)) {
|
|
254
|
+
const cat = catData;
|
|
255
|
+
const label = catId.charAt(0).toUpperCase() + catId.slice(1);
|
|
256
|
+
console.log(` ${label}: ${cat.score}% (${cat.grade?.grade || 'N/A'})`);
|
|
257
|
+
}
|
|
258
|
+
console.log();
|
|
259
|
+
}
|
|
260
|
+
} else {
|
|
261
|
+
const error = await response.json().catch(() => ({}));
|
|
262
|
+
spinner.fail(`Sync failed: ${error.error || response.status}`);
|
|
263
|
+
}
|
|
264
|
+
} catch (error) {
|
|
265
|
+
spinner.fail(`Sync error: ${error.message}`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// ============================================================================
|
|
270
|
+
// Commands
|
|
271
|
+
// ============================================================================
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Collect all metrics
|
|
275
|
+
*/
|
|
276
|
+
async function collectMetrics(options = {}) {
|
|
277
|
+
const cfg = config.load();
|
|
278
|
+
const projectRoot = cfg._projectRoot;
|
|
279
|
+
|
|
280
|
+
const spinner = utils.createSpinner('Collecting metrics...').start();
|
|
281
|
+
|
|
282
|
+
try {
|
|
283
|
+
const results = await metricsEngine.collectAllMetrics(projectRoot, options);
|
|
284
|
+
spinner.stop();
|
|
285
|
+
|
|
286
|
+
// Store metrics
|
|
287
|
+
metricsEngine.storeMetrics(projectRoot, results);
|
|
288
|
+
|
|
289
|
+
// Display
|
|
290
|
+
displayMetrics(results, options);
|
|
291
|
+
|
|
292
|
+
// Sync if requested
|
|
293
|
+
if (options.sync) {
|
|
294
|
+
await syncToRemote(projectRoot, results);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Generate badges if requested
|
|
298
|
+
if (options.badges) {
|
|
299
|
+
console.log(`\n${utils.COLORS.bold}Badges${utils.COLORS.reset}`);
|
|
300
|
+
console.log(generateBadges(results));
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return results;
|
|
304
|
+
} catch (error) {
|
|
305
|
+
spinner.fail(`Collection failed: ${error.message}`);
|
|
306
|
+
throw error;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Show specific metric details
|
|
312
|
+
*/
|
|
313
|
+
async function showMetricDetail(metricId, projectRoot) {
|
|
314
|
+
const results = await metricsEngine.collectAllMetrics(projectRoot);
|
|
315
|
+
const metric = results.metrics[metricId];
|
|
316
|
+
|
|
317
|
+
if (!metric) {
|
|
318
|
+
console.log(`${utils.COLORS.red}Unknown metric: ${metricId}${utils.COLORS.reset}`);
|
|
319
|
+
console.log(`${utils.COLORS.dim}Available: ${Object.keys(results.metrics).join(', ')}${utils.COLORS.reset}`);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const definition = metricsEngine.METRICS[metricId];
|
|
324
|
+
const color = getScoreColor(metric.score);
|
|
325
|
+
|
|
326
|
+
console.log(`
|
|
327
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}${definition?.icon || '📊'} ${definition?.label || metricId}${utils.COLORS.reset}
|
|
328
|
+
|
|
329
|
+
${utils.COLORS.bold}Score${utils.COLORS.reset}
|
|
330
|
+
${color}${metric.score}%${utils.COLORS.reset} ${createBar(metric.score)}
|
|
331
|
+
|
|
332
|
+
${utils.COLORS.bold}Category${utils.COLORS.reset}
|
|
333
|
+
${definition?.category || 'unknown'}
|
|
334
|
+
|
|
335
|
+
${utils.COLORS.bold}Data${utils.COLORS.reset}
|
|
336
|
+
${JSON.stringify(metric.data, null, 2).split('\n').map(l => ' ' + l).join('\n')}
|
|
337
|
+
`);
|
|
338
|
+
|
|
339
|
+
if (metric.source) {
|
|
340
|
+
console.log(`${utils.COLORS.dim}Source: ${metric.source}${utils.COLORS.reset}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Export metrics as JSON
|
|
346
|
+
*/
|
|
347
|
+
async function exportMetrics(format, projectRoot) {
|
|
348
|
+
const results = await metricsEngine.collectAllMetrics(projectRoot);
|
|
349
|
+
|
|
350
|
+
if (format === 'json') {
|
|
351
|
+
console.log(JSON.stringify(results, null, 2));
|
|
352
|
+
} else if (format === 'csv') {
|
|
353
|
+
console.log('metric,score,category');
|
|
354
|
+
for (const [id, metric] of Object.entries(results.metrics)) {
|
|
355
|
+
const def = metricsEngine.METRICS[id];
|
|
356
|
+
console.log(`${id},${metric.score},${def?.category || ''}`);
|
|
357
|
+
}
|
|
358
|
+
} else if (format === 'markdown') {
|
|
359
|
+
console.log('# Project Metrics\n');
|
|
360
|
+
console.log(`**Overall Score:** ${results.overallScore}%\n`);
|
|
361
|
+
console.log('| Metric | Score | Category |');
|
|
362
|
+
console.log('|--------|-------|----------|');
|
|
363
|
+
for (const [id, metric] of Object.entries(results.metrics)) {
|
|
364
|
+
const def = metricsEngine.METRICS[id];
|
|
365
|
+
console.log(`| ${def?.label || id} | ${metric.score}% | ${def?.category || ''} |`);
|
|
366
|
+
}
|
|
367
|
+
console.log(`\n${generateBadges(results)}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Show help
|
|
373
|
+
*/
|
|
374
|
+
function showHelp() {
|
|
375
|
+
console.log(`
|
|
376
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}📊 Bootspring Metrics${utils.COLORS.reset}
|
|
377
|
+
${utils.COLORS.dim}Collect and track project metrics${utils.COLORS.reset}
|
|
378
|
+
|
|
379
|
+
${utils.COLORS.bold}Usage:${utils.COLORS.reset}
|
|
380
|
+
bootspring metrics <command> [options]
|
|
381
|
+
|
|
382
|
+
${utils.COLORS.bold}Commands:${utils.COLORS.reset}
|
|
383
|
+
${utils.COLORS.cyan}collect${utils.COLORS.reset} Collect all metrics and store results
|
|
384
|
+
${utils.COLORS.cyan}status${utils.COLORS.reset} Show current metrics dashboard
|
|
385
|
+
${utils.COLORS.cyan}show <metric>${utils.COLORS.reset} Show details for specific metric
|
|
386
|
+
${utils.COLORS.cyan}export${utils.COLORS.reset} Export metrics (json, csv, markdown)
|
|
387
|
+
${utils.COLORS.cyan}badges${utils.COLORS.reset} Generate shields.io badge markdown
|
|
388
|
+
${utils.COLORS.cyan}sync${utils.COLORS.reset} Sync metrics to remote dashboard
|
|
389
|
+
|
|
390
|
+
${utils.COLORS.bold}Options:${utils.COLORS.reset}
|
|
391
|
+
--compact Show compact output
|
|
392
|
+
--sync Sync to dashboard after collecting
|
|
393
|
+
--badges Generate badges with output
|
|
394
|
+
--format <f> Export format (json, csv, markdown)
|
|
395
|
+
|
|
396
|
+
${utils.COLORS.bold}Available Metrics:${utils.COLORS.reset}
|
|
397
|
+
${Object.entries(metricsEngine.METRICS).map(([id, m]) =>
|
|
398
|
+
` ${m.icon} ${id.padEnd(18)} ${utils.COLORS.dim}${m.label}${utils.COLORS.reset}`
|
|
399
|
+
).join('\n')}
|
|
400
|
+
|
|
401
|
+
${utils.COLORS.bold}Examples:${utils.COLORS.reset}
|
|
402
|
+
bootspring metrics collect
|
|
403
|
+
bootspring metrics collect --sync
|
|
404
|
+
bootspring metrics status
|
|
405
|
+
bootspring metrics show testCoverage
|
|
406
|
+
bootspring metrics export --format markdown
|
|
407
|
+
bootspring metrics badges
|
|
408
|
+
`);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Main run function
|
|
413
|
+
*/
|
|
414
|
+
async function run(args) {
|
|
415
|
+
const parsedArgs = utils.parseArgs(args);
|
|
416
|
+
const subcommand = parsedArgs._[0];
|
|
417
|
+
const cfg = config.load();
|
|
418
|
+
const projectRoot = cfg._projectRoot;
|
|
419
|
+
|
|
420
|
+
switch (subcommand) {
|
|
421
|
+
case 'collect':
|
|
422
|
+
case 'run':
|
|
423
|
+
await collectMetrics({
|
|
424
|
+
sync: parsedArgs.sync,
|
|
425
|
+
badges: parsedArgs.badges,
|
|
426
|
+
compact: parsedArgs.compact
|
|
427
|
+
});
|
|
428
|
+
break;
|
|
429
|
+
|
|
430
|
+
case 'status':
|
|
431
|
+
case 'dashboard':
|
|
432
|
+
displayDashboard(projectRoot);
|
|
433
|
+
break;
|
|
434
|
+
|
|
435
|
+
case 'show':
|
|
436
|
+
case 'detail': {
|
|
437
|
+
const metricId = parsedArgs._[1];
|
|
438
|
+
if (!metricId) {
|
|
439
|
+
console.log(`${utils.COLORS.red}Please specify a metric ID${utils.COLORS.reset}`);
|
|
440
|
+
console.log(`${utils.COLORS.dim}Example: bootspring metrics show testCoverage${utils.COLORS.reset}`);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
await showMetricDetail(metricId, projectRoot);
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
case 'export':
|
|
448
|
+
await exportMetrics(parsedArgs.format || 'json', projectRoot);
|
|
449
|
+
break;
|
|
450
|
+
|
|
451
|
+
case 'badges': {
|
|
452
|
+
const results = await metricsEngine.collectAllMetrics(projectRoot);
|
|
453
|
+
console.log(generateBadges(results));
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
case 'sync': {
|
|
458
|
+
const metrics = await metricsEngine.collectAllMetrics(projectRoot);
|
|
459
|
+
await syncToRemote(projectRoot, metrics);
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
case 'help':
|
|
464
|
+
case '-h':
|
|
465
|
+
case '--help':
|
|
466
|
+
showHelp();
|
|
467
|
+
break;
|
|
468
|
+
|
|
469
|
+
default:
|
|
470
|
+
if (!subcommand) {
|
|
471
|
+
// Default to status
|
|
472
|
+
displayDashboard(projectRoot);
|
|
473
|
+
} else {
|
|
474
|
+
utils.print.error(`Unknown command: ${subcommand}`);
|
|
475
|
+
showHelp();
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
module.exports = { run, collectMetrics, displayMetrics, generateBadges };
|