@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.
Files changed (253) hide show
  1. package/README.md +107 -14
  2. package/bin/bootspring.js +166 -27
  3. package/cli/agent.js +189 -17
  4. package/cli/analyze.js +499 -0
  5. package/cli/audit.js +557 -0
  6. package/cli/auth.js +495 -38
  7. package/cli/billing.js +302 -0
  8. package/cli/build.js +695 -0
  9. package/cli/business.js +109 -26
  10. package/cli/checkpoint-utils.js +168 -0
  11. package/cli/checkpoint.js +639 -0
  12. package/cli/cloud-sync.js +447 -0
  13. package/cli/content.js +198 -0
  14. package/cli/context.js +1 -1
  15. package/cli/deploy.js +543 -0
  16. package/cli/fundraise.js +112 -50
  17. package/cli/github-cmd.js +435 -0
  18. package/cli/health.js +477 -0
  19. package/cli/init.js +84 -13
  20. package/cli/legal.js +107 -95
  21. package/cli/log.js +2 -2
  22. package/cli/loop.js +976 -73
  23. package/cli/manager.js +711 -0
  24. package/cli/metrics.js +480 -0
  25. package/cli/monitor.js +812 -0
  26. package/cli/onboard.js +521 -0
  27. package/cli/orchestrator.js +12 -24
  28. package/cli/prd.js +594 -0
  29. package/cli/preseed-start.js +1483 -0
  30. package/cli/preseed.js +2302 -0
  31. package/cli/project.js +436 -0
  32. package/cli/quality.js +233 -0
  33. package/cli/security.js +913 -0
  34. package/cli/seed.js +1441 -5
  35. package/cli/skill.js +273 -211
  36. package/cli/suggest.js +989 -0
  37. package/cli/switch.js +453 -0
  38. package/cli/visualize.js +527 -0
  39. package/cli/watch.js +769 -0
  40. package/cli/workspace.js +607 -0
  41. package/core/analyze-workflow.js +1134 -0
  42. package/core/api-client.js +535 -22
  43. package/core/audit-workflow.js +1350 -0
  44. package/core/build-orchestrator.js +480 -0
  45. package/core/build-state.js +577 -0
  46. package/core/checkpoint-engine.js +408 -0
  47. package/core/config.js +1109 -26
  48. package/core/context-loader.js +21 -1
  49. package/core/deploy-workflow.js +836 -0
  50. package/core/entitlements.js +93 -22
  51. package/core/github-sync.js +610 -0
  52. package/core/index.js +8 -1
  53. package/core/ingest.js +1111 -0
  54. package/core/metrics-engine.js +768 -0
  55. package/core/onboard-workflow.js +1007 -0
  56. package/core/preseed-workflow.js +934 -0
  57. package/core/preseed.js +1617 -0
  58. package/core/project-context.js +325 -0
  59. package/core/project-state.js +694 -0
  60. package/core/r2-sync.js +583 -0
  61. package/core/scaffold.js +525 -7
  62. package/core/session.js +258 -0
  63. package/core/task-extractor.js +758 -0
  64. package/core/telemetry.js +28 -6
  65. package/core/tier-enforcement.js +737 -0
  66. package/core/utils.js +38 -14
  67. package/generators/questionnaire.js +15 -12
  68. package/generators/sections/ai.js +7 -7
  69. package/generators/sections/content.js +300 -0
  70. package/generators/sections/index.js +3 -0
  71. package/generators/sections/plugins.js +7 -6
  72. package/generators/templates/build-planning.template.js +596 -0
  73. package/generators/templates/content.template.js +819 -0
  74. package/generators/templates/index.js +2 -1
  75. package/hooks/git-autopilot.js +1250 -0
  76. package/hooks/index.js +9 -0
  77. package/intelligence/agent-collab.js +2057 -0
  78. package/intelligence/auto-suggest.js +634 -0
  79. package/intelligence/content-gen.js +1589 -0
  80. package/intelligence/cross-project.js +1647 -0
  81. package/intelligence/index.js +184 -0
  82. package/intelligence/learning/insights.json +517 -7
  83. package/intelligence/learning/pattern-learner.js +1008 -14
  84. package/intelligence/memory/decision-tracker.js +1431 -31
  85. package/intelligence/memory/decisions.jsonl +0 -0
  86. package/intelligence/orchestrator.js +2896 -1
  87. package/intelligence/prd.js +92 -1
  88. package/intelligence/recommendation-weights.json +14 -2
  89. package/intelligence/recommendations.js +463 -9
  90. package/intelligence/workflow-composer.js +1451 -0
  91. package/marketplace/index.d.ts +324 -0
  92. package/marketplace/index.js +1921 -0
  93. package/mcp/contracts/mcp-contract.v1.json +342 -4
  94. package/mcp/registry.js +680 -3
  95. package/mcp/response-formatter.js +23 -0
  96. package/mcp/tools/assist-tool.js +78 -4
  97. package/mcp/tools/autopilot-tool.js +408 -0
  98. package/mcp/tools/content-tool.js +571 -0
  99. package/mcp/tools/dashboard-tool.js +251 -5
  100. package/mcp/tools/mvp-tool.js +344 -0
  101. package/mcp/tools/plugin-tool.js +23 -1
  102. package/mcp/tools/prd-tool.js +579 -0
  103. package/mcp/tools/seed-tool.js +447 -0
  104. package/mcp/tools/skill-tool.js +43 -14
  105. package/mcp/tools/suggest-tool.js +147 -0
  106. package/package.json +15 -6
  107. package/agents/README.md +0 -93
  108. package/agents/ai-integration-expert/context.md +0 -386
  109. package/agents/api-expert/context.md +0 -416
  110. package/agents/architecture-expert/context.md +0 -454
  111. package/agents/auth-expert/context.md +0 -399
  112. package/agents/backend-expert/context.md +0 -483
  113. package/agents/business-strategy-expert/context.md +0 -180
  114. package/agents/code-review-expert/context.md +0 -365
  115. package/agents/competitive-analysis-expert/context.md +0 -239
  116. package/agents/data-modeling-expert/context.md +0 -352
  117. package/agents/database-expert/context.md +0 -250
  118. package/agents/devops-expert/context.md +0 -446
  119. package/agents/email-expert/context.md +0 -379
  120. package/agents/financial-expert/context.md +0 -213
  121. package/agents/frontend-expert/context.md +0 -364
  122. package/agents/fundraising-expert/context.md +0 -257
  123. package/agents/growth-expert/context.md +0 -249
  124. package/agents/index.js +0 -140
  125. package/agents/investor-relations-expert/context.md +0 -266
  126. package/agents/legal-expert/context.md +0 -284
  127. package/agents/marketing-expert/context.md +0 -236
  128. package/agents/monitoring-expert/context.md +0 -362
  129. package/agents/operations-expert/context.md +0 -279
  130. package/agents/partnerships-expert/context.md +0 -286
  131. package/agents/payment-expert/context.md +0 -340
  132. package/agents/performance-expert/context.md +0 -377
  133. package/agents/private-equity-expert/context.md +0 -246
  134. package/agents/railway-expert/context.md +0 -284
  135. package/agents/research-expert/context.md +0 -245
  136. package/agents/sales-expert/context.md +0 -241
  137. package/agents/security-expert/context.md +0 -343
  138. package/agents/testing-expert/context.md +0 -414
  139. package/agents/ui-ux-expert/context.md +0 -448
  140. package/agents/vercel-expert/context.md +0 -426
  141. package/skills/index.js +0 -787
  142. package/skills/patterns/README.md +0 -163
  143. package/skills/patterns/ai/agents.md +0 -281
  144. package/skills/patterns/ai/claude.md +0 -138
  145. package/skills/patterns/ai/embeddings.md +0 -150
  146. package/skills/patterns/ai/rag.md +0 -266
  147. package/skills/patterns/ai/streaming.md +0 -170
  148. package/skills/patterns/ai/structured-output.md +0 -162
  149. package/skills/patterns/ai/tools.md +0 -154
  150. package/skills/patterns/analytics/tracking.md +0 -220
  151. package/skills/patterns/api/errors.md +0 -296
  152. package/skills/patterns/api/graphql.md +0 -440
  153. package/skills/patterns/api/middleware.md +0 -279
  154. package/skills/patterns/api/openapi.md +0 -285
  155. package/skills/patterns/api/rate-limiting.md +0 -231
  156. package/skills/patterns/api/route-handler.md +0 -217
  157. package/skills/patterns/api/server-action.md +0 -249
  158. package/skills/patterns/api/versioning.md +0 -443
  159. package/skills/patterns/api/webhooks.md +0 -247
  160. package/skills/patterns/auth/clerk.md +0 -132
  161. package/skills/patterns/auth/mfa.md +0 -313
  162. package/skills/patterns/auth/nextauth.md +0 -140
  163. package/skills/patterns/auth/oauth.md +0 -237
  164. package/skills/patterns/auth/rbac.md +0 -152
  165. package/skills/patterns/auth/session-management.md +0 -367
  166. package/skills/patterns/auth/session.md +0 -120
  167. package/skills/patterns/database/audit.md +0 -177
  168. package/skills/patterns/database/migrations.md +0 -177
  169. package/skills/patterns/database/pagination.md +0 -230
  170. package/skills/patterns/database/pooling.md +0 -357
  171. package/skills/patterns/database/prisma.md +0 -180
  172. package/skills/patterns/database/relations.md +0 -187
  173. package/skills/patterns/database/seeding.md +0 -246
  174. package/skills/patterns/database/soft-delete.md +0 -153
  175. package/skills/patterns/database/transactions.md +0 -162
  176. package/skills/patterns/deployment/ci-cd.md +0 -231
  177. package/skills/patterns/deployment/docker.md +0 -188
  178. package/skills/patterns/deployment/monitoring.md +0 -387
  179. package/skills/patterns/deployment/vercel.md +0 -160
  180. package/skills/patterns/email/resend.md +0 -143
  181. package/skills/patterns/email/templates.md +0 -245
  182. package/skills/patterns/email/transactional.md +0 -503
  183. package/skills/patterns/email/verification.md +0 -176
  184. package/skills/patterns/files/download.md +0 -243
  185. package/skills/patterns/files/upload.md +0 -239
  186. package/skills/patterns/i18n/nextintl.md +0 -188
  187. package/skills/patterns/logging/structured.md +0 -292
  188. package/skills/patterns/notifications/email-queue.md +0 -248
  189. package/skills/patterns/notifications/push.md +0 -279
  190. package/skills/patterns/payments/checkout.md +0 -303
  191. package/skills/patterns/payments/invoices.md +0 -287
  192. package/skills/patterns/payments/portal.md +0 -245
  193. package/skills/patterns/payments/stripe.md +0 -272
  194. package/skills/patterns/payments/subscriptions.md +0 -300
  195. package/skills/patterns/payments/usage.md +0 -279
  196. package/skills/patterns/performance/caching.md +0 -276
  197. package/skills/patterns/performance/code-splitting.md +0 -233
  198. package/skills/patterns/performance/edge.md +0 -254
  199. package/skills/patterns/performance/isr.md +0 -266
  200. package/skills/patterns/performance/lazy-loading.md +0 -281
  201. package/skills/patterns/realtime/sse.md +0 -327
  202. package/skills/patterns/realtime/websockets.md +0 -336
  203. package/skills/patterns/search/filtering.md +0 -329
  204. package/skills/patterns/search/fulltext.md +0 -260
  205. package/skills/patterns/security/audit-logging.md +0 -444
  206. package/skills/patterns/security/csrf.md +0 -234
  207. package/skills/patterns/security/headers.md +0 -252
  208. package/skills/patterns/security/sanitization.md +0 -258
  209. package/skills/patterns/security/secrets.md +0 -261
  210. package/skills/patterns/security/validation.md +0 -268
  211. package/skills/patterns/security/xss.md +0 -229
  212. package/skills/patterns/seo/metadata.md +0 -252
  213. package/skills/patterns/state/context.md +0 -349
  214. package/skills/patterns/state/react-query.md +0 -313
  215. package/skills/patterns/state/url-state.md +0 -482
  216. package/skills/patterns/state/zustand.md +0 -262
  217. package/skills/patterns/testing/api.md +0 -259
  218. package/skills/patterns/testing/component.md +0 -233
  219. package/skills/patterns/testing/coverage.md +0 -207
  220. package/skills/patterns/testing/fixtures.md +0 -225
  221. package/skills/patterns/testing/integration.md +0 -436
  222. package/skills/patterns/testing/mocking.md +0 -177
  223. package/skills/patterns/testing/playwright.md +0 -162
  224. package/skills/patterns/testing/snapshot.md +0 -175
  225. package/skills/patterns/testing/vitest.md +0 -307
  226. package/skills/patterns/ui/accordions.md +0 -395
  227. package/skills/patterns/ui/cards.md +0 -299
  228. package/skills/patterns/ui/dropdowns.md +0 -476
  229. package/skills/patterns/ui/empty-states.md +0 -320
  230. package/skills/patterns/ui/forms.md +0 -405
  231. package/skills/patterns/ui/inputs.md +0 -319
  232. package/skills/patterns/ui/layouts.md +0 -282
  233. package/skills/patterns/ui/loading.md +0 -291
  234. package/skills/patterns/ui/modals.md +0 -338
  235. package/skills/patterns/ui/navigation.md +0 -374
  236. package/skills/patterns/ui/tables.md +0 -407
  237. package/skills/patterns/ui/toasts.md +0 -300
  238. package/skills/patterns/ui/tooltips.md +0 -396
  239. package/skills/patterns/utils/dates.md +0 -435
  240. package/skills/patterns/utils/errors.md +0 -451
  241. package/skills/patterns/utils/formatting.md +0 -345
  242. package/skills/patterns/utils/validation.md +0 -434
  243. package/templates/bootspring.config.js +0 -83
  244. package/templates/business/business-model-canvas.md +0 -246
  245. package/templates/business/business-plan.md +0 -266
  246. package/templates/business/competitive-analysis.md +0 -312
  247. package/templates/fundraising/data-room-checklist.md +0 -300
  248. package/templates/fundraising/investor-research.md +0 -243
  249. package/templates/fundraising/pitch-deck-outline.md +0 -253
  250. package/templates/legal/gdpr-checklist.md +0 -339
  251. package/templates/legal/privacy-policy.md +0 -285
  252. package/templates/legal/terms-of-service.md +0 -222
  253. package/templates/mcp.json +0 -9
package/cli/monitor.js ADDED
@@ -0,0 +1,812 @@
1
+ /**
2
+ * Bootspring Monitor Command
3
+ * Production monitoring, hooks, and alerting
4
+ *
5
+ * @package bootspring
6
+ * @command monitor
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
+
15
+ // Supported monitoring providers
16
+ const MONITORING_PROVIDERS = {
17
+ sentry: {
18
+ name: 'Sentry',
19
+ description: 'Error tracking and performance monitoring',
20
+ envVars: ['SENTRY_DSN', 'SENTRY_AUTH_TOKEN'],
21
+ docs: 'https://docs.sentry.io/'
22
+ },
23
+ datadog: {
24
+ name: 'Datadog',
25
+ description: 'Infrastructure and application monitoring',
26
+ envVars: ['DD_API_KEY', 'DD_APP_KEY'],
27
+ docs: 'https://docs.datadoghq.com/'
28
+ },
29
+ newrelic: {
30
+ name: 'New Relic',
31
+ description: 'Full-stack observability platform',
32
+ envVars: ['NEW_RELIC_LICENSE_KEY', 'NEW_RELIC_APP_NAME'],
33
+ docs: 'https://docs.newrelic.com/'
34
+ },
35
+ logrocket: {
36
+ name: 'LogRocket',
37
+ description: 'Session replay and error tracking',
38
+ envVars: ['LOGROCKET_APP_ID'],
39
+ docs: 'https://docs.logrocket.com/'
40
+ },
41
+ axiom: {
42
+ name: 'Axiom',
43
+ description: 'Log management and analytics',
44
+ envVars: ['AXIOM_TOKEN', 'AXIOM_DATASET'],
45
+ docs: 'https://axiom.co/docs'
46
+ }
47
+ };
48
+
49
+ // Supported alert providers
50
+ const ALERT_PROVIDERS = {
51
+ slack: {
52
+ name: 'Slack',
53
+ description: 'Team messaging and notifications',
54
+ envVars: ['SLACK_WEBHOOK_URL'],
55
+ docs: 'https://api.slack.com/messaging/webhooks'
56
+ },
57
+ discord: {
58
+ name: 'Discord',
59
+ description: 'Community chat notifications',
60
+ envVars: ['DISCORD_WEBHOOK_URL'],
61
+ docs: 'https://discord.com/developers/docs/resources/webhook'
62
+ },
63
+ pagerduty: {
64
+ name: 'PagerDuty',
65
+ description: 'On-call management and incident response',
66
+ envVars: ['PAGERDUTY_ROUTING_KEY'],
67
+ docs: 'https://developer.pagerduty.com/'
68
+ },
69
+ email: {
70
+ name: 'Email',
71
+ description: 'Email notifications',
72
+ envVars: ['ALERT_EMAIL_TO', 'SMTP_HOST'],
73
+ docs: 'Configure SMTP settings for email alerts'
74
+ }
75
+ };
76
+
77
+ // Performance metrics to track
78
+ const PERFORMANCE_METRICS = {
79
+ bundleSize: {
80
+ name: 'Bundle Size',
81
+ description: 'JavaScript bundle size in KB',
82
+ command: 'du -sk .next 2>/dev/null || du -sk dist 2>/dev/null || echo "0"',
83
+ unit: 'KB'
84
+ },
85
+ buildTime: {
86
+ name: 'Build Time',
87
+ description: 'Time to build in seconds',
88
+ unit: 'seconds'
89
+ },
90
+ testCoverage: {
91
+ name: 'Test Coverage',
92
+ description: 'Code coverage percentage',
93
+ unit: '%'
94
+ },
95
+ dependencies: {
96
+ name: 'Dependencies',
97
+ description: 'Number of production dependencies',
98
+ unit: 'count'
99
+ }
100
+ };
101
+
102
+ /**
103
+ * Get monitor state directory
104
+ */
105
+ function getStateDir(projectRoot) {
106
+ return path.join(projectRoot, '.bootspring', 'monitor');
107
+ }
108
+
109
+ /**
110
+ * Load monitor configuration
111
+ */
112
+ function loadMonitorConfig(projectRoot) {
113
+ const stateDir = getStateDir(projectRoot);
114
+ const configPath = path.join(stateDir, 'config.json');
115
+
116
+ if (fs.existsSync(configPath)) {
117
+ try {
118
+ return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
119
+ } catch {
120
+ return createDefaultConfig();
121
+ }
122
+ }
123
+
124
+ return createDefaultConfig();
125
+ }
126
+
127
+ /**
128
+ * Create default monitor configuration
129
+ */
130
+ function createDefaultConfig() {
131
+ return {
132
+ version: '1.0.0',
133
+ monitoring: {
134
+ enabled: false,
135
+ providers: []
136
+ },
137
+ alerts: {
138
+ enabled: false,
139
+ providers: [],
140
+ thresholds: {
141
+ errorRatePercent: 1,
142
+ bundleSizeKB: 500,
143
+ buildTimeSeconds: 120
144
+ }
145
+ },
146
+ metrics: {
147
+ tracking: true,
148
+ history: []
149
+ },
150
+ hooks: {
151
+ postDeploy: [],
152
+ onError: [],
153
+ onAlert: []
154
+ }
155
+ };
156
+ }
157
+
158
+ /**
159
+ * Save monitor configuration
160
+ */
161
+ function saveMonitorConfig(projectRoot, monitorConfig) {
162
+ const stateDir = getStateDir(projectRoot);
163
+ if (!fs.existsSync(stateDir)) {
164
+ fs.mkdirSync(stateDir, { recursive: true });
165
+ }
166
+
167
+ const configPath = path.join(stateDir, 'config.json');
168
+ fs.writeFileSync(configPath, JSON.stringify(monitorConfig, null, 2));
169
+ }
170
+
171
+ /**
172
+ * Initialize monitoring
173
+ */
174
+ function initMonitoring(projectRoot) {
175
+ const monitorConfig = loadMonitorConfig(projectRoot);
176
+ const stateDir = getStateDir(projectRoot);
177
+
178
+ if (!fs.existsSync(stateDir)) {
179
+ fs.mkdirSync(stateDir, { recursive: true });
180
+ }
181
+
182
+ // Create subdirectories
183
+ const dirs = ['metrics', 'snapshots', 'alerts'];
184
+ for (const dir of dirs) {
185
+ const dirPath = path.join(stateDir, dir);
186
+ if (!fs.existsSync(dirPath)) {
187
+ fs.mkdirSync(dirPath, { recursive: true });
188
+ }
189
+ }
190
+
191
+ saveMonitorConfig(projectRoot, monitorConfig);
192
+
193
+ utils.print.success('Monitoring initialized');
194
+ console.log(`${utils.COLORS.dim}State directory: ${stateDir}${utils.COLORS.reset}`);
195
+ }
196
+
197
+ /**
198
+ * Show monitoring status
199
+ */
200
+ function showStatus(projectRoot) {
201
+ const monitorConfig = loadMonitorConfig(projectRoot);
202
+
203
+ console.log(`
204
+ ${utils.COLORS.cyan}${utils.COLORS.bold}📊 Monitoring Status${utils.COLORS.reset}
205
+ ${utils.COLORS.dim}Project: ${projectRoot}${utils.COLORS.reset}
206
+ `);
207
+
208
+ // Monitoring providers
209
+ console.log(`${utils.COLORS.bold}Monitoring Providers${utils.COLORS.reset}`);
210
+ if (monitorConfig.monitoring.providers.length === 0) {
211
+ console.log(` ${utils.COLORS.dim}No providers configured${utils.COLORS.reset}`);
212
+ } else {
213
+ for (const provider of monitorConfig.monitoring.providers) {
214
+ const info = MONITORING_PROVIDERS[provider.id];
215
+ const status = provider.enabled ?
216
+ `${utils.COLORS.green}✓ enabled${utils.COLORS.reset}` :
217
+ `${utils.COLORS.yellow}○ disabled${utils.COLORS.reset}`;
218
+ console.log(` ${info?.name || provider.id}: ${status}`);
219
+ }
220
+ }
221
+ console.log();
222
+
223
+ // Alert providers
224
+ console.log(`${utils.COLORS.bold}Alert Providers${utils.COLORS.reset}`);
225
+ if (monitorConfig.alerts.providers.length === 0) {
226
+ console.log(` ${utils.COLORS.dim}No alert providers configured${utils.COLORS.reset}`);
227
+ } else {
228
+ for (const provider of monitorConfig.alerts.providers) {
229
+ const info = ALERT_PROVIDERS[provider.id];
230
+ const status = provider.enabled ?
231
+ `${utils.COLORS.green}✓ enabled${utils.COLORS.reset}` :
232
+ `${utils.COLORS.yellow}○ disabled${utils.COLORS.reset}`;
233
+ console.log(` ${info?.name || provider.id}: ${status}`);
234
+ }
235
+ }
236
+ console.log();
237
+
238
+ // Thresholds
239
+ console.log(`${utils.COLORS.bold}Alert Thresholds${utils.COLORS.reset}`);
240
+ const thresholds = monitorConfig.alerts.thresholds;
241
+ console.log(` Error rate: ${thresholds.errorRatePercent}%`);
242
+ console.log(` Bundle size: ${thresholds.bundleSizeKB} KB`);
243
+ console.log(` Build time: ${thresholds.buildTimeSeconds}s`);
244
+ console.log();
245
+
246
+ // Recent metrics
247
+ const metricsDir = path.join(getStateDir(projectRoot), 'metrics');
248
+ if (fs.existsSync(metricsDir)) {
249
+ const files = fs.readdirSync(metricsDir).sort().slice(-5);
250
+ if (files.length > 0) {
251
+ console.log(`${utils.COLORS.bold}Recent Metrics${utils.COLORS.reset}`);
252
+ for (const file of files) {
253
+ try {
254
+ const metrics = JSON.parse(fs.readFileSync(path.join(metricsDir, file), 'utf-8'));
255
+ const date = new Date(metrics.timestamp).toLocaleDateString();
256
+ console.log(` ${date}: bundle=${metrics.bundleSize || 'N/A'}KB, deps=${metrics.dependencies || 'N/A'}`);
257
+ } catch {
258
+ // Skip invalid files
259
+ }
260
+ }
261
+ }
262
+ }
263
+ }
264
+
265
+ /**
266
+ * Collect current metrics
267
+ */
268
+ function collectMetrics(projectRoot) {
269
+ const spinner = utils.createSpinner('Collecting metrics...').start();
270
+
271
+ const metrics = {
272
+ timestamp: new Date().toISOString(),
273
+ bundleSize: null,
274
+ buildTime: null,
275
+ testCoverage: null,
276
+ dependencies: null,
277
+ devDependencies: null
278
+ };
279
+
280
+ try {
281
+ // Get bundle size
282
+ const buildDirs = ['.next', 'dist', 'build', 'out'];
283
+ for (const dir of buildDirs) {
284
+ const dirPath = path.join(projectRoot, dir);
285
+ if (fs.existsSync(dirPath)) {
286
+ try {
287
+ const output = execSync(`du -sk "${dirPath}"`, {
288
+ cwd: projectRoot,
289
+ encoding: 'utf-8',
290
+ stdio: ['pipe', 'pipe', 'pipe']
291
+ });
292
+ metrics.bundleSize = parseInt(output.split('\t')[0], 10);
293
+ break;
294
+ } catch {
295
+ // Continue to next directory
296
+ }
297
+ }
298
+ }
299
+
300
+ // Get dependency counts from package.json
301
+ const pkgPath = path.join(projectRoot, 'package.json');
302
+ if (fs.existsSync(pkgPath)) {
303
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
304
+ metrics.dependencies = Object.keys(pkg.dependencies || {}).length;
305
+ metrics.devDependencies = Object.keys(pkg.devDependencies || {}).length;
306
+ }
307
+
308
+ // Get test coverage if available
309
+ const coveragePath = path.join(projectRoot, 'coverage', 'coverage-summary.json');
310
+ if (fs.existsSync(coveragePath)) {
311
+ try {
312
+ const coverage = JSON.parse(fs.readFileSync(coveragePath, 'utf-8'));
313
+ metrics.testCoverage = coverage.total?.lines?.pct || null;
314
+ } catch {
315
+ // Coverage file invalid
316
+ }
317
+ }
318
+
319
+ spinner.succeed('Metrics collected');
320
+ } catch (error) {
321
+ spinner.fail(`Failed to collect metrics: ${error.message}`);
322
+ }
323
+
324
+ // Save metrics
325
+ const metricsDir = path.join(getStateDir(projectRoot), 'metrics');
326
+ if (!fs.existsSync(metricsDir)) {
327
+ fs.mkdirSync(metricsDir, { recursive: true });
328
+ }
329
+
330
+ const filename = `metrics-${new Date().toISOString().split('T')[0]}.json`;
331
+ fs.writeFileSync(path.join(metricsDir, filename), JSON.stringify(metrics, null, 2));
332
+
333
+ // Display metrics
334
+ console.log();
335
+ console.log(`${utils.COLORS.bold}Current Metrics${utils.COLORS.reset}`);
336
+ console.log(` Bundle size: ${metrics.bundleSize !== null ? `${metrics.bundleSize} KB` : 'N/A'}`);
337
+ console.log(` Dependencies: ${metrics.dependencies !== null ? metrics.dependencies : 'N/A'}`);
338
+ console.log(` Dev deps: ${metrics.devDependencies !== null ? metrics.devDependencies : 'N/A'}`);
339
+ console.log(` Test coverage: ${metrics.testCoverage !== null ? `${metrics.testCoverage}%` : 'N/A'}`);
340
+
341
+ return metrics;
342
+ }
343
+
344
+ /**
345
+ * Add monitoring provider
346
+ */
347
+ function addProvider(projectRoot, type, providerId) {
348
+ const providers = type === 'monitoring' ? MONITORING_PROVIDERS : ALERT_PROVIDERS;
349
+
350
+ if (!providers[providerId]) {
351
+ utils.print.error(`Unknown ${type} provider: ${providerId}`);
352
+ console.log(`${utils.COLORS.dim}Available: ${Object.keys(providers).join(', ')}${utils.COLORS.reset}`);
353
+ return;
354
+ }
355
+
356
+ const provider = providers[providerId];
357
+ const monitorConfig = loadMonitorConfig(projectRoot);
358
+
359
+ // Check if already added
360
+ const existing = monitorConfig[type].providers.find(p => p.id === providerId);
361
+ if (existing) {
362
+ utils.print.warn(`${provider.name} is already configured`);
363
+ return;
364
+ }
365
+
366
+ // Add provider
367
+ monitorConfig[type].providers.push({
368
+ id: providerId,
369
+ enabled: true,
370
+ addedAt: new Date().toISOString()
371
+ });
372
+ monitorConfig[type].enabled = true;
373
+
374
+ saveMonitorConfig(projectRoot, monitorConfig);
375
+
376
+ utils.print.success(`Added ${provider.name}`);
377
+ console.log();
378
+ console.log(`${utils.COLORS.bold}Required Environment Variables${utils.COLORS.reset}`);
379
+ for (const envVar of provider.envVars) {
380
+ const hasVar = process.env[envVar] ?
381
+ `${utils.COLORS.green}✓${utils.COLORS.reset}` :
382
+ `${utils.COLORS.yellow}○${utils.COLORS.reset}`;
383
+ console.log(` ${hasVar} ${envVar}`);
384
+ }
385
+ console.log();
386
+ console.log(`${utils.COLORS.dim}Documentation: ${provider.docs}${utils.COLORS.reset}`);
387
+ }
388
+
389
+ /**
390
+ * Remove monitoring provider
391
+ */
392
+ function removeProvider(projectRoot, type, providerId) {
393
+ const monitorConfig = loadMonitorConfig(projectRoot);
394
+
395
+ const index = monitorConfig[type].providers.findIndex(p => p.id === providerId);
396
+ if (index === -1) {
397
+ utils.print.error(`Provider ${providerId} is not configured`);
398
+ return;
399
+ }
400
+
401
+ monitorConfig[type].providers.splice(index, 1);
402
+ if (monitorConfig[type].providers.length === 0) {
403
+ monitorConfig[type].enabled = false;
404
+ }
405
+
406
+ saveMonitorConfig(projectRoot, monitorConfig);
407
+ utils.print.success(`Removed ${providerId}`);
408
+ }
409
+
410
+ /**
411
+ * Run post-deploy hooks
412
+ */
413
+ async function runPostDeploy(projectRoot, options = {}) {
414
+ const monitorConfig = loadMonitorConfig(projectRoot);
415
+
416
+ console.log(`
417
+ ${utils.COLORS.cyan}${utils.COLORS.bold}🚀 Post-Deploy Hooks${utils.COLORS.reset}
418
+ `);
419
+
420
+ const results = {
421
+ timestamp: new Date().toISOString(),
422
+ deployTarget: options.target || 'unknown',
423
+ steps: []
424
+ };
425
+
426
+ // Step 1: Collect metrics
427
+ console.log(`${utils.COLORS.bold}1. Collecting Metrics${utils.COLORS.reset}`);
428
+ const metrics = collectMetrics(projectRoot);
429
+ results.steps.push({ name: 'metrics', status: 'success', data: metrics });
430
+ console.log();
431
+
432
+ // Step 2: Check thresholds
433
+ console.log(`${utils.COLORS.bold}2. Checking Thresholds${utils.COLORS.reset}`);
434
+ const thresholds = monitorConfig.alerts.thresholds;
435
+ let hasWarnings = false;
436
+
437
+ if (metrics.bundleSize && metrics.bundleSize > thresholds.bundleSizeKB) {
438
+ console.log(` ${utils.COLORS.yellow}⚠${utils.COLORS.reset} Bundle size (${metrics.bundleSize}KB) exceeds threshold (${thresholds.bundleSizeKB}KB)`);
439
+ hasWarnings = true;
440
+ } else if (metrics.bundleSize) {
441
+ console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} Bundle size within threshold`);
442
+ }
443
+
444
+ if (hasWarnings) {
445
+ results.steps.push({ name: 'thresholds', status: 'warning' });
446
+ } else {
447
+ results.steps.push({ name: 'thresholds', status: 'success' });
448
+ }
449
+ console.log();
450
+
451
+ // Step 3: Regenerate context
452
+ console.log(`${utils.COLORS.bold}3. Regenerating Context${utils.COLORS.reset}`);
453
+ try {
454
+ // Update CLAUDE.md with production status
455
+ await regenerateContext(projectRoot, options);
456
+ results.steps.push({ name: 'context', status: 'success' });
457
+ console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} Context updated`);
458
+ } catch (error) {
459
+ results.steps.push({ name: 'context', status: 'error', error: error.message });
460
+ console.log(` ${utils.COLORS.yellow}⚠${utils.COLORS.reset} Context update failed: ${error.message}`);
461
+ }
462
+ console.log();
463
+
464
+ // Step 4: Send alerts if configured
465
+ if (monitorConfig.alerts.enabled && monitorConfig.alerts.providers.length > 0) {
466
+ console.log(`${utils.COLORS.bold}4. Sending Notifications${utils.COLORS.reset}`);
467
+ for (const provider of monitorConfig.alerts.providers) {
468
+ if (provider.enabled) {
469
+ await sendDeployNotification(provider.id, {
470
+ target: options.target,
471
+ metrics,
472
+ warnings: hasWarnings
473
+ });
474
+ }
475
+ }
476
+ console.log();
477
+ }
478
+
479
+ // Save post-deploy snapshot
480
+ const snapshotsDir = path.join(getStateDir(projectRoot), 'snapshots');
481
+ if (!fs.existsSync(snapshotsDir)) {
482
+ fs.mkdirSync(snapshotsDir, { recursive: true });
483
+ }
484
+
485
+ const snapshotFile = `deploy-${new Date().toISOString().replace(/[:.]/g, '-')}.json`;
486
+ fs.writeFileSync(path.join(snapshotsDir, snapshotFile), JSON.stringify(results, null, 2));
487
+
488
+ utils.print.success('Post-deploy hooks completed');
489
+ console.log(`${utils.COLORS.dim}Snapshot saved: ${snapshotFile}${utils.COLORS.reset}`);
490
+
491
+ return results;
492
+ }
493
+
494
+ /**
495
+ * Regenerate production context
496
+ */
497
+ async function regenerateContext(projectRoot, options = {}) {
498
+ // Update the CLAUDE.md with production deployment info
499
+ const claudeMdPath = path.join(projectRoot, 'CLAUDE.md');
500
+
501
+ if (!fs.existsSync(claudeMdPath)) {
502
+ return;
503
+ }
504
+
505
+ let content = fs.readFileSync(claudeMdPath, 'utf-8');
506
+
507
+ // Update status
508
+ content = content.replace(
509
+ /\*\*Status\*\*: \w+/,
510
+ '**Status**: production'
511
+ );
512
+
513
+ // Update last deployed
514
+ const deployDate = new Date().toISOString().split('T')[0];
515
+ if (content.includes('**Last Deployed**:')) {
516
+ content = content.replace(
517
+ /\*\*Last Deployed\*\*: .*/,
518
+ `**Last Deployed**: ${deployDate}`
519
+ );
520
+ } else if (content.includes('**Last Updated**:')) {
521
+ content = content.replace(
522
+ /\*\*Last Updated\*\*: .*/,
523
+ `**Last Updated**: ${deployDate}\n**Last Deployed**: ${deployDate}`
524
+ );
525
+ }
526
+
527
+ // Add deployment target if specified
528
+ if (options.target) {
529
+ if (content.includes('**Deployed To**:')) {
530
+ content = content.replace(
531
+ /\*\*Deployed To\*\*: .*/,
532
+ `**Deployed To**: ${options.target}`
533
+ );
534
+ }
535
+ }
536
+
537
+ fs.writeFileSync(claudeMdPath, content);
538
+ }
539
+
540
+ /**
541
+ * Send deploy notification to provider
542
+ */
543
+ async function sendDeployNotification(providerId, data) {
544
+ const { target, metrics, warnings } = data;
545
+
546
+ switch (providerId) {
547
+ case 'slack': {
548
+ const webhookUrl = process.env.SLACK_WEBHOOK_URL;
549
+ if (!webhookUrl) {
550
+ console.log(` ${utils.COLORS.yellow}○${utils.COLORS.reset} Slack: SLACK_WEBHOOK_URL not set`);
551
+ return;
552
+ }
553
+
554
+ const message = {
555
+ text: '🚀 Deployment Complete',
556
+ blocks: [
557
+ {
558
+ type: 'section',
559
+ text: {
560
+ type: 'mrkdwn',
561
+ text: `*Deployment Complete*\nTarget: ${target}\nBundle: ${metrics.bundleSize || 'N/A'} KB\n${warnings ? '⚠️ Warnings detected' : '✅ All checks passed'}`
562
+ }
563
+ }
564
+ ]
565
+ };
566
+
567
+ try {
568
+ execSync(`curl -s -X POST -H 'Content-type: application/json' --data '${JSON.stringify(message)}' "${webhookUrl}"`, {
569
+ stdio: 'pipe'
570
+ });
571
+ console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} Slack notification sent`);
572
+ } catch {
573
+ console.log(` ${utils.COLORS.yellow}○${utils.COLORS.reset} Slack notification failed`);
574
+ }
575
+ break;
576
+ }
577
+
578
+ case 'discord': {
579
+ const webhookUrl = process.env.DISCORD_WEBHOOK_URL;
580
+ if (!webhookUrl) {
581
+ console.log(` ${utils.COLORS.yellow}○${utils.COLORS.reset} Discord: DISCORD_WEBHOOK_URL not set`);
582
+ return;
583
+ }
584
+
585
+ const message = {
586
+ content: `🚀 **Deployment Complete**\nTarget: ${target}\nBundle: ${metrics.bundleSize || 'N/A'} KB\n${warnings ? '⚠️ Warnings detected' : '✅ All checks passed'}`
587
+ };
588
+
589
+ try {
590
+ execSync(`curl -s -X POST -H 'Content-type: application/json' --data '${JSON.stringify(message)}' "${webhookUrl}"`, {
591
+ stdio: 'pipe'
592
+ });
593
+ console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} Discord notification sent`);
594
+ } catch {
595
+ console.log(` ${utils.COLORS.yellow}○${utils.COLORS.reset} Discord notification failed`);
596
+ }
597
+ break;
598
+ }
599
+
600
+ default:
601
+ console.log(` ${utils.COLORS.dim}${providerId}: Notification not implemented${utils.COLORS.reset}`);
602
+ }
603
+ }
604
+
605
+ /**
606
+ * List available providers
607
+ */
608
+ function listProviders() {
609
+ console.log(`
610
+ ${utils.COLORS.cyan}${utils.COLORS.bold}📊 Available Providers${utils.COLORS.reset}
611
+
612
+ ${utils.COLORS.bold}Monitoring${utils.COLORS.reset}
613
+ `);
614
+
615
+ for (const [id, provider] of Object.entries(MONITORING_PROVIDERS)) {
616
+ console.log(` ${utils.COLORS.cyan}${id}${utils.COLORS.reset}`);
617
+ console.log(` ${provider.description}`);
618
+ console.log(` ${utils.COLORS.dim}Env: ${provider.envVars.join(', ')}${utils.COLORS.reset}`);
619
+ console.log();
620
+ }
621
+
622
+ console.log(`${utils.COLORS.bold}Alerts${utils.COLORS.reset}
623
+ `);
624
+
625
+ for (const [id, provider] of Object.entries(ALERT_PROVIDERS)) {
626
+ console.log(` ${utils.COLORS.cyan}${id}${utils.COLORS.reset}`);
627
+ console.log(` ${provider.description}`);
628
+ console.log(` ${utils.COLORS.dim}Env: ${provider.envVars.join(', ')}${utils.COLORS.reset}`);
629
+ console.log();
630
+ }
631
+ }
632
+
633
+ /**
634
+ * Set alert threshold
635
+ */
636
+ function setThreshold(projectRoot, name, value) {
637
+ const validThresholds = ['errorRatePercent', 'bundleSizeKB', 'buildTimeSeconds'];
638
+
639
+ if (!validThresholds.includes(name)) {
640
+ utils.print.error(`Unknown threshold: ${name}`);
641
+ console.log(`${utils.COLORS.dim}Available: ${validThresholds.join(', ')}${utils.COLORS.reset}`);
642
+ return;
643
+ }
644
+
645
+ const numValue = parseFloat(value);
646
+ if (isNaN(numValue)) {
647
+ utils.print.error('Value must be a number');
648
+ return;
649
+ }
650
+
651
+ const monitorConfig = loadMonitorConfig(projectRoot);
652
+ monitorConfig.alerts.thresholds[name] = numValue;
653
+ saveMonitorConfig(projectRoot, monitorConfig);
654
+
655
+ utils.print.success(`Set ${name} to ${numValue}`);
656
+ }
657
+
658
+ /**
659
+ * Show help
660
+ */
661
+ function showHelp() {
662
+ console.log(`
663
+ ${utils.COLORS.cyan}${utils.COLORS.bold}📊 Bootspring Monitor${utils.COLORS.reset}
664
+ ${utils.COLORS.dim}Production monitoring, hooks, and alerting${utils.COLORS.reset}
665
+
666
+ ${utils.COLORS.bold}Usage:${utils.COLORS.reset}
667
+ bootspring monitor <command> [options]
668
+
669
+ ${utils.COLORS.bold}Commands:${utils.COLORS.reset}
670
+ ${utils.COLORS.cyan}init${utils.COLORS.reset} Initialize monitoring
671
+ ${utils.COLORS.cyan}status${utils.COLORS.reset} Show monitoring status
672
+ ${utils.COLORS.cyan}metrics${utils.COLORS.reset} Collect current metrics
673
+ ${utils.COLORS.cyan}providers${utils.COLORS.reset} List available providers
674
+ ${utils.COLORS.cyan}post-deploy${utils.COLORS.reset} Run post-deploy hooks
675
+
676
+ ${utils.COLORS.bold}Provider Management:${utils.COLORS.reset}
677
+ ${utils.COLORS.cyan}add-monitoring${utils.COLORS.reset} <id> Add monitoring provider
678
+ ${utils.COLORS.cyan}add-alert${utils.COLORS.reset} <id> Add alert provider
679
+ ${utils.COLORS.cyan}remove-monitoring${utils.COLORS.reset} <id> Remove monitoring provider
680
+ ${utils.COLORS.cyan}remove-alert${utils.COLORS.reset} <id> Remove alert provider
681
+
682
+ ${utils.COLORS.bold}Configuration:${utils.COLORS.reset}
683
+ ${utils.COLORS.cyan}threshold${utils.COLORS.reset} <name> <val> Set alert threshold
684
+
685
+ ${utils.COLORS.bold}Monitoring Providers:${utils.COLORS.reset}
686
+ sentry, datadog, newrelic, logrocket, axiom
687
+
688
+ ${utils.COLORS.bold}Alert Providers:${utils.COLORS.reset}
689
+ slack, discord, pagerduty, email
690
+
691
+ ${utils.COLORS.bold}Examples:${utils.COLORS.reset}
692
+ bootspring monitor init
693
+ bootspring monitor status
694
+ bootspring monitor metrics
695
+ bootspring monitor add-alert slack
696
+ bootspring monitor add-monitoring sentry
697
+ bootspring monitor threshold bundleSizeKB 1000
698
+ bootspring monitor post-deploy --target=vercel
699
+ `);
700
+ }
701
+
702
+ /**
703
+ * Run monitor command
704
+ */
705
+ async function run(args) {
706
+ const parsedArgs = utils.parseArgs(args);
707
+ const subcommand = parsedArgs._[0];
708
+
709
+ if (!subcommand) {
710
+ showHelp();
711
+ return;
712
+ }
713
+
714
+ const cfg = config.load();
715
+ const projectRoot = cfg._projectRoot;
716
+
717
+ switch (subcommand) {
718
+ case 'init':
719
+ initMonitoring(projectRoot);
720
+ break;
721
+
722
+ case 'status':
723
+ showStatus(projectRoot);
724
+ break;
725
+
726
+ case 'metrics':
727
+ collectMetrics(projectRoot);
728
+ break;
729
+
730
+ case 'providers':
731
+ listProviders();
732
+ break;
733
+
734
+ case 'add-monitoring': {
735
+ const providerId = parsedArgs._[1];
736
+ if (!providerId) {
737
+ utils.print.error('Provider ID required');
738
+ console.log(`${utils.COLORS.dim}Usage: bootspring monitor add-monitoring <provider>${utils.COLORS.reset}`);
739
+ return;
740
+ }
741
+ addProvider(projectRoot, 'monitoring', providerId);
742
+ break;
743
+ }
744
+
745
+ case 'add-alert': {
746
+ const providerId = parsedArgs._[1];
747
+ if (!providerId) {
748
+ utils.print.error('Provider ID required');
749
+ console.log(`${utils.COLORS.dim}Usage: bootspring monitor add-alert <provider>${utils.COLORS.reset}`);
750
+ return;
751
+ }
752
+ addProvider(projectRoot, 'alerts', providerId);
753
+ break;
754
+ }
755
+
756
+ case 'remove-monitoring': {
757
+ const providerId = parsedArgs._[1];
758
+ if (!providerId) {
759
+ utils.print.error('Provider ID required');
760
+ return;
761
+ }
762
+ removeProvider(projectRoot, 'monitoring', providerId);
763
+ break;
764
+ }
765
+
766
+ case 'remove-alert': {
767
+ const providerId = parsedArgs._[1];
768
+ if (!providerId) {
769
+ utils.print.error('Provider ID required');
770
+ return;
771
+ }
772
+ removeProvider(projectRoot, 'alerts', providerId);
773
+ break;
774
+ }
775
+
776
+ case 'threshold': {
777
+ const name = parsedArgs._[1];
778
+ const value = parsedArgs._[2];
779
+ if (!name || !value) {
780
+ utils.print.error('Name and value required');
781
+ console.log(`${utils.COLORS.dim}Usage: bootspring monitor threshold <name> <value>${utils.COLORS.reset}`);
782
+ return;
783
+ }
784
+ setThreshold(projectRoot, name, value);
785
+ break;
786
+ }
787
+
788
+ case 'post-deploy':
789
+ await runPostDeploy(projectRoot, {
790
+ target: parsedArgs.target
791
+ });
792
+ break;
793
+
794
+ case 'help':
795
+ case '--help':
796
+ case '-h':
797
+ showHelp();
798
+ break;
799
+
800
+ default:
801
+ utils.print.error(`Unknown command: ${subcommand}`);
802
+ showHelp();
803
+ }
804
+ }
805
+
806
+ module.exports = {
807
+ run,
808
+ MONITORING_PROVIDERS,
809
+ ALERT_PROVIDERS,
810
+ collectMetrics,
811
+ runPostDeploy
812
+ };