@equilateral_ai/mindmeld 3.5.3 → 4.0.2

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 (138) hide show
  1. package/hooks/session-start.js +312 -85
  2. package/package.json +21 -13
  3. package/scripts/init-project.js +9 -23
  4. package/scripts/repo-analyzer.js +118 -2
  5. package/src/client/dbShim.js +16 -0
  6. package/src/core/AuthManager.js +3 -2
  7. package/src/handlers/helpers/dbOperations.js +9 -46
  8. package/src/index.js +2 -217
  9. package/src/utils/piiMask.js +16 -0
  10. package/scripts/inject.js +0 -409
  11. package/scripts/mcp-bridge.js +0 -220
  12. package/scripts/standards.js +0 -285
  13. package/src/collaboration/CollaborationPrompt.js +0 -460
  14. package/src/core/AlertEngine.js +0 -813
  15. package/src/core/AlertNotifier.js +0 -363
  16. package/src/core/CorrelationAnalyzer.js +0 -931
  17. package/src/core/CrossReferenceEngine.js +0 -624
  18. package/src/core/CurationEngine.js +0 -688
  19. package/src/core/DeprecationScheduler.js +0 -183
  20. package/src/core/LoadBearingDetector.js +0 -242
  21. package/src/core/NotificationService.js +0 -1032
  22. package/src/core/RapportOrchestrator.js +0 -632
  23. package/src/core/RelevanceDetector.js +0 -694
  24. package/src/core/StandardLifecycle.js +0 -244
  25. package/src/core/StandardsIngestion.js +0 -991
  26. package/src/core/TeamLoadBearingDetector.js +0 -431
  27. package/src/core/parsers/adrParser.js +0 -479
  28. package/src/core/parsers/cursorRulesParser.js +0 -564
  29. package/src/core/parsers/eslintParser.js +0 -439
  30. package/src/database/dbOperations.js +0 -105
  31. package/src/handlers/activity/activityGetMe.js +0 -98
  32. package/src/handlers/activity/activityGetTeam.js +0 -175
  33. package/src/handlers/admin/adminSetup.js +0 -216
  34. package/src/handlers/alerts/alertsAcknowledge.js +0 -92
  35. package/src/handlers/alerts/alertsGet.js +0 -250
  36. package/src/handlers/analytics/activitySummaryGet.js +0 -234
  37. package/src/handlers/analytics/coachingGet.js +0 -361
  38. package/src/handlers/analytics/convergenceGet.js +0 -236
  39. package/src/handlers/analytics/developerScoreGet.js +0 -137
  40. package/src/handlers/collaborators/collaboratorAdd.js +0 -200
  41. package/src/handlers/collaborators/collaboratorInvite.js +0 -219
  42. package/src/handlers/collaborators/collaboratorList.js +0 -82
  43. package/src/handlers/collaborators/collaboratorRemove.js +0 -128
  44. package/src/handlers/collaborators/inviteAccept.js +0 -122
  45. package/src/handlers/company/companyUsersDelete.js +0 -141
  46. package/src/handlers/company/companyUsersGet.js +0 -90
  47. package/src/handlers/company/companyUsersPost.js +0 -267
  48. package/src/handlers/company/companyUsersPut.js +0 -76
  49. package/src/handlers/context/contextGet.js +0 -57
  50. package/src/handlers/context/invariantsGet.js +0 -74
  51. package/src/handlers/context/loopsGet.js +0 -82
  52. package/src/handlers/context/notesCreate.js +0 -74
  53. package/src/handlers/context/purposeGet.js +0 -78
  54. package/src/handlers/correlations/correlationsDeveloperGet.js +0 -227
  55. package/src/handlers/correlations/correlationsGet.js +0 -93
  56. package/src/handlers/correlations/correlationsProjectGet.js +0 -153
  57. package/src/handlers/enterprise/controlTowerGet.js +0 -224
  58. package/src/handlers/enterprise/enterpriseAuditGet.js +0 -108
  59. package/src/handlers/enterprise/enterpriseContributorsGet.js +0 -85
  60. package/src/handlers/enterprise/enterpriseKnowledgeCategoriesGet.js +0 -53
  61. package/src/handlers/enterprise/enterpriseKnowledgeCreate.js +0 -77
  62. package/src/handlers/enterprise/enterpriseKnowledgeDelete.js +0 -71
  63. package/src/handlers/enterprise/enterpriseKnowledgeGet.js +0 -87
  64. package/src/handlers/enterprise/enterpriseKnowledgeUpdate.js +0 -122
  65. package/src/handlers/enterprise/enterpriseOnboardingComplete.js +0 -77
  66. package/src/handlers/enterprise/enterpriseOnboardingInvite.js +0 -138
  67. package/src/handlers/enterprise/enterpriseOnboardingSetup.js +0 -128
  68. package/src/handlers/enterprise/enterpriseOnboardingStatus.js +0 -88
  69. package/src/handlers/github/githubConnectionStatus.js +0 -49
  70. package/src/handlers/github/githubDiscoverPatterns.js +0 -621
  71. package/src/handlers/github/githubOAuthCallback.js +0 -178
  72. package/src/handlers/github/githubOAuthStart.js +0 -59
  73. package/src/handlers/github/githubPatternsReview.js +0 -76
  74. package/src/handlers/github/githubReposList.js +0 -105
  75. package/src/handlers/health/healthGet.js +0 -55
  76. package/src/handlers/helpers/auditLogger.js +0 -201
  77. package/src/handlers/helpers/checkSuperAdmin.js +0 -84
  78. package/src/handlers/helpers/decisionFrames.js +0 -29
  79. package/src/handlers/helpers/errorHandler.js +0 -49
  80. package/src/handlers/helpers/index.js +0 -138
  81. package/src/handlers/helpers/lambdaWrapper.js +0 -60
  82. package/src/handlers/helpers/mindmeldMcpCore.js +0 -1103
  83. package/src/handlers/helpers/predictiveCache.js +0 -51
  84. package/src/handlers/helpers/projectAccess.js +0 -88
  85. package/src/handlers/helpers/responseUtil.js +0 -55
  86. package/src/handlers/helpers/subscriptionTiers.js +0 -1168
  87. package/src/handlers/mcp/mcpHandler.js +0 -569
  88. package/src/handlers/mcp/mindmeldMcpHandler.js +0 -124
  89. package/src/handlers/mcp/mindmeldMcpStreamHandler.js +0 -342
  90. package/src/handlers/notifications/getPreferences.js +0 -84
  91. package/src/handlers/notifications/sendNotification.js +0 -170
  92. package/src/handlers/notifications/updatePreferences.js +0 -316
  93. package/src/handlers/patterns/patternEvaluatePromotionPost.js +0 -173
  94. package/src/handlers/patterns/patternUsagePost.js +0 -182
  95. package/src/handlers/patterns/patternViolationPost.js +0 -185
  96. package/src/handlers/projects/projectCreate.js +0 -248
  97. package/src/handlers/projects/projectDelete.js +0 -82
  98. package/src/handlers/projects/projectGet.js +0 -95
  99. package/src/handlers/projects/projectUpdate.js +0 -117
  100. package/src/handlers/reports/aiLeverage.js +0 -210
  101. package/src/handlers/reports/engineeringInvestment.js +0 -132
  102. package/src/handlers/reports/riskForecast.js +0 -206
  103. package/src/handlers/reports/standardsRoi.js +0 -254
  104. package/src/handlers/scheduled/analyzeCorrelations.js +0 -178
  105. package/src/handlers/scheduled/analyzeGitHistory.js +0 -510
  106. package/src/handlers/scheduled/generateAlerts.js +0 -135
  107. package/src/handlers/scheduled/maturityUpdateJob.js +0 -166
  108. package/src/handlers/scheduled/refreshActivity.js +0 -21
  109. package/src/handlers/scheduled/scanCompliance.js +0 -334
  110. package/src/handlers/sessions/sessionEndPost.js +0 -180
  111. package/src/handlers/sessions/sessionStandardsPost.js +0 -171
  112. package/src/handlers/standards/catalogGet.js +0 -185
  113. package/src/handlers/standards/catalogSync.js +0 -120
  114. package/src/handlers/standards/discoveriesGet.js +0 -89
  115. package/src/handlers/standards/projectStandardsGet.js +0 -129
  116. package/src/handlers/standards/projectStandardsPut.js +0 -151
  117. package/src/handlers/standards/standardsAuditGet.js +0 -65
  118. package/src/handlers/standards/standardsParseUpload.js +0 -149
  119. package/src/handlers/standards/standardsRelevantPost.js +0 -405
  120. package/src/handlers/standards/standardsTransition.js +0 -161
  121. package/src/handlers/stripe/addonManagePost.js +0 -240
  122. package/src/handlers/stripe/billingPortalPost.js +0 -93
  123. package/src/handlers/stripe/enterpriseCheckoutPost.js +0 -272
  124. package/src/handlers/stripe/seatsUpdatePost.js +0 -185
  125. package/src/handlers/stripe/subscriptionCancelDelete.js +0 -169
  126. package/src/handlers/stripe/subscriptionCreatePost.js +0 -221
  127. package/src/handlers/stripe/subscriptionUpdatePut.js +0 -163
  128. package/src/handlers/stripe/webhookPost.js +0 -482
  129. package/src/handlers/user/apiTokenCreate.js +0 -71
  130. package/src/handlers/user/apiTokenList.js +0 -64
  131. package/src/handlers/user/userSplashAck.js +0 -91
  132. package/src/handlers/user/userSplashGet.js +0 -211
  133. package/src/handlers/users/cognitoPostConfirmation.js +0 -186
  134. package/src/handlers/users/cognitoPreSignUp.js +0 -114
  135. package/src/handlers/users/userEntitlementsGet.js +0 -89
  136. package/src/handlers/users/userGet.js +0 -118
  137. package/src/handlers/users/userProfilePut.js +0 -77
  138. package/src/handlers/webhooks/githubWebhook.js +0 -215
@@ -1,71 +0,0 @@
1
- /**
2
- * API Token Create Handler
3
- *
4
- * Creates a new MCP API token for the authenticated user.
5
- * Token plaintext is returned ONCE — only the SHA-256 hash is stored.
6
- *
7
- * POST /api/user/api-tokens
8
- * Auth: Cognito JWT required
9
- * Body: { name?: string }
10
- */
11
-
12
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse } = require('./helpers');
13
- const crypto = require('crypto');
14
-
15
- async function apiTokenCreate({ body, requestContext }) {
16
- const email = requestContext.authorizer?.claims?.email
17
- || requestContext.authorizer?.jwt?.claims?.email;
18
-
19
- if (!email) {
20
- return createErrorResponse(401, 'Authentication required');
21
- }
22
-
23
- const tokenName = body.name || 'Default';
24
-
25
- // Look up user's client and company
26
- const userResult = await executeQuery(`
27
- SELECT u.client_id, ue.company_id
28
- FROM rapport.users u
29
- LEFT JOIN rapport.user_entitlements ue ON u.email_address = ue.email_address
30
- WHERE u.email_address = $1
31
- LIMIT 1
32
- `, [email]);
33
-
34
- if (userResult.rows.length === 0) {
35
- return createErrorResponse(404, 'User not found');
36
- }
37
-
38
- const { client_id, company_id } = userResult.rows[0];
39
-
40
- // Verify active subscription
41
- const clientResult = await executeQuery(
42
- 'SELECT subscription_tier FROM rapport.clients WHERE client_id = $1',
43
- [client_id]
44
- );
45
-
46
- if (clientResult.rows.length === 0 || !clientResult.rows[0].subscription_tier || clientResult.rows[0].subscription_tier === 'free') {
47
- return createErrorResponse(403, 'Active MindMeld subscription required to create API tokens');
48
- }
49
-
50
- // Generate token: mm_live_ + 40 random hex chars
51
- const tokenId = crypto.randomUUID();
52
- const tokenRandom = crypto.randomBytes(20).toString('hex');
53
- const plaintext = `mm_live_${tokenRandom}`;
54
- const tokenPrefix = plaintext.substring(0, 12);
55
- const tokenHash = crypto.createHash('sha256').update(plaintext).digest('hex');
56
-
57
- await executeQuery(`
58
- INSERT INTO rapport.api_tokens (token_id, token_hash, token_prefix, email_address, client_id, company_id, token_name)
59
- VALUES ($1, $2, $3, $4, $5, $6, $7)
60
- `, [tokenId, tokenHash, tokenPrefix, email, client_id, company_id || null, tokenName]);
61
-
62
- return createSuccessResponse({
63
- token_id: tokenId,
64
- token: plaintext,
65
- token_prefix: tokenPrefix,
66
- name: tokenName,
67
- message: 'Save this token — it will not be shown again.'
68
- }, 'API token created');
69
- }
70
-
71
- exports.handler = wrapHandler(apiTokenCreate);
@@ -1,64 +0,0 @@
1
- /**
2
- * API Token List/Revoke Handler
3
- *
4
- * GET /api/user/api-tokens — List tokens (prefix only, never plaintext)
5
- * DELETE /api/user/api-tokens?token_id=xxx — Revoke a token
6
- *
7
- * Auth: Cognito JWT required
8
- */
9
-
10
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse } = require('./helpers');
11
-
12
- async function apiTokenList({ body, queryParams, requestContext, httpMethod }) {
13
- const email = requestContext.authorizer?.claims?.email
14
- || requestContext.authorizer?.jwt?.claims?.email;
15
-
16
- if (!email) {
17
- return createErrorResponse(401, 'Authentication required');
18
- }
19
-
20
- // DELETE — revoke a token
21
- if (httpMethod === 'DELETE') {
22
- const tokenId = queryParams.token_id || body.token_id;
23
- if (!tokenId) {
24
- return createErrorResponse(400, 'token_id is required');
25
- }
26
-
27
- const result = await executeQuery(`
28
- UPDATE rapport.api_tokens
29
- SET status = 'revoked', revoked_at = NOW()
30
- WHERE token_id = $1 AND email_address = $2 AND status = 'active'
31
- RETURNING token_id
32
- `, [tokenId, email]);
33
-
34
- if (result.rows.length === 0) {
35
- return createErrorResponse(404, 'Token not found or already revoked');
36
- }
37
-
38
- return createSuccessResponse({ token_id: tokenId }, 'Token revoked');
39
- }
40
-
41
- // GET — list tokens
42
- const result = await executeQuery(`
43
- SELECT token_id, token_prefix, token_name, status,
44
- last_used_at, request_count, created_at, revoked_at
45
- FROM rapport.api_tokens
46
- WHERE email_address = $1
47
- ORDER BY created_at DESC
48
- `, [email]);
49
-
50
- return createSuccessResponse({
51
- tokens: result.rows.map(r => ({
52
- token_id: r.token_id,
53
- prefix: r.token_prefix,
54
- name: r.token_name,
55
- status: r.status,
56
- last_used: r.last_used_at,
57
- request_count: r.request_count,
58
- created_at: r.created_at,
59
- revoked_at: r.revoked_at
60
- }))
61
- }, `${result.rows.length} token(s) found`);
62
- }
63
-
64
- exports.handler = wrapHandler(apiTokenList);
@@ -1,91 +0,0 @@
1
- /**
2
- * User Splash Acknowledge Handler
3
- * Records that the user has dismissed the weekly splash screen
4
- *
5
- * POST /api/user/splash/acknowledge
6
- * Auth: Cognito JWT required
7
- *
8
- * Body: { week_start: "2026-01-27" }
9
- * Records acknowledgment so splash does not show again this week
10
- */
11
-
12
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
13
-
14
- async function acknowledgeUserSplash({ requestContext, body }) {
15
- const Request_ID = requestContext.requestId;
16
- const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
17
-
18
- if (!email) {
19
- return createErrorResponse(401, 'Authentication required');
20
- }
21
-
22
- const weekStart = body?.week_start;
23
- if (!weekStart) {
24
- return createErrorResponse(400, 'week_start is required');
25
- }
26
-
27
- // Validate week_start format (YYYY-MM-DD)
28
- const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
29
- if (!dateRegex.test(weekStart)) {
30
- return createErrorResponse(400, 'week_start must be in YYYY-MM-DD format');
31
- }
32
-
33
- // Attempt to insert acknowledgment into dedicated table
34
- try {
35
- await executeQuery(`
36
- INSERT INTO rapport.user_splash_acknowledgments (email_address, week_start, acknowledged_at)
37
- VALUES ($1, $2, NOW())
38
- ON CONFLICT (email_address, week_start) DO NOTHING
39
- `, [email, weekStart]);
40
-
41
- return createSuccessResponse(
42
- { acknowledged: true, week_start: weekStart },
43
- 'Splash acknowledged',
44
- { Request_ID, Timestamp: new Date().toISOString() }
45
- );
46
- } catch (tableErr) {
47
- // If the dedicated table does not exist, fall back to storing
48
- // acknowledgment as metadata in the patterns table
49
- console.log('[SplashAck] Dedicated table not available, using fallback:', tableErr.message);
50
-
51
- try {
52
- await executeQuery(`
53
- INSERT INTO rapport.patterns (
54
- pattern_type,
55
- pattern_key,
56
- pattern_value,
57
- created_by,
58
- created_at,
59
- metadata
60
- )
61
- VALUES (
62
- 'splash_ack',
63
- $1,
64
- $2,
65
- $3,
66
- NOW(),
67
- $4
68
- )
69
- ON CONFLICT (pattern_type, pattern_key) WHERE pattern_type = 'splash_ack'
70
- DO UPDATE SET
71
- pattern_value = EXCLUDED.pattern_value,
72
- created_at = NOW()
73
- `, [
74
- `splash_ack:${email}:${weekStart}`,
75
- weekStart,
76
- email,
77
- JSON.stringify({ type: 'splash_acknowledgment', week_start: weekStart })
78
- ]);
79
-
80
- return createSuccessResponse(
81
- { acknowledged: true, week_start: weekStart, fallback: true },
82
- 'Splash acknowledged (fallback)',
83
- { Request_ID, Timestamp: new Date().toISOString() }
84
- );
85
- } catch (fallbackErr) {
86
- return handleError(fallbackErr);
87
- }
88
- }
89
- }
90
-
91
- exports.handler = wrapHandler(acknowledgeUserSplash);
@@ -1,211 +0,0 @@
1
- /**
2
- * User Splash Get Handler
3
- * Returns weekly activity summary for the splash screen overlay
4
- *
5
- * GET /api/user/splash
6
- * Auth: Cognito JWT required
7
- *
8
- * Returns:
9
- * - show_splash: whether to display the splash (false if already acknowledged this week)
10
- * - summary: weekly activity metrics from sessions + session_standards
11
- * - is_greenfield: true if user has < 7 days of session history
12
- * - tips: getting-started tips for greenfield users
13
- */
14
-
15
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
16
-
17
- async function getUserSplash({ requestContext }) {
18
- const Request_ID = requestContext.requestId;
19
- const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
20
-
21
- if (!email) {
22
- return createErrorResponse(401, 'Authentication required');
23
- }
24
-
25
- // Calculate current week boundaries (Monday to Sunday UTC)
26
- const now = new Date();
27
- const dayOfWeek = now.getUTCDay();
28
- const mondayOffset = dayOfWeek === 0 ? -6 : 1 - dayOfWeek;
29
- const weekStart = new Date(now);
30
- weekStart.setUTCDate(now.getUTCDate() + mondayOffset);
31
- weekStart.setUTCHours(0, 0, 0, 0);
32
-
33
- const weekEnd = new Date(weekStart);
34
- weekEnd.setUTCDate(weekStart.getUTCDate() + 6);
35
- weekEnd.setUTCHours(23, 59, 59, 999);
36
-
37
- const weekStartStr = weekStart.toISOString().split('T')[0];
38
- const weekEndStr = weekEnd.toISOString().split('T')[0];
39
-
40
- // Check if user has already acknowledged the splash this week
41
- let alreadyAcknowledged = false;
42
- try {
43
- const ackResult = await executeQuery(`
44
- SELECT 1
45
- FROM rapport.user_splash_acknowledgments
46
- WHERE email_address = $1
47
- AND week_start = $2
48
- LIMIT 1
49
- `, [email, weekStartStr]);
50
-
51
- alreadyAcknowledged = ackResult.rowCount > 0;
52
- } catch (err) {
53
- // Table may not exist yet; treat as not acknowledged
54
- console.log('[Splash] Acknowledgment table not found, continuing:', err.message);
55
- }
56
-
57
- if (alreadyAcknowledged) {
58
- return createSuccessResponse(
59
- { show_splash: false },
60
- 'Splash already acknowledged this week',
61
- { Request_ID, Timestamp: new Date().toISOString() }
62
- );
63
- }
64
-
65
- // Determine if user is greenfield (< 7 days of session history)
66
- let isGreenfield = true;
67
- try {
68
- const activityCheck = await executeQuery(`
69
- SELECT MIN(started_at) as first_session
70
- FROM rapport.sessions
71
- WHERE email_address = $1
72
- `, [email]);
73
-
74
- if (activityCheck.rowCount > 0 && activityCheck.rows[0].first_session) {
75
- const firstSession = new Date(activityCheck.rows[0].first_session);
76
- const daysSinceFirst = Math.floor((now.getTime() - firstSession.getTime()) / (1000 * 60 * 60 * 24));
77
- isGreenfield = daysSinceFirst < 7;
78
- }
79
- } catch (err) {
80
- console.log('[Splash] Could not determine greenfield status:', err.message);
81
- }
82
-
83
- // Aggregate weekly metrics from sessions + session_standards
84
- const weekStartISO = weekStart.toISOString();
85
- const weekEndISO = weekEnd.toISOString();
86
-
87
- // Session stats (count, duration, projects)
88
- let sessionsCount = 0;
89
- let totalDurationMinutes = 0;
90
- let activeProjects = 0;
91
- try {
92
- const sessionResult = await executeQuery(`
93
- SELECT
94
- COUNT(*) as session_count,
95
- COALESCE(ROUND(SUM(duration_seconds) / 60.0), 0) as total_minutes,
96
- COUNT(DISTINCT project_id) as project_count
97
- FROM rapport.sessions
98
- WHERE email_address = $1
99
- AND started_at >= $2
100
- AND started_at <= $3
101
- `, [email, weekStartISO, weekEndISO]);
102
-
103
- if (sessionResult.rowCount > 0) {
104
- sessionsCount = parseInt(sessionResult.rows[0].session_count, 10) || 0;
105
- totalDurationMinutes = parseInt(sessionResult.rows[0].total_minutes, 10) || 0;
106
- activeProjects = parseInt(sessionResult.rows[0].project_count, 10) || 0;
107
- }
108
- } catch (err) {
109
- console.log('[Splash] Session stats query failed:', err.message);
110
- }
111
-
112
- // Standards stats (injected, followed, violated)
113
- let standardsInjected = 0;
114
- let standardsFollowed = 0;
115
- let violationsDetected = 0;
116
- try {
117
- const standardsResult = await executeQuery(`
118
- SELECT
119
- COUNT(*) as total_injected,
120
- COUNT(*) FILTER (WHERE ss.followed = true) as total_followed,
121
- COUNT(*) FILTER (WHERE ss.violated = true) as total_violated
122
- FROM rapport.session_standards ss
123
- JOIN rapport.sessions s ON s.session_id = ss.session_id
124
- WHERE s.email_address = $1
125
- AND s.started_at >= $2
126
- AND s.started_at <= $3
127
- `, [email, weekStartISO, weekEndISO]);
128
-
129
- if (standardsResult.rowCount > 0) {
130
- standardsInjected = parseInt(standardsResult.rows[0].total_injected, 10) || 0;
131
- standardsFollowed = parseInt(standardsResult.rows[0].total_followed, 10) || 0;
132
- violationsDetected = parseInt(standardsResult.rows[0].total_violated, 10) || 0;
133
- }
134
- } catch (err) {
135
- console.log('[Splash] Standards stats query failed:', err.message);
136
- }
137
-
138
- // Patterns harvested (keep existing query — patterns table exists)
139
- let patternsHarvested = 0;
140
- try {
141
- const patternsResult = await executeQuery(`
142
- SELECT COUNT(*) as count
143
- FROM rapport.patterns
144
- WHERE created_by = $1
145
- AND created_at >= $2
146
- AND created_at <= $3
147
- `, [email, weekStartISO, weekEndISO]);
148
-
149
- patternsHarvested = parseInt(patternsResult.rows[0].count, 10) || 0;
150
- } catch (err) {
151
- console.log('[Splash] Patterns query failed:', err.message);
152
- }
153
-
154
- // Recent category distribution (what the user has been working on)
155
- let recentCategories = {};
156
- try {
157
- const categoryResult = await executeQuery(`
158
- SELECT sp.category, COUNT(*) as usage_count
159
- FROM rapport.session_standards ss
160
- JOIN rapport.sessions s ON s.session_id = ss.session_id
161
- JOIN rapport.standards_patterns sp ON sp.pattern_id = ss.standard_id
162
- WHERE s.email_address = $1
163
- AND s.started_at >= $2
164
- AND s.started_at <= $3
165
- GROUP BY sp.category
166
- ORDER BY usage_count DESC
167
- LIMIT 5
168
- `, [email, weekStartISO, weekEndISO]);
169
-
170
- for (const row of categoryResult.rows) {
171
- recentCategories[row.category] = parseInt(row.usage_count, 10);
172
- }
173
- } catch (err) {
174
- console.log('[Splash] Recent categories query failed:', err.message);
175
- }
176
-
177
- // Build tips for greenfield users
178
- const tips = isGreenfield ? [
179
- 'MindMeld automatically injects relevant standards into your AI coding sessions.',
180
- 'Use /mm-load <domain> to pre-load standards before starting work (e.g., /mm-load Frontend).',
181
- 'Use /mm-workflows to see available step-by-step procedures.',
182
- 'Patterns discovered in your code can be promoted to team-wide standards.',
183
- 'Use /mm-status to check your system health and API connectivity.'
184
- ] : [];
185
-
186
- const summary = {
187
- sessions_count: sessionsCount,
188
- total_duration_minutes: totalDurationMinutes,
189
- standards_injected: standardsInjected,
190
- standards_followed: standardsFollowed,
191
- violations_detected: violationsDetected,
192
- active_projects: activeProjects,
193
- recent_categories: recentCategories,
194
- patterns_harvested: patternsHarvested,
195
- week_start: weekStartStr,
196
- week_end: weekEndStr
197
- };
198
-
199
- return createSuccessResponse(
200
- {
201
- show_splash: true,
202
- summary,
203
- is_greenfield: isGreenfield,
204
- tips
205
- },
206
- 'Splash data retrieved',
207
- { Request_ID, Timestamp: new Date().toISOString() }
208
- );
209
- }
210
-
211
- exports.handler = wrapHandler(getUserSplash);
@@ -1,186 +0,0 @@
1
- /**
2
- * Cognito Post-Confirmation Handler
3
- * Creates user record and personal workspace after email verification
4
- *
5
- * Triggered by: Cognito User Pool post-confirmation trigger
6
- * Following: cognito_authentication_standards.md
7
- */
8
-
9
- const { executeQuery } = require('./helpers');
10
-
11
- /**
12
- * Handle post-confirmation trigger
13
- * Creates user, personal client, personal company, and entitlement
14
- */
15
- async function handler(event, context) {
16
- console.log('Post-confirmation trigger:', JSON.stringify(event, null, 2));
17
-
18
- // Only process SignUp confirmations
19
- if (event.triggerSource !== 'PostConfirmation_ConfirmSignUp') {
20
- console.log('Skipping trigger source:', event.triggerSource);
21
- return event;
22
- }
23
-
24
- const { userName, request } = event;
25
- const { userAttributes } = request;
26
-
27
- const email = userAttributes.email;
28
- const cognitoSub = userAttributes.sub;
29
- const firstName = userAttributes.given_name || '';
30
- const lastName = userAttributes.family_name || '';
31
-
32
- try {
33
- await executeQuery('BEGIN');
34
-
35
- try {
36
- // 1. Create user record
37
- const userQuery = `
38
- INSERT INTO rapport.users (
39
- email_address,
40
- cognito_sub,
41
- first_name,
42
- last_name,
43
- client_id,
44
- user_status,
45
- active
46
- )
47
- VALUES ($1, $2, $3, $4, $5, 'Active', true)
48
- ON CONFLICT (email_address) DO UPDATE SET
49
- cognito_sub = EXCLUDED.cognito_sub,
50
- first_name = EXCLUDED.first_name,
51
- last_name = EXCLUDED.last_name,
52
- last_updated = CURRENT_TIMESTAMP
53
- RETURNING email_address
54
- `;
55
-
56
- // Personal client ID based on email (sanitized)
57
- const personalClientId = `personal_${email.replace(/[^a-zA-Z0-9]/g, '_').toLowerCase()}`;
58
-
59
- await executeQuery(userQuery, [
60
- email,
61
- cognitoSub,
62
- firstName,
63
- lastName,
64
- personalClientId
65
- ]);
66
-
67
- console.log('User created/updated:', email);
68
-
69
- // 2. Create personal client (free tier)
70
- const clientQuery = `
71
- INSERT INTO rapport.clients (
72
- client_id,
73
- client_name,
74
- client_type,
75
- client_status,
76
- subscription_tier,
77
- subscription_status
78
- )
79
- VALUES ($1, $2, 'PERSONAL', 'Active', 'free', 'active')
80
- ON CONFLICT (client_id) DO NOTHING
81
- RETURNING client_id
82
- `;
83
-
84
- const clientName = firstName && lastName
85
- ? `${firstName} ${lastName}'s Workspace`
86
- : `${email.split('@')[0]}'s Workspace`;
87
-
88
- await executeQuery(clientQuery, [personalClientId, clientName]);
89
-
90
- console.log('Personal client created:', personalClientId);
91
-
92
- // 3. Create personal company
93
- const companyId = `${personalClientId}_main`;
94
- const companyQuery = `
95
- INSERT INTO rapport.companies (
96
- company_id,
97
- client_id,
98
- company_name,
99
- company_status
100
- )
101
- VALUES ($1, $2, $3, 'Active')
102
- ON CONFLICT (company_id) DO NOTHING
103
- RETURNING company_id
104
- `;
105
-
106
- await executeQuery(companyQuery, [
107
- companyId,
108
- personalClientId,
109
- 'Personal Projects'
110
- ]);
111
-
112
- console.log('Personal company created:', companyId);
113
-
114
- // 4. Create admin entitlement for personal workspace
115
- const entitlementQuery = `
116
- INSERT INTO rapport.user_entitlements (
117
- email_address,
118
- client_id,
119
- company_id,
120
- admin,
121
- member
122
- )
123
- VALUES ($1, $2, $3, true, true)
124
- ON CONFLICT (email_address, company_id) DO NOTHING
125
- `;
126
-
127
- await executeQuery(entitlementQuery, [email, personalClientId, companyId]);
128
-
129
- console.log('Entitlement created for:', email);
130
-
131
- // 5. Check for and accept pending enterprise invitations
132
- const pendingInvites = await executeQuery(`
133
- SELECT invitation_id, client_id, company_id, role
134
- FROM rapport.enterprise_invitations
135
- WHERE email = $1 AND status = 'pending'
136
- `, [email.toLowerCase()]);
137
-
138
- if (pendingInvites.rows.length > 0) {
139
- console.log(`Found ${pendingInvites.rows.length} pending enterprise invitation(s)`);
140
-
141
- for (const invite of pendingInvites.rows) {
142
- // Create entitlement for the enterprise company
143
- const isAdmin = invite.role === 'admin';
144
- await executeQuery(`
145
- INSERT INTO rapport.user_entitlements (
146
- email_address,
147
- client_id,
148
- company_id,
149
- admin,
150
- member
151
- )
152
- VALUES ($1, $2, $3, $4, true)
153
- ON CONFLICT (email_address, company_id) DO NOTHING
154
- `, [email, invite.client_id, invite.company_id, isAdmin]);
155
-
156
- // Mark invitation as accepted
157
- await executeQuery(`
158
- UPDATE rapport.enterprise_invitations
159
- SET status = 'accepted', accepted_at = NOW()
160
- WHERE invitation_id = $1
161
- `, [invite.invitation_id]);
162
-
163
- console.log(`Accepted enterprise invitation for company: ${invite.company_id} (role: ${invite.role})`);
164
- }
165
- }
166
-
167
- await executeQuery('COMMIT');
168
-
169
- console.log('Post-confirmation complete for:', email);
170
-
171
- } catch (error) {
172
- await executeQuery('ROLLBACK');
173
- throw error;
174
- }
175
-
176
- } catch (error) {
177
- console.error('Post-confirmation error:', error);
178
- // Don't throw - this would prevent user creation in Cognito
179
- // Log the error and continue
180
- }
181
-
182
- // Must return the event for Cognito to proceed
183
- return event;
184
- }
185
-
186
- module.exports = { handler };