@equilateral_ai/mindmeld 3.5.2 → 4.0.1

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 (140) hide show
  1. package/hooks/session-end.js +25 -0
  2. package/hooks/session-start.js +363 -83
  3. package/hooks/session-watcher.js +585 -0
  4. package/package.json +19 -13
  5. package/scripts/init-project.js +9 -23
  6. package/src/client/dbShim.js +16 -0
  7. package/src/core/AuthManager.js +3 -2
  8. package/src/handlers/helpers/dbOperations.js +9 -46
  9. package/src/index.js +2 -217
  10. package/src/utils/piiMask.js +16 -0
  11. package/scripts/harvest.js +0 -601
  12. package/scripts/inject.js +0 -409
  13. package/scripts/mcp-bridge.js +0 -220
  14. package/scripts/repo-analyzer.js +0 -870
  15. package/src/collaboration/CollaborationPrompt.js +0 -460
  16. package/src/core/AlertEngine.js +0 -813
  17. package/src/core/AlertNotifier.js +0 -363
  18. package/src/core/CorrelationAnalyzer.js +0 -931
  19. package/src/core/CrossReferenceEngine.js +0 -624
  20. package/src/core/CurationEngine.js +0 -688
  21. package/src/core/DeprecationScheduler.js +0 -183
  22. package/src/core/LoadBearingDetector.js +0 -242
  23. package/src/core/NotificationService.js +0 -1032
  24. package/src/core/RapportOrchestrator.js +0 -632
  25. package/src/core/RelevanceDetector.js +0 -694
  26. package/src/core/StandardLifecycle.js +0 -244
  27. package/src/core/StandardsIngestion.js +0 -991
  28. package/src/core/TeamLoadBearingDetector.js +0 -431
  29. package/src/core/parsers/adrParser.js +0 -479
  30. package/src/core/parsers/cursorRulesParser.js +0 -564
  31. package/src/core/parsers/eslintParser.js +0 -439
  32. package/src/database/dbOperations.js +0 -105
  33. package/src/handlers/activity/activityGetMe.js +0 -98
  34. package/src/handlers/activity/activityGetTeam.js +0 -175
  35. package/src/handlers/admin/adminSetup.js +0 -216
  36. package/src/handlers/alerts/alertsAcknowledge.js +0 -92
  37. package/src/handlers/alerts/alertsGet.js +0 -250
  38. package/src/handlers/analytics/activitySummaryGet.js +0 -234
  39. package/src/handlers/analytics/coachingGet.js +0 -361
  40. package/src/handlers/analytics/convergenceGet.js +0 -236
  41. package/src/handlers/analytics/developerScoreGet.js +0 -137
  42. package/src/handlers/collaborators/collaboratorAdd.js +0 -200
  43. package/src/handlers/collaborators/collaboratorInvite.js +0 -219
  44. package/src/handlers/collaborators/collaboratorList.js +0 -82
  45. package/src/handlers/collaborators/collaboratorRemove.js +0 -128
  46. package/src/handlers/collaborators/inviteAccept.js +0 -122
  47. package/src/handlers/company/companyUsersDelete.js +0 -141
  48. package/src/handlers/company/companyUsersGet.js +0 -90
  49. package/src/handlers/company/companyUsersPost.js +0 -267
  50. package/src/handlers/company/companyUsersPut.js +0 -76
  51. package/src/handlers/context/contextGet.js +0 -57
  52. package/src/handlers/context/invariantsGet.js +0 -74
  53. package/src/handlers/context/loopsGet.js +0 -82
  54. package/src/handlers/context/notesCreate.js +0 -74
  55. package/src/handlers/context/purposeGet.js +0 -78
  56. package/src/handlers/correlations/correlationsDeveloperGet.js +0 -227
  57. package/src/handlers/correlations/correlationsGet.js +0 -93
  58. package/src/handlers/correlations/correlationsProjectGet.js +0 -153
  59. package/src/handlers/enterprise/controlTowerGet.js +0 -224
  60. package/src/handlers/enterprise/enterpriseAuditGet.js +0 -108
  61. package/src/handlers/enterprise/enterpriseContributorsGet.js +0 -85
  62. package/src/handlers/enterprise/enterpriseKnowledgeCategoriesGet.js +0 -53
  63. package/src/handlers/enterprise/enterpriseKnowledgeCreate.js +0 -77
  64. package/src/handlers/enterprise/enterpriseKnowledgeDelete.js +0 -71
  65. package/src/handlers/enterprise/enterpriseKnowledgeGet.js +0 -87
  66. package/src/handlers/enterprise/enterpriseKnowledgeUpdate.js +0 -122
  67. package/src/handlers/enterprise/enterpriseOnboardingComplete.js +0 -77
  68. package/src/handlers/enterprise/enterpriseOnboardingInvite.js +0 -138
  69. package/src/handlers/enterprise/enterpriseOnboardingSetup.js +0 -128
  70. package/src/handlers/enterprise/enterpriseOnboardingStatus.js +0 -88
  71. package/src/handlers/github/githubConnectionStatus.js +0 -49
  72. package/src/handlers/github/githubDiscoverPatterns.js +0 -621
  73. package/src/handlers/github/githubOAuthCallback.js +0 -178
  74. package/src/handlers/github/githubOAuthStart.js +0 -59
  75. package/src/handlers/github/githubPatternsReview.js +0 -76
  76. package/src/handlers/github/githubReposList.js +0 -105
  77. package/src/handlers/health/healthGet.js +0 -55
  78. package/src/handlers/helpers/auditLogger.js +0 -201
  79. package/src/handlers/helpers/checkSuperAdmin.js +0 -84
  80. package/src/handlers/helpers/decisionFrames.js +0 -29
  81. package/src/handlers/helpers/errorHandler.js +0 -49
  82. package/src/handlers/helpers/index.js +0 -138
  83. package/src/handlers/helpers/lambdaWrapper.js +0 -60
  84. package/src/handlers/helpers/mindmeldMcpCore.js +0 -1103
  85. package/src/handlers/helpers/predictiveCache.js +0 -51
  86. package/src/handlers/helpers/projectAccess.js +0 -88
  87. package/src/handlers/helpers/responseUtil.js +0 -55
  88. package/src/handlers/helpers/subscriptionTiers.js +0 -1168
  89. package/src/handlers/mcp/mcpHandler.js +0 -569
  90. package/src/handlers/mcp/mindmeldMcpHandler.js +0 -124
  91. package/src/handlers/mcp/mindmeldMcpStreamHandler.js +0 -342
  92. package/src/handlers/notifications/getPreferences.js +0 -84
  93. package/src/handlers/notifications/sendNotification.js +0 -170
  94. package/src/handlers/notifications/updatePreferences.js +0 -316
  95. package/src/handlers/patterns/patternEvaluatePromotionPost.js +0 -173
  96. package/src/handlers/patterns/patternUsagePost.js +0 -182
  97. package/src/handlers/patterns/patternViolationPost.js +0 -185
  98. package/src/handlers/projects/projectCreate.js +0 -248
  99. package/src/handlers/projects/projectDelete.js +0 -82
  100. package/src/handlers/projects/projectGet.js +0 -95
  101. package/src/handlers/projects/projectUpdate.js +0 -117
  102. package/src/handlers/reports/aiLeverage.js +0 -210
  103. package/src/handlers/reports/engineeringInvestment.js +0 -132
  104. package/src/handlers/reports/riskForecast.js +0 -206
  105. package/src/handlers/reports/standardsRoi.js +0 -254
  106. package/src/handlers/scheduled/analyzeCorrelations.js +0 -178
  107. package/src/handlers/scheduled/analyzeGitHistory.js +0 -510
  108. package/src/handlers/scheduled/generateAlerts.js +0 -135
  109. package/src/handlers/scheduled/maturityUpdateJob.js +0 -166
  110. package/src/handlers/scheduled/refreshActivity.js +0 -21
  111. package/src/handlers/scheduled/scanCompliance.js +0 -334
  112. package/src/handlers/sessions/sessionEndPost.js +0 -180
  113. package/src/handlers/sessions/sessionStandardsPost.js +0 -171
  114. package/src/handlers/standards/catalogGet.js +0 -185
  115. package/src/handlers/standards/catalogSync.js +0 -120
  116. package/src/handlers/standards/discoveriesGet.js +0 -89
  117. package/src/handlers/standards/projectStandardsGet.js +0 -129
  118. package/src/handlers/standards/projectStandardsPut.js +0 -151
  119. package/src/handlers/standards/standardsAuditGet.js +0 -65
  120. package/src/handlers/standards/standardsParseUpload.js +0 -149
  121. package/src/handlers/standards/standardsRelevantPost.js +0 -405
  122. package/src/handlers/standards/standardsTransition.js +0 -161
  123. package/src/handlers/stripe/addonManagePost.js +0 -240
  124. package/src/handlers/stripe/billingPortalPost.js +0 -93
  125. package/src/handlers/stripe/enterpriseCheckoutPost.js +0 -272
  126. package/src/handlers/stripe/seatsUpdatePost.js +0 -185
  127. package/src/handlers/stripe/subscriptionCancelDelete.js +0 -169
  128. package/src/handlers/stripe/subscriptionCreatePost.js +0 -221
  129. package/src/handlers/stripe/subscriptionUpdatePut.js +0 -163
  130. package/src/handlers/stripe/webhookPost.js +0 -482
  131. package/src/handlers/user/apiTokenCreate.js +0 -71
  132. package/src/handlers/user/apiTokenList.js +0 -64
  133. package/src/handlers/user/userSplashAck.js +0 -91
  134. package/src/handlers/user/userSplashGet.js +0 -211
  135. package/src/handlers/users/cognitoPostConfirmation.js +0 -186
  136. package/src/handlers/users/cognitoPreSignUp.js +0 -114
  137. package/src/handlers/users/userEntitlementsGet.js +0 -89
  138. package/src/handlers/users/userGet.js +0 -118
  139. package/src/handlers/users/userProfilePut.js +0 -77
  140. package/src/handlers/webhooks/githubWebhook.js +0 -215
@@ -1,78 +0,0 @@
1
- /**
2
- * Purpose Get Handler
3
- * Retrieves relationship purpose for a scope
4
- *
5
- * GET /api/context/purpose?scope=jarvis
6
- */
7
-
8
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError, checkSuperAdmin } = require('./helpers');
9
-
10
- /**
11
- * Get relationship purpose
12
- */
13
- async function getPurpose({ queryStringParameters: queryParams = {}, requestContext }) {
14
- try {
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, 'Unauthorized');
20
- }
21
-
22
- // Gate to super admins only (internal/beta endpoint)
23
- await checkSuperAdmin.requireSuperAdmin(email);
24
-
25
- const scope = queryParams.scope || 'jarvis';
26
-
27
- const query = `
28
- SELECT
29
- purpose_text,
30
- confidence,
31
- confirmed,
32
- maturity,
33
- evolution,
34
- created_at,
35
- updated_at
36
- FROM rapport.relationship_purpose
37
- WHERE email_address = $1
38
- AND scope = $2
39
- `;
40
- const result = await executeQuery(query, [email, scope]);
41
-
42
- if (result.rowCount === 0) {
43
- return createSuccessResponse(
44
- {
45
- scope,
46
- purpose: null,
47
- message: 'No purpose defined for this scope'
48
- },
49
- 'No purpose found',
50
- { Request_ID, Timestamp: new Date().toISOString() }
51
- );
52
- }
53
-
54
- const row = result.rows[0];
55
-
56
- return createSuccessResponse(
57
- {
58
- scope,
59
- purpose: row.purpose_text,
60
- confidence: parseFloat(row.confidence),
61
- confirmed: row.confirmed,
62
- maturity: row.maturity,
63
- evolution: row.evolution || []
64
- },
65
- row.purpose_text,
66
- {
67
- Request_ID,
68
- Timestamp: new Date().toISOString()
69
- }
70
- );
71
-
72
- } catch (error) {
73
- console.error('Handler Error:', error);
74
- return handleError(error);
75
- }
76
- }
77
-
78
- exports.handler = wrapHandler(getPurpose);
@@ -1,227 +0,0 @@
1
- /**
2
- * Get Developer Correlations Handler
3
- * Returns session-to-commit correlation data for a specific developer
4
- *
5
- * GET /api/correlations/developer?email=xxx
6
- * Query params:
7
- * - email (required)
8
- * - lookbackDays (optional, default: 30)
9
- *
10
- * Returns:
11
- * - Developer productivity metrics
12
- * - Session history with commit correlation
13
- * - Trend analysis
14
- */
15
-
16
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse } = require('./helpers');
17
- const { CorrelationAnalyzer } = require('./core/CorrelationAnalyzer');
18
-
19
- exports.handler = wrapHandler(async (event, context) => {
20
- // Extract user email from Cognito claims
21
- const requestingEmail = event.requestContext?.authorizer?.claims?.email;
22
- if (!requestingEmail) {
23
- return createErrorResponse(401, 'Unauthorized - no email in claims');
24
- }
25
-
26
- // Get target developer email from query parameters
27
- const targetEmail = decodeURIComponent(event.queryStringParameters?.email || '');
28
- if (!targetEmail) {
29
- return createErrorResponse(400, 'Developer email is required');
30
- }
31
-
32
- // Parse query parameters
33
- const queryParams = event.queryStringParameters || {};
34
- const lookbackDays = parseInt(queryParams.lookbackDays) || 30;
35
-
36
- // Check if requesting own data or if admin
37
- const isSelf = requestingEmail.toLowerCase() === targetEmail.toLowerCase();
38
-
39
- if (!isSelf) {
40
- // Verify requesting user is admin in a shared company
41
- const accessResult = await executeQuery(`
42
- SELECT DISTINCT ue1.company_id
43
- FROM rapport.user_entitlements ue1
44
- JOIN rapport.user_entitlements ue2 ON ue1.company_id = ue2.company_id
45
- WHERE ue1.email_address = $1
46
- AND ue2.email_address = $2
47
- AND (ue1.admin = true OR ue1.manager = true)
48
- `, [requestingEmail, targetEmail]);
49
-
50
- if (accessResult.rows.length === 0) {
51
- return createErrorResponse(403, 'Access denied - admin privileges required to view other developers');
52
- }
53
- }
54
-
55
- // Get developer info
56
- const developerResult = await executeQuery(`
57
- SELECT
58
- u.email_address as email,
59
- CONCAT(u.first_name, ' ', u.last_name) as display_name,
60
- u.first_name,
61
- u.last_name
62
- FROM rapport.users u
63
- WHERE u.email_address = $1
64
- `, [targetEmail]);
65
-
66
- if (developerResult.rows.length === 0) {
67
- return createErrorResponse(404, 'Developer not found');
68
- }
69
-
70
- const developer = developerResult.rows[0];
71
-
72
- // Initialize analyzer
73
- const analyzer = new CorrelationAnalyzer();
74
-
75
- // Get developer productivity metrics
76
- const productivity = await analyzer.getDeveloperProductivity(targetEmail, lookbackDays);
77
-
78
- // Get session history
79
- const sessionHistory = await getSessionHistory(targetEmail, lookbackDays, 20);
80
-
81
- // Get weekly trend
82
- const weeklyTrend = await getWeeklyTrend(targetEmail, lookbackDays);
83
-
84
- // Get pattern usage
85
- const patternUsage = await getPatternUsage(targetEmail, lookbackDays);
86
-
87
- return createSuccessResponse({
88
- developer: {
89
- email: developer.email,
90
- displayName: developer.display_name,
91
- firstName: developer.first_name,
92
- lastName: developer.last_name
93
- },
94
- productivity,
95
- sessionHistory,
96
- weeklyTrend,
97
- patternUsage
98
- });
99
- });
100
-
101
- /**
102
- * Get session history for a developer
103
- */
104
- async function getSessionHistory(email, lookbackDays, limit) {
105
- const query = `
106
- SELECT
107
- sc.session_id,
108
- sc.project_id,
109
- p.project_name,
110
- sc.session_started_at,
111
- sc.session_ended_at,
112
- sc.session_duration_seconds,
113
- sc.has_commits,
114
- sc.commit_count,
115
- sc.total_insertions,
116
- sc.total_deletions,
117
- sc.total_files_changed,
118
- sc.avg_commit_latency_minutes,
119
- sc.patterns_used,
120
- sc.correlation_type,
121
- sc.correlation_score
122
- FROM rapport.session_correlations sc
123
- LEFT JOIN rapport.projects p ON sc.project_id = p.project_id
124
- WHERE sc.email_address = $1
125
- AND sc.session_started_at > NOW() - $2 * INTERVAL '1 day'
126
- ORDER BY sc.session_started_at DESC
127
- LIMIT $3
128
- `;
129
-
130
- const result = await executeQuery(query, [email, lookbackDays, limit]);
131
-
132
- return result.rows.map(row => ({
133
- sessionId: row.session_id,
134
- projectId: row.project_id,
135
- projectName: row.project_name,
136
- startedAt: row.session_started_at,
137
- endedAt: row.session_ended_at,
138
- durationMinutes: Math.round((row.session_duration_seconds || 0) / 60),
139
- hasCommits: row.has_commits,
140
- commitCount: row.commit_count,
141
- insertions: row.total_insertions,
142
- deletions: row.total_deletions,
143
- filesChanged: row.total_files_changed,
144
- commitLatencyMinutes: row.avg_commit_latency_minutes,
145
- patternsUsed: row.patterns_used,
146
- correlationType: row.correlation_type,
147
- correlationScore: parseFloat(row.correlation_score) || 0
148
- }));
149
- }
150
-
151
- /**
152
- * Get weekly productivity trend for a developer
153
- */
154
- async function getWeeklyTrend(email, lookbackDays) {
155
- const query = `
156
- SELECT
157
- DATE_TRUNC('week', sc.session_started_at) as week,
158
- COUNT(*) as total_sessions,
159
- COUNT(*) FILTER (WHERE sc.has_commits = true) as productive_sessions,
160
- ROUND(
161
- COUNT(*) FILTER (WHERE sc.has_commits = true)::decimal /
162
- NULLIF(COUNT(*), 0) * 100,
163
- 1
164
- ) as conversion_rate,
165
- SUM(sc.commit_count) as total_commits,
166
- SUM(sc.total_insertions) as total_insertions,
167
- SUM(sc.total_deletions) as total_deletions,
168
- SUM(sc.session_duration_seconds) / 3600.0 as session_hours
169
- FROM rapport.session_correlations sc
170
- WHERE sc.email_address = $1
171
- AND sc.session_started_at > NOW() - $2 * INTERVAL '1 day'
172
- GROUP BY DATE_TRUNC('week', sc.session_started_at)
173
- ORDER BY week DESC
174
- `;
175
-
176
- const result = await executeQuery(query, [email, lookbackDays]);
177
-
178
- return result.rows.map(row => ({
179
- week: row.week,
180
- totalSessions: parseInt(row.total_sessions) || 0,
181
- productiveSessions: parseInt(row.productive_sessions) || 0,
182
- conversionRate: parseFloat(row.conversion_rate) || 0,
183
- totalCommits: parseInt(row.total_commits) || 0,
184
- totalInsertions: parseInt(row.total_insertions) || 0,
185
- totalDeletions: parseInt(row.total_deletions) || 0,
186
- sessionHours: parseFloat(row.session_hours) || 0
187
- }));
188
- }
189
-
190
- /**
191
- * Get pattern usage statistics for a developer
192
- */
193
- async function getPatternUsage(email, lookbackDays) {
194
- const query = `
195
- SELECT
196
- p.pattern_id,
197
- p.intent,
198
- p.maturity,
199
- COUNT(DISTINCT pu.session_id) as sessions_used,
200
- COUNT(DISTINCT sc.session_id) FILTER (WHERE sc.has_commits = true) as sessions_with_commits,
201
- ROUND(
202
- COUNT(DISTINCT sc.session_id) FILTER (WHERE sc.has_commits = true)::decimal /
203
- NULLIF(COUNT(DISTINCT pu.session_id), 0) * 100,
204
- 1
205
- ) as pattern_conversion_rate
206
- FROM rapport.pattern_usage pu
207
- JOIN rapport.patterns p ON pu.pattern_id = p.pattern_id
208
- LEFT JOIN rapport.session_correlations sc ON pu.session_id = sc.session_id
209
- WHERE pu.email_address = $1
210
- AND pu.used_at > NOW() - $2 * INTERVAL '1 day'
211
- GROUP BY p.pattern_id, p.intent, p.maturity
212
- HAVING COUNT(DISTINCT pu.session_id) >= 2
213
- ORDER BY sessions_used DESC
214
- LIMIT 10
215
- `;
216
-
217
- const result = await executeQuery(query, [email, lookbackDays]);
218
-
219
- return result.rows.map(row => ({
220
- patternId: row.pattern_id,
221
- intent: row.intent,
222
- maturity: row.maturity,
223
- sessionsUsed: parseInt(row.sessions_used) || 0,
224
- sessionsWithCommits: parseInt(row.sessions_with_commits) || 0,
225
- patternConversionRate: parseFloat(row.pattern_conversion_rate) || 0
226
- }));
227
- }
@@ -1,93 +0,0 @@
1
- /**
2
- * Get Correlations Handler
3
- * Returns session-to-commit correlation summary for a company
4
- *
5
- * GET /api/correlations
6
- * Query params:
7
- * - lookbackDays (optional, default: 30)
8
- *
9
- * Returns:
10
- * - Overall correlation summary
11
- * - Conversion rate metrics
12
- * - Correlation type distribution
13
- */
14
-
15
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse } = require('./helpers');
16
- const { CorrelationAnalyzer } = require('./core/CorrelationAnalyzer');
17
-
18
- exports.handler = wrapHandler(async (event, context) => {
19
- // Extract user email from Cognito claims
20
- const email = event.requestContext?.authorizer?.claims?.email;
21
- if (!email) {
22
- return createErrorResponse(401, 'Unauthorized - no email in claims');
23
- }
24
-
25
- // Parse query parameters
26
- const queryParams = event.queryStringParameters || {};
27
- const lookbackDays = parseInt(queryParams.lookbackDays) || 30;
28
-
29
- // Get user's company
30
- const companyResult = await executeQuery(`
31
- SELECT ue.company_id
32
- FROM rapport.user_entitlements ue
33
- WHERE ue.email_address = $1
34
- LIMIT 1
35
- `, [email]);
36
-
37
- if (companyResult.rows.length === 0) {
38
- return createErrorResponse(403, 'User not associated with any company');
39
- }
40
-
41
- const companyId = companyResult.rows[0].company_id;
42
-
43
- // Initialize analyzer
44
- const analyzer = new CorrelationAnalyzer();
45
-
46
- // Get correlation summary
47
- let summary = null;
48
- let patternEffectiveness = [];
49
- let strugglingDevelopers = [];
50
-
51
- try {
52
- summary = await analyzer.getCorrelationSummary(companyId, lookbackDays);
53
- } catch (err) {
54
- console.error('Error getting correlation summary:', err.message);
55
- summary = { error: err.message };
56
- }
57
-
58
- try {
59
- patternEffectiveness = await analyzer.getPatternEffectiveness(null, lookbackDays);
60
- } catch (err) {
61
- console.error('Error getting pattern effectiveness:', err.message);
62
- }
63
-
64
- // Get struggling developers (for admins only)
65
- try {
66
- const isAdmin = await checkIsAdmin(email, companyId);
67
- if (isAdmin) {
68
- strugglingDevelopers = await analyzer.identifyStrugglingDevelopers(companyId, lookbackDays);
69
- }
70
- } catch (err) {
71
- console.error('Error getting struggling developers:', err.message);
72
- }
73
-
74
- return createSuccessResponse({
75
- summary,
76
- patternEffectiveness,
77
- strugglingDevelopers
78
- });
79
- });
80
-
81
- /**
82
- * Check if user is admin for the company
83
- */
84
- async function checkIsAdmin(email, companyId) {
85
- const result = await executeQuery(`
86
- SELECT admin, manager
87
- FROM rapport.user_entitlements
88
- WHERE email_address = $1 AND company_id = $2
89
- `, [email, companyId]);
90
-
91
- if (result.rows.length === 0) return false;
92
- return result.rows[0].admin || result.rows[0].manager;
93
- }
@@ -1,153 +0,0 @@
1
- /**
2
- * Get Project Correlations Handler
3
- * Returns session-to-commit correlation data for a specific project
4
- *
5
- * GET /api/correlations/project?project_id=xxx
6
- * Query params:
7
- * - project_id (required)
8
- * - lookbackDays (optional, default: 30)
9
- *
10
- * Returns:
11
- * - Project productivity metrics
12
- * - Developer breakdown
13
- * - Pattern effectiveness for project
14
- */
15
-
16
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, verifyProjectAccess } = require('./helpers');
17
- const { CorrelationAnalyzer } = require('./core/CorrelationAnalyzer');
18
-
19
- exports.handler = wrapHandler(async (event, context) => {
20
- // Extract user email from Cognito claims
21
- const email = event.requestContext?.authorizer?.claims?.email;
22
- if (!email) {
23
- return createErrorResponse(401, 'Unauthorized - no email in claims');
24
- }
25
-
26
- // Get project ID from query parameters
27
- const projectId = event.queryStringParameters?.project_id;
28
- if (!projectId) {
29
- return createErrorResponse(400, 'Project ID is required');
30
- }
31
-
32
- // Parse query parameters
33
- const queryParams = event.queryStringParameters || {};
34
- const lookbackDays = parseInt(queryParams.lookbackDays) || 30;
35
-
36
- // Verify user has access to this project (collaborator or company member)
37
- const project = await verifyProjectAccess(projectId, email);
38
- if (!project) {
39
- return createErrorResponse(403, 'Access denied to this project');
40
- }
41
-
42
- // Initialize analyzer
43
- const analyzer = new CorrelationAnalyzer();
44
-
45
- // Get project productivity metrics
46
- const projectMetrics = await analyzer.getProjectProductivity(projectId, lookbackDays);
47
-
48
- // Get developer breakdown for the project
49
- const developerBreakdown = await getDeveloperBreakdown(projectId, lookbackDays);
50
-
51
- // Get pattern effectiveness for this project
52
- const patternEffectiveness = await analyzer.getPatternEffectiveness(projectId, lookbackDays);
53
-
54
- // Get recent correlations
55
- const recentCorrelations = await getRecentCorrelations(projectId, 10);
56
-
57
- return createSuccessResponse({
58
- project: {
59
- projectId: project.project_id,
60
- projectName: project.project_name,
61
- companyId: project.company_id
62
- },
63
- metrics: projectMetrics,
64
- developerBreakdown,
65
- patternEffectiveness,
66
- recentCorrelations
67
- });
68
- });
69
-
70
- /**
71
- * Get developer breakdown for a project
72
- */
73
- async function getDeveloperBreakdown(projectId, lookbackDays) {
74
- const query = `
75
- SELECT
76
- sc.email_address,
77
- CONCAT(u.first_name, ' ', u.last_name) as display_name,
78
- COUNT(*) as total_sessions,
79
- COUNT(*) FILTER (WHERE sc.has_commits = true) as productive_sessions,
80
- ROUND(
81
- COUNT(*) FILTER (WHERE sc.has_commits = true)::decimal /
82
- NULLIF(COUNT(*), 0) * 100,
83
- 1
84
- ) as conversion_rate,
85
- SUM(sc.commit_count) as total_commits,
86
- SUM(sc.total_insertions) as total_insertions,
87
- SUM(sc.total_deletions) as total_deletions,
88
- ROUND(AVG(sc.session_duration_seconds) / 60, 0) as avg_session_minutes,
89
- MAX(sc.session_started_at) as last_session
90
- FROM rapport.session_correlations sc
91
- JOIN rapport.users u ON sc.email_address = u.email_address
92
- WHERE sc.project_id = $1
93
- AND sc.session_started_at > NOW() - $2 * INTERVAL '1 day'
94
- GROUP BY sc.email_address, u.first_name, u.last_name
95
- ORDER BY total_commits DESC NULLS LAST
96
- `;
97
-
98
- const result = await executeQuery(query, [projectId, lookbackDays]);
99
-
100
- return result.rows.map(row => ({
101
- email: row.email_address,
102
- displayName: row.display_name,
103
- totalSessions: parseInt(row.total_sessions) || 0,
104
- productiveSessions: parseInt(row.productive_sessions) || 0,
105
- conversionRate: parseFloat(row.conversion_rate) || 0,
106
- totalCommits: parseInt(row.total_commits) || 0,
107
- totalInsertions: parseInt(row.total_insertions) || 0,
108
- totalDeletions: parseInt(row.total_deletions) || 0,
109
- avgSessionMinutes: parseInt(row.avg_session_minutes) || 0,
110
- lastSession: row.last_session
111
- }));
112
- }
113
-
114
- /**
115
- * Get recent correlations for a project
116
- */
117
- async function getRecentCorrelations(projectId, limit) {
118
- const query = `
119
- SELECT
120
- sc.session_id,
121
- sc.email_address,
122
- CONCAT(u.first_name, ' ', u.last_name) as display_name,
123
- sc.session_started_at,
124
- sc.session_duration_seconds,
125
- sc.has_commits,
126
- sc.commit_count,
127
- sc.total_insertions,
128
- sc.total_deletions,
129
- sc.correlation_type,
130
- sc.correlation_score
131
- FROM rapport.session_correlations sc
132
- JOIN rapport.users u ON sc.email_address = u.email_address
133
- WHERE sc.project_id = $1
134
- ORDER BY sc.session_started_at DESC
135
- LIMIT $2
136
- `;
137
-
138
- const result = await executeQuery(query, [projectId, limit]);
139
-
140
- return result.rows.map(row => ({
141
- sessionId: row.session_id,
142
- email: row.email_address,
143
- displayName: row.display_name,
144
- sessionStarted: row.session_started_at,
145
- sessionDurationMinutes: Math.round((row.session_duration_seconds || 0) / 60),
146
- hasCommits: row.has_commits,
147
- commitCount: row.commit_count,
148
- totalInsertions: row.total_insertions,
149
- totalDeletions: row.total_deletions,
150
- correlationType: row.correlation_type,
151
- correlationScore: parseFloat(row.correlation_score) || 0
152
- }));
153
- }