@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/health.js
ADDED
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootspring Health Command
|
|
3
|
+
* Quick project health overview and status check
|
|
4
|
+
*
|
|
5
|
+
* @package bootspring
|
|
6
|
+
* @command health
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const { execSync } = require('child_process');
|
|
12
|
+
const config = require('../core/config');
|
|
13
|
+
const utils = require('../core/utils');
|
|
14
|
+
const projectState = require('../core/project-state');
|
|
15
|
+
const checkpointEngine = require('../core/checkpoint-engine');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get workflow status
|
|
19
|
+
*/
|
|
20
|
+
function getWorkflowStatus(projectRoot) {
|
|
21
|
+
const workflows = ['analyze', 'audit', 'onboard', 'deploy'];
|
|
22
|
+
const status = {};
|
|
23
|
+
|
|
24
|
+
for (const workflow of workflows) {
|
|
25
|
+
const stateFile = path.join(projectRoot, '.bootspring', workflow, 'workflow-state.json');
|
|
26
|
+
if (fs.existsSync(stateFile)) {
|
|
27
|
+
try {
|
|
28
|
+
const state = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
|
|
29
|
+
const completedPhases = Object.values(state.phases || {})
|
|
30
|
+
.filter(p => p.status === 'completed').length;
|
|
31
|
+
const totalPhases = Object.keys(state.phases || {}).length;
|
|
32
|
+
|
|
33
|
+
status[workflow] = {
|
|
34
|
+
exists: true,
|
|
35
|
+
progress: totalPhases > 0 ? Math.round((completedPhases / totalPhases) * 100) : 0,
|
|
36
|
+
lastUpdated: state.lastUpdated,
|
|
37
|
+
isComplete: completedPhases === totalPhases && totalPhases > 0
|
|
38
|
+
};
|
|
39
|
+
} catch {
|
|
40
|
+
status[workflow] = { exists: true, error: true };
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
status[workflow] = { exists: false };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return status;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get todo summary
|
|
52
|
+
*/
|
|
53
|
+
function getTodoSummary(projectRoot) {
|
|
54
|
+
const todoPath = path.join(projectRoot, 'todo.md');
|
|
55
|
+
if (!fs.existsSync(todoPath)) {
|
|
56
|
+
return { exists: false };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const content = fs.readFileSync(todoPath, 'utf-8');
|
|
61
|
+
const pending = (content.match(/- \[ \]/g) || []).length;
|
|
62
|
+
const completed = (content.match(/- \[x\]/gi) || []).length;
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
exists: true,
|
|
66
|
+
pending,
|
|
67
|
+
completed,
|
|
68
|
+
total: pending + completed
|
|
69
|
+
};
|
|
70
|
+
} catch {
|
|
71
|
+
return { exists: true, error: true };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get dependency health
|
|
77
|
+
*/
|
|
78
|
+
function getDependencyHealth(projectRoot) {
|
|
79
|
+
const pkgPath = path.join(projectRoot, 'package.json');
|
|
80
|
+
if (!fs.existsSync(pkgPath)) {
|
|
81
|
+
return { exists: false };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
86
|
+
const deps = Object.keys(pkg.dependencies || {}).length;
|
|
87
|
+
const devDeps = Object.keys(pkg.devDependencies || {}).length;
|
|
88
|
+
|
|
89
|
+
// Try to get outdated count
|
|
90
|
+
const outdated = 0;
|
|
91
|
+
let vulnerabilities = { total: 0, critical: 0, high: 0 };
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const auditResult = execSync('npm audit --json 2>/dev/null', {
|
|
95
|
+
cwd: projectRoot,
|
|
96
|
+
encoding: 'utf-8',
|
|
97
|
+
timeout: 10000,
|
|
98
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
99
|
+
});
|
|
100
|
+
const audit = JSON.parse(auditResult);
|
|
101
|
+
if (audit.metadata && audit.metadata.vulnerabilities) {
|
|
102
|
+
vulnerabilities = audit.metadata.vulnerabilities;
|
|
103
|
+
}
|
|
104
|
+
} catch {
|
|
105
|
+
// Audit failed or timed out
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
exists: true,
|
|
110
|
+
dependencies: deps,
|
|
111
|
+
devDependencies: devDeps,
|
|
112
|
+
total: deps + devDeps,
|
|
113
|
+
vulnerabilities
|
|
114
|
+
};
|
|
115
|
+
} catch {
|
|
116
|
+
return { exists: true, error: true };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Get quality metrics
|
|
122
|
+
*/
|
|
123
|
+
function getQualityMetrics(projectRoot) {
|
|
124
|
+
const metrics = {
|
|
125
|
+
hasTypeScript: false,
|
|
126
|
+
hasEslint: false,
|
|
127
|
+
hasPrettier: false,
|
|
128
|
+
hasTests: false,
|
|
129
|
+
testCoverage: null
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// Check for TypeScript
|
|
133
|
+
if (fs.existsSync(path.join(projectRoot, 'tsconfig.json'))) {
|
|
134
|
+
metrics.hasTypeScript = true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Check for ESLint
|
|
138
|
+
const eslintConfigs = ['.eslintrc', '.eslintrc.json', '.eslintrc.js', 'eslint.config.js'];
|
|
139
|
+
metrics.hasEslint = eslintConfigs.some(f => fs.existsSync(path.join(projectRoot, f)));
|
|
140
|
+
|
|
141
|
+
// Check for Prettier
|
|
142
|
+
const prettierConfigs = ['.prettierrc', '.prettierrc.json', 'prettier.config.js'];
|
|
143
|
+
metrics.hasPrettier = prettierConfigs.some(f => fs.existsSync(path.join(projectRoot, f)));
|
|
144
|
+
|
|
145
|
+
// Check for tests
|
|
146
|
+
const testDirs = ['__tests__', 'tests', 'test', 'spec'];
|
|
147
|
+
metrics.hasTests = testDirs.some(d => fs.existsSync(path.join(projectRoot, d)));
|
|
148
|
+
|
|
149
|
+
// Check for coverage
|
|
150
|
+
const coveragePath = path.join(projectRoot, 'coverage', 'coverage-summary.json');
|
|
151
|
+
if (fs.existsSync(coveragePath)) {
|
|
152
|
+
try {
|
|
153
|
+
const coverage = JSON.parse(fs.readFileSync(coveragePath, 'utf-8'));
|
|
154
|
+
metrics.testCoverage = coverage.total?.lines?.pct || null;
|
|
155
|
+
} catch {
|
|
156
|
+
// Coverage file invalid
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return metrics;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Get monitor metrics
|
|
165
|
+
*/
|
|
166
|
+
function getMonitorMetrics(projectRoot) {
|
|
167
|
+
const metricsDir = path.join(projectRoot, '.bootspring', 'monitor', 'metrics');
|
|
168
|
+
if (!fs.existsSync(metricsDir)) {
|
|
169
|
+
return { exists: false };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
const files = fs.readdirSync(metricsDir).sort().slice(-1);
|
|
174
|
+
if (files.length === 0) {
|
|
175
|
+
return { exists: true, noData: true };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const latest = JSON.parse(fs.readFileSync(path.join(metricsDir, files[0]), 'utf-8'));
|
|
179
|
+
return {
|
|
180
|
+
exists: true,
|
|
181
|
+
bundleSize: latest.bundleSize,
|
|
182
|
+
dependencies: latest.dependencies,
|
|
183
|
+
timestamp: latest.timestamp
|
|
184
|
+
};
|
|
185
|
+
} catch {
|
|
186
|
+
return { exists: true, error: true };
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Calculate overall health score
|
|
192
|
+
*/
|
|
193
|
+
function calculateHealthScore(data) {
|
|
194
|
+
let score = 0;
|
|
195
|
+
let maxScore = 0;
|
|
196
|
+
|
|
197
|
+
// Checkpoints (20 points max)
|
|
198
|
+
if (data.checkpoints && data.checkpoints.total > 0) {
|
|
199
|
+
maxScore += 20;
|
|
200
|
+
score += (data.checkpoints.percentage / 100) * 20;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Workflows (15 points max)
|
|
204
|
+
maxScore += 15;
|
|
205
|
+
const workflowsDone = Object.values(data.workflows)
|
|
206
|
+
.filter(w => w.isComplete).length;
|
|
207
|
+
score += (workflowsDone / 4) * 15;
|
|
208
|
+
|
|
209
|
+
// Todos (10 points max)
|
|
210
|
+
if (data.todos.exists && data.todos.total > 0) {
|
|
211
|
+
maxScore += 10;
|
|
212
|
+
const completionRate = data.todos.completed / data.todos.total;
|
|
213
|
+
score += completionRate * 10;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Quality tooling (25 points max)
|
|
217
|
+
maxScore += 25;
|
|
218
|
+
if (data.quality.hasTypeScript) score += 8;
|
|
219
|
+
if (data.quality.hasEslint) score += 8;
|
|
220
|
+
if (data.quality.hasPrettier) score += 4;
|
|
221
|
+
if (data.quality.hasTests) score += 5;
|
|
222
|
+
|
|
223
|
+
// Test coverage (15 points max)
|
|
224
|
+
if (data.quality.testCoverage !== null) {
|
|
225
|
+
maxScore += 15;
|
|
226
|
+
score += (data.quality.testCoverage / 100) * 15;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Dependencies (15 points max)
|
|
230
|
+
maxScore += 15;
|
|
231
|
+
if (data.deps.exists) {
|
|
232
|
+
const vulns = data.deps.vulnerabilities || {};
|
|
233
|
+
if (vulns.critical === 0 && vulns.high === 0) {
|
|
234
|
+
score += 15;
|
|
235
|
+
} else if (vulns.critical === 0) {
|
|
236
|
+
score += 8;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return Math.round((score / maxScore) * 100);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Get health grade
|
|
245
|
+
*/
|
|
246
|
+
function getHealthGrade(score) {
|
|
247
|
+
if (score >= 90) return { grade: 'A', color: utils.COLORS.green };
|
|
248
|
+
if (score >= 80) return { grade: 'B', color: utils.COLORS.green };
|
|
249
|
+
if (score >= 70) return { grade: 'C', color: utils.COLORS.yellow };
|
|
250
|
+
if (score >= 60) return { grade: 'D', color: utils.COLORS.yellow };
|
|
251
|
+
return { grade: 'F', color: utils.COLORS.red };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Show health report
|
|
256
|
+
*/
|
|
257
|
+
function showHealth(projectRoot) {
|
|
258
|
+
const cfg = config.load();
|
|
259
|
+
|
|
260
|
+
console.log(`
|
|
261
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Project Health Report${utils.COLORS.reset}
|
|
262
|
+
${utils.COLORS.dim}${cfg.project?.name || 'Unknown Project'}${utils.COLORS.reset}
|
|
263
|
+
`);
|
|
264
|
+
|
|
265
|
+
// Collect all data
|
|
266
|
+
const data = {
|
|
267
|
+
workflows: getWorkflowStatus(projectRoot),
|
|
268
|
+
todos: getTodoSummary(projectRoot),
|
|
269
|
+
quality: getQualityMetrics(projectRoot),
|
|
270
|
+
deps: getDependencyHealth(projectRoot),
|
|
271
|
+
monitor: getMonitorMetrics(projectRoot),
|
|
272
|
+
checkpoints: projectState.getCheckpointProgress(projectRoot)
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
// Calculate score
|
|
276
|
+
const score = calculateHealthScore(data);
|
|
277
|
+
const { grade, color } = getHealthGrade(score);
|
|
278
|
+
|
|
279
|
+
// Health Score
|
|
280
|
+
console.log(`${utils.COLORS.bold}Overall Health${utils.COLORS.reset}`);
|
|
281
|
+
console.log(` ${color}${utils.COLORS.bold}${grade}${utils.COLORS.reset} - ${score}/100`);
|
|
282
|
+
console.log();
|
|
283
|
+
|
|
284
|
+
// Checkpoints
|
|
285
|
+
if (data.checkpoints && data.checkpoints.total > 0) {
|
|
286
|
+
console.log(`${utils.COLORS.bold}Checkpoints${utils.COLORS.reset}`);
|
|
287
|
+
const cpBar = checkpointEngine.getColoredProgressBar(data.checkpoints.percentage, 20);
|
|
288
|
+
console.log(` ${cpBar} ${data.checkpoints.completed}/${data.checkpoints.total} (${data.checkpoints.percentage}%)`);
|
|
289
|
+
console.log();
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Workflows
|
|
293
|
+
console.log(`${utils.COLORS.bold}Workflows${utils.COLORS.reset}`);
|
|
294
|
+
for (const [name, status] of Object.entries(data.workflows)) {
|
|
295
|
+
if (status.error) {
|
|
296
|
+
console.log(` ${utils.COLORS.yellow}⚠${utils.COLORS.reset} ${name}: Error reading state`);
|
|
297
|
+
} else if (!status.exists) {
|
|
298
|
+
console.log(` ${utils.COLORS.dim}○${utils.COLORS.reset} ${name}: Not started`);
|
|
299
|
+
} else if (status.isComplete) {
|
|
300
|
+
console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} ${name}: Complete`);
|
|
301
|
+
} else {
|
|
302
|
+
console.log(` ${utils.COLORS.yellow}◐${utils.COLORS.reset} ${name}: ${status.progress}% complete`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
console.log();
|
|
306
|
+
|
|
307
|
+
// Todos
|
|
308
|
+
console.log(`${utils.COLORS.bold}Tasks${utils.COLORS.reset}`);
|
|
309
|
+
if (!data.todos.exists) {
|
|
310
|
+
console.log(` ${utils.COLORS.dim}No todo.md found${utils.COLORS.reset}`);
|
|
311
|
+
} else {
|
|
312
|
+
const pendingIcon = data.todos.pending === 0 ?
|
|
313
|
+
`${utils.COLORS.green}✓${utils.COLORS.reset}` :
|
|
314
|
+
`${utils.COLORS.yellow}●${utils.COLORS.reset}`;
|
|
315
|
+
console.log(` ${pendingIcon} ${data.todos.pending} pending, ${data.todos.completed} completed`);
|
|
316
|
+
}
|
|
317
|
+
console.log();
|
|
318
|
+
|
|
319
|
+
// Quality
|
|
320
|
+
console.log(`${utils.COLORS.bold}Quality Tooling${utils.COLORS.reset}`);
|
|
321
|
+
const qIcon = (has) => has ?
|
|
322
|
+
`${utils.COLORS.green}✓${utils.COLORS.reset}` :
|
|
323
|
+
`${utils.COLORS.dim}○${utils.COLORS.reset}`;
|
|
324
|
+
console.log(` ${qIcon(data.quality.hasTypeScript)} TypeScript`);
|
|
325
|
+
console.log(` ${qIcon(data.quality.hasEslint)} ESLint`);
|
|
326
|
+
console.log(` ${qIcon(data.quality.hasPrettier)} Prettier`);
|
|
327
|
+
console.log(` ${qIcon(data.quality.hasTests)} Tests`);
|
|
328
|
+
if (data.quality.testCoverage !== null) {
|
|
329
|
+
const covColor = data.quality.testCoverage >= 80 ? utils.COLORS.green :
|
|
330
|
+
data.quality.testCoverage >= 60 ? utils.COLORS.yellow : utils.COLORS.red;
|
|
331
|
+
console.log(` ${covColor}●${utils.COLORS.reset} Coverage: ${data.quality.testCoverage}%`);
|
|
332
|
+
}
|
|
333
|
+
console.log();
|
|
334
|
+
|
|
335
|
+
// Dependencies
|
|
336
|
+
console.log(`${utils.COLORS.bold}Dependencies${utils.COLORS.reset}`);
|
|
337
|
+
if (!data.deps.exists) {
|
|
338
|
+
console.log(` ${utils.COLORS.dim}No package.json found${utils.COLORS.reset}`);
|
|
339
|
+
} else {
|
|
340
|
+
console.log(` ${utils.COLORS.dim}●${utils.COLORS.reset} ${data.deps.dependencies} deps, ${data.deps.devDependencies} devDeps`);
|
|
341
|
+
const vulns = data.deps.vulnerabilities || {};
|
|
342
|
+
if (vulns.critical > 0) {
|
|
343
|
+
console.log(` ${utils.COLORS.red}✗${utils.COLORS.reset} ${vulns.critical} critical vulnerabilities`);
|
|
344
|
+
} else if (vulns.high > 0) {
|
|
345
|
+
console.log(` ${utils.COLORS.yellow}⚠${utils.COLORS.reset} ${vulns.high} high vulnerabilities`);
|
|
346
|
+
} else if (vulns.total > 0) {
|
|
347
|
+
console.log(` ${utils.COLORS.yellow}○${utils.COLORS.reset} ${vulns.total} vulnerabilities (low/moderate)`);
|
|
348
|
+
} else {
|
|
349
|
+
console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} No vulnerabilities`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
console.log();
|
|
353
|
+
|
|
354
|
+
// Metrics
|
|
355
|
+
if (data.monitor.exists && !data.monitor.noData && !data.monitor.error) {
|
|
356
|
+
console.log(`${utils.COLORS.bold}Latest Metrics${utils.COLORS.reset}`);
|
|
357
|
+
if (data.monitor.bundleSize) {
|
|
358
|
+
console.log(` Bundle size: ${data.monitor.bundleSize} KB`);
|
|
359
|
+
}
|
|
360
|
+
if (data.monitor.timestamp) {
|
|
361
|
+
const date = new Date(data.monitor.timestamp).toLocaleDateString();
|
|
362
|
+
console.log(` ${utils.COLORS.dim}Collected: ${date}${utils.COLORS.reset}`);
|
|
363
|
+
}
|
|
364
|
+
console.log();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Recommendations
|
|
368
|
+
const recommendations = [];
|
|
369
|
+
|
|
370
|
+
// Checkpoint recommendations
|
|
371
|
+
if (!data.checkpoints || data.checkpoints.total === 0) {
|
|
372
|
+
recommendations.push('Run `bootspring checkpoint init` to start tracking progress');
|
|
373
|
+
} else if (data.checkpoints.percentage < 50) {
|
|
374
|
+
recommendations.push('Complete more checkpoints with `bootspring checkpoint sync`');
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (!data.quality.hasTypeScript) {
|
|
378
|
+
recommendations.push('Add TypeScript for type safety');
|
|
379
|
+
}
|
|
380
|
+
if (!data.quality.hasEslint) {
|
|
381
|
+
recommendations.push('Add ESLint for code quality');
|
|
382
|
+
}
|
|
383
|
+
if (!data.quality.hasTests) {
|
|
384
|
+
recommendations.push('Add tests for reliability');
|
|
385
|
+
}
|
|
386
|
+
if (data.todos.pending > 5) {
|
|
387
|
+
recommendations.push(`Address ${data.todos.pending} pending tasks`);
|
|
388
|
+
}
|
|
389
|
+
if (!data.workflows.analyze?.isComplete) {
|
|
390
|
+
recommendations.push('Run `bootspring analyze` for codebase insights');
|
|
391
|
+
}
|
|
392
|
+
if (!data.workflows.audit?.isComplete) {
|
|
393
|
+
recommendations.push('Run `bootspring audit` for quality assessment');
|
|
394
|
+
}
|
|
395
|
+
const vulns = data.deps.vulnerabilities || {};
|
|
396
|
+
if (vulns.critical > 0 || vulns.high > 0) {
|
|
397
|
+
recommendations.push('Run `npm audit fix` to address vulnerabilities');
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (recommendations.length > 0) {
|
|
401
|
+
console.log(`${utils.COLORS.bold}Recommendations${utils.COLORS.reset}`);
|
|
402
|
+
recommendations.slice(0, 5).forEach(rec => {
|
|
403
|
+
console.log(` ${utils.COLORS.cyan}→${utils.COLORS.reset} ${rec}`);
|
|
404
|
+
});
|
|
405
|
+
console.log();
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return { score, grade, data };
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Show help
|
|
413
|
+
*/
|
|
414
|
+
function showHelp() {
|
|
415
|
+
console.log(`
|
|
416
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Health${utils.COLORS.reset}
|
|
417
|
+
${utils.COLORS.dim}Quick project health overview${utils.COLORS.reset}
|
|
418
|
+
|
|
419
|
+
${utils.COLORS.bold}Usage:${utils.COLORS.reset}
|
|
420
|
+
bootspring health [options]
|
|
421
|
+
|
|
422
|
+
${utils.COLORS.bold}Options:${utils.COLORS.reset}
|
|
423
|
+
--json Output as JSON
|
|
424
|
+
|
|
425
|
+
${utils.COLORS.bold}What it checks:${utils.COLORS.reset}
|
|
426
|
+
- Workflow completion status
|
|
427
|
+
- Todo/task progress
|
|
428
|
+
- Quality tooling (TypeScript, ESLint, Prettier)
|
|
429
|
+
- Test coverage
|
|
430
|
+
- Dependency vulnerabilities
|
|
431
|
+
- Recent metrics
|
|
432
|
+
|
|
433
|
+
${utils.COLORS.bold}Examples:${utils.COLORS.reset}
|
|
434
|
+
bootspring health
|
|
435
|
+
bootspring health --json
|
|
436
|
+
`);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Run health command
|
|
441
|
+
*/
|
|
442
|
+
async function run(args) {
|
|
443
|
+
const parsedArgs = utils.parseArgs(args);
|
|
444
|
+
const subcommand = parsedArgs._[0];
|
|
445
|
+
|
|
446
|
+
if (subcommand === 'help' || subcommand === '-h' || subcommand === '--help') {
|
|
447
|
+
showHelp();
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const cfg = config.load();
|
|
452
|
+
const projectRoot = cfg._projectRoot;
|
|
453
|
+
|
|
454
|
+
if (parsedArgs.json) {
|
|
455
|
+
const data = {
|
|
456
|
+
workflows: getWorkflowStatus(projectRoot),
|
|
457
|
+
todos: getTodoSummary(projectRoot),
|
|
458
|
+
quality: getQualityMetrics(projectRoot),
|
|
459
|
+
deps: getDependencyHealth(projectRoot),
|
|
460
|
+
monitor: getMonitorMetrics(projectRoot)
|
|
461
|
+
};
|
|
462
|
+
data.score = calculateHealthScore(data);
|
|
463
|
+
data.grade = getHealthGrade(data.score).grade;
|
|
464
|
+
console.log(JSON.stringify(data, null, 2));
|
|
465
|
+
} else {
|
|
466
|
+
showHealth(projectRoot);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
module.exports = {
|
|
471
|
+
run,
|
|
472
|
+
getWorkflowStatus,
|
|
473
|
+
getTodoSummary,
|
|
474
|
+
getDependencyHealth,
|
|
475
|
+
getQualityMetrics,
|
|
476
|
+
calculateHealthScore
|
|
477
|
+
};
|
package/cli/init.js
CHANGED
|
@@ -19,6 +19,8 @@ const { runQuestionnaire } = require('../generators/questionnaire');
|
|
|
19
19
|
const claudeTemplate = require('../generators/templates/claude.template');
|
|
20
20
|
const seedTemplate = require('../generators/templates/seed.template');
|
|
21
21
|
const planningTemplate = require('../generators/templates/planning.template');
|
|
22
|
+
const api = require('../core/api-client');
|
|
23
|
+
const { requireProjectSelection } = require('./auth');
|
|
22
24
|
// Presets available via runQuestionnaire('preset-name')
|
|
23
25
|
|
|
24
26
|
// Available options (for legacy quick mode)
|
|
@@ -314,9 +316,57 @@ async function run(args) {
|
|
|
314
316
|
const parsedArgs = utils.parseArgs(args);
|
|
315
317
|
const force = parsedArgs.force || parsedArgs.f;
|
|
316
318
|
const quick = parsedArgs.quick || parsedArgs.q;
|
|
317
|
-
const
|
|
319
|
+
const listPresetsFlag = parsedArgs['list-presets'] || parsedArgs.presets;
|
|
320
|
+
const presetArg = parsedArgs.preset || (quick ? 'minimal' : null);
|
|
318
321
|
const projectRoot = config.findProjectRoot();
|
|
319
322
|
|
|
323
|
+
// Handle --list-presets flag (no auth required for help)
|
|
324
|
+
if (listPresetsFlag) {
|
|
325
|
+
console.log(`
|
|
326
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}Available Config Presets${utils.COLORS.reset}
|
|
327
|
+
|
|
328
|
+
${config.listPresets({ verbose: true }).map(p =>
|
|
329
|
+
` ${utils.COLORS.bold}${p.key}${utils.COLORS.reset}
|
|
330
|
+
${p.description}
|
|
331
|
+
${utils.COLORS.dim}Tags: ${p.tags.join(', ')}${p.extends ? ` | Extends: ${p.extends}` : ''}${utils.COLORS.reset}
|
|
332
|
+
`).join('\n')}
|
|
333
|
+
${utils.COLORS.dim}Usage: bootspring init --preset <preset-name>
|
|
334
|
+
bootspring init --preset saas-starter,ai-app (combine presets)${utils.COLORS.reset}
|
|
335
|
+
`);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Require authentication before proceeding
|
|
340
|
+
try {
|
|
341
|
+
api.requireAuth();
|
|
342
|
+
} catch (error) {
|
|
343
|
+
utils.print.error(error.message);
|
|
344
|
+
utils.print.dim('Run: bootspring auth login');
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Ensure project is selected
|
|
349
|
+
await requireProjectSelection();
|
|
350
|
+
|
|
351
|
+
// Parse and validate preset(s)
|
|
352
|
+
let configPresets = null;
|
|
353
|
+
if (presetArg && !['minimal', 'standard', 'full'].includes(presetArg)) {
|
|
354
|
+
// This is a config preset (saas-starter, api-only, etc.), not a questionnaire preset
|
|
355
|
+
configPresets = config.parsePresetString(presetArg);
|
|
356
|
+
const validation = config.validatePresets(configPresets);
|
|
357
|
+
if (!validation.valid) {
|
|
358
|
+
utils.print.error('Invalid preset(s):');
|
|
359
|
+
validation.errors.forEach(err => utils.print.dim(` - ${err}`));
|
|
360
|
+
console.log(`\n${utils.COLORS.dim}Run 'bootspring init --list-presets' to see available presets${utils.COLORS.reset}`);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Determine questionnaire preset (for interactive mode)
|
|
366
|
+
const questionnairePreset = !configPresets && presetArg && ['minimal', 'standard', 'full'].includes(presetArg)
|
|
367
|
+
? presetArg
|
|
368
|
+
: null;
|
|
369
|
+
|
|
320
370
|
console.log(`
|
|
321
371
|
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Init${utils.COLORS.reset}
|
|
322
372
|
${utils.COLORS.dim}Development scaffolding with intelligence${utils.COLORS.reset}
|
|
@@ -337,21 +387,41 @@ ${utils.COLORS.dim}Development scaffolding with intelligence${utils.COLORS.reset
|
|
|
337
387
|
|
|
338
388
|
let projectConfig;
|
|
339
389
|
|
|
340
|
-
//
|
|
341
|
-
if (
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
390
|
+
// Config preset mode (saas-starter, api-only, etc.)
|
|
391
|
+
if (configPresets && configPresets.length > 0) {
|
|
392
|
+
const presetDisplay = configPresets.join(' + ');
|
|
393
|
+
utils.print.info(`Using config preset(s): ${presetDisplay}`);
|
|
394
|
+
|
|
395
|
+
try {
|
|
396
|
+
// Apply presets (combining if multiple)
|
|
397
|
+
projectConfig = config.applyPreset(configPresets, {
|
|
398
|
+
project: {
|
|
399
|
+
name: detected.name,
|
|
400
|
+
description: '',
|
|
401
|
+
version: '1.0.0'
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
projectConfig._configPreset = configPresets.join(',');
|
|
405
|
+
projectConfig._preset = 'config'; // Mark as config preset for template generation
|
|
406
|
+
} catch (error) {
|
|
407
|
+
utils.print.error(`Failed to apply preset: ${error.message}`);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
// Questionnaire preset mode (minimal, standard, full)
|
|
412
|
+
else if (questionnairePreset) {
|
|
413
|
+
utils.print.info(`Using ${questionnairePreset} preset questionnaire`);
|
|
345
414
|
|
|
346
415
|
try {
|
|
347
|
-
projectConfig = await runQuestionnaire(
|
|
348
|
-
projectConfig._preset =
|
|
416
|
+
projectConfig = await runQuestionnaire(questionnairePreset);
|
|
417
|
+
projectConfig._preset = questionnairePreset;
|
|
349
418
|
} catch (error) {
|
|
350
419
|
utils.print.error(`Questionnaire failed: ${error.message}`);
|
|
351
420
|
return;
|
|
352
421
|
}
|
|
353
|
-
}
|
|
354
|
-
|
|
422
|
+
}
|
|
423
|
+
// Quick mode - use detected values (legacy behavior)
|
|
424
|
+
else if (quick) {
|
|
355
425
|
utils.print.info('Using detected settings (quick mode)');
|
|
356
426
|
projectConfig = {
|
|
357
427
|
project: {
|
|
@@ -377,8 +447,9 @@ ${utils.COLORS.dim}Development scaffolding with intelligence${utils.COLORS.reset
|
|
|
377
447
|
quality: { preCommit: true, prePush: false, strictMode: false },
|
|
378
448
|
paths: { context: 'CLAUDE.md', config: 'bootspring.config.js', todo: 'todo.md' }
|
|
379
449
|
};
|
|
380
|
-
}
|
|
381
|
-
|
|
450
|
+
}
|
|
451
|
+
// Default: Interactive questionnaire mode
|
|
452
|
+
else {
|
|
382
453
|
try {
|
|
383
454
|
projectConfig = await runQuestionnaire('standard');
|
|
384
455
|
projectConfig._preset = 'standard';
|
|
@@ -483,7 +554,7 @@ ${utils.COLORS.dim}Development scaffolding with intelligence${utils.COLORS.reset
|
|
|
483
554
|
// Global install detected but no global MCP config - suggest global
|
|
484
555
|
utils.print.info('Global bootspring installation detected');
|
|
485
556
|
|
|
486
|
-
if (!quick && !
|
|
557
|
+
if (!quick && !presetArg) {
|
|
487
558
|
const rl = createPrompt();
|
|
488
559
|
const useGlobal = await askYesNo(rl, 'Configure MCP server globally (for all projects)?', true);
|
|
489
560
|
rl.close();
|