@equilateral_ai/mindmeld 3.5.3 → 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 (139) hide show
  1. package/hooks/session-start.js +312 -85
  2. package/package.json +20 -14
  3. package/scripts/init-project.js +9 -23
  4. package/src/client/dbShim.js +16 -0
  5. package/src/core/AuthManager.js +3 -2
  6. package/src/handlers/helpers/dbOperations.js +9 -46
  7. package/src/index.js +2 -217
  8. package/src/utils/piiMask.js +16 -0
  9. package/scripts/harvest.js +0 -601
  10. package/scripts/inject.js +0 -409
  11. package/scripts/mcp-bridge.js +0 -220
  12. package/scripts/repo-analyzer.js +0 -870
  13. package/scripts/standards.js +0 -285
  14. package/src/collaboration/CollaborationPrompt.js +0 -460
  15. package/src/core/AlertEngine.js +0 -813
  16. package/src/core/AlertNotifier.js +0 -363
  17. package/src/core/CorrelationAnalyzer.js +0 -931
  18. package/src/core/CrossReferenceEngine.js +0 -624
  19. package/src/core/CurationEngine.js +0 -688
  20. package/src/core/DeprecationScheduler.js +0 -183
  21. package/src/core/LoadBearingDetector.js +0 -242
  22. package/src/core/NotificationService.js +0 -1032
  23. package/src/core/RapportOrchestrator.js +0 -632
  24. package/src/core/RelevanceDetector.js +0 -694
  25. package/src/core/StandardLifecycle.js +0 -244
  26. package/src/core/StandardsIngestion.js +0 -991
  27. package/src/core/TeamLoadBearingDetector.js +0 -431
  28. package/src/core/parsers/adrParser.js +0 -479
  29. package/src/core/parsers/cursorRulesParser.js +0 -564
  30. package/src/core/parsers/eslintParser.js +0 -439
  31. package/src/database/dbOperations.js +0 -105
  32. package/src/handlers/activity/activityGetMe.js +0 -98
  33. package/src/handlers/activity/activityGetTeam.js +0 -175
  34. package/src/handlers/admin/adminSetup.js +0 -216
  35. package/src/handlers/alerts/alertsAcknowledge.js +0 -92
  36. package/src/handlers/alerts/alertsGet.js +0 -250
  37. package/src/handlers/analytics/activitySummaryGet.js +0 -234
  38. package/src/handlers/analytics/coachingGet.js +0 -361
  39. package/src/handlers/analytics/convergenceGet.js +0 -236
  40. package/src/handlers/analytics/developerScoreGet.js +0 -137
  41. package/src/handlers/collaborators/collaboratorAdd.js +0 -200
  42. package/src/handlers/collaborators/collaboratorInvite.js +0 -219
  43. package/src/handlers/collaborators/collaboratorList.js +0 -82
  44. package/src/handlers/collaborators/collaboratorRemove.js +0 -128
  45. package/src/handlers/collaborators/inviteAccept.js +0 -122
  46. package/src/handlers/company/companyUsersDelete.js +0 -141
  47. package/src/handlers/company/companyUsersGet.js +0 -90
  48. package/src/handlers/company/companyUsersPost.js +0 -267
  49. package/src/handlers/company/companyUsersPut.js +0 -76
  50. package/src/handlers/context/contextGet.js +0 -57
  51. package/src/handlers/context/invariantsGet.js +0 -74
  52. package/src/handlers/context/loopsGet.js +0 -82
  53. package/src/handlers/context/notesCreate.js +0 -74
  54. package/src/handlers/context/purposeGet.js +0 -78
  55. package/src/handlers/correlations/correlationsDeveloperGet.js +0 -227
  56. package/src/handlers/correlations/correlationsGet.js +0 -93
  57. package/src/handlers/correlations/correlationsProjectGet.js +0 -153
  58. package/src/handlers/enterprise/controlTowerGet.js +0 -224
  59. package/src/handlers/enterprise/enterpriseAuditGet.js +0 -108
  60. package/src/handlers/enterprise/enterpriseContributorsGet.js +0 -85
  61. package/src/handlers/enterprise/enterpriseKnowledgeCategoriesGet.js +0 -53
  62. package/src/handlers/enterprise/enterpriseKnowledgeCreate.js +0 -77
  63. package/src/handlers/enterprise/enterpriseKnowledgeDelete.js +0 -71
  64. package/src/handlers/enterprise/enterpriseKnowledgeGet.js +0 -87
  65. package/src/handlers/enterprise/enterpriseKnowledgeUpdate.js +0 -122
  66. package/src/handlers/enterprise/enterpriseOnboardingComplete.js +0 -77
  67. package/src/handlers/enterprise/enterpriseOnboardingInvite.js +0 -138
  68. package/src/handlers/enterprise/enterpriseOnboardingSetup.js +0 -128
  69. package/src/handlers/enterprise/enterpriseOnboardingStatus.js +0 -88
  70. package/src/handlers/github/githubConnectionStatus.js +0 -49
  71. package/src/handlers/github/githubDiscoverPatterns.js +0 -621
  72. package/src/handlers/github/githubOAuthCallback.js +0 -178
  73. package/src/handlers/github/githubOAuthStart.js +0 -59
  74. package/src/handlers/github/githubPatternsReview.js +0 -76
  75. package/src/handlers/github/githubReposList.js +0 -105
  76. package/src/handlers/health/healthGet.js +0 -55
  77. package/src/handlers/helpers/auditLogger.js +0 -201
  78. package/src/handlers/helpers/checkSuperAdmin.js +0 -84
  79. package/src/handlers/helpers/decisionFrames.js +0 -29
  80. package/src/handlers/helpers/errorHandler.js +0 -49
  81. package/src/handlers/helpers/index.js +0 -138
  82. package/src/handlers/helpers/lambdaWrapper.js +0 -60
  83. package/src/handlers/helpers/mindmeldMcpCore.js +0 -1103
  84. package/src/handlers/helpers/predictiveCache.js +0 -51
  85. package/src/handlers/helpers/projectAccess.js +0 -88
  86. package/src/handlers/helpers/responseUtil.js +0 -55
  87. package/src/handlers/helpers/subscriptionTiers.js +0 -1168
  88. package/src/handlers/mcp/mcpHandler.js +0 -569
  89. package/src/handlers/mcp/mindmeldMcpHandler.js +0 -124
  90. package/src/handlers/mcp/mindmeldMcpStreamHandler.js +0 -342
  91. package/src/handlers/notifications/getPreferences.js +0 -84
  92. package/src/handlers/notifications/sendNotification.js +0 -170
  93. package/src/handlers/notifications/updatePreferences.js +0 -316
  94. package/src/handlers/patterns/patternEvaluatePromotionPost.js +0 -173
  95. package/src/handlers/patterns/patternUsagePost.js +0 -182
  96. package/src/handlers/patterns/patternViolationPost.js +0 -185
  97. package/src/handlers/projects/projectCreate.js +0 -248
  98. package/src/handlers/projects/projectDelete.js +0 -82
  99. package/src/handlers/projects/projectGet.js +0 -95
  100. package/src/handlers/projects/projectUpdate.js +0 -117
  101. package/src/handlers/reports/aiLeverage.js +0 -210
  102. package/src/handlers/reports/engineeringInvestment.js +0 -132
  103. package/src/handlers/reports/riskForecast.js +0 -206
  104. package/src/handlers/reports/standardsRoi.js +0 -254
  105. package/src/handlers/scheduled/analyzeCorrelations.js +0 -178
  106. package/src/handlers/scheduled/analyzeGitHistory.js +0 -510
  107. package/src/handlers/scheduled/generateAlerts.js +0 -135
  108. package/src/handlers/scheduled/maturityUpdateJob.js +0 -166
  109. package/src/handlers/scheduled/refreshActivity.js +0 -21
  110. package/src/handlers/scheduled/scanCompliance.js +0 -334
  111. package/src/handlers/sessions/sessionEndPost.js +0 -180
  112. package/src/handlers/sessions/sessionStandardsPost.js +0 -171
  113. package/src/handlers/standards/catalogGet.js +0 -185
  114. package/src/handlers/standards/catalogSync.js +0 -120
  115. package/src/handlers/standards/discoveriesGet.js +0 -89
  116. package/src/handlers/standards/projectStandardsGet.js +0 -129
  117. package/src/handlers/standards/projectStandardsPut.js +0 -151
  118. package/src/handlers/standards/standardsAuditGet.js +0 -65
  119. package/src/handlers/standards/standardsParseUpload.js +0 -149
  120. package/src/handlers/standards/standardsRelevantPost.js +0 -405
  121. package/src/handlers/standards/standardsTransition.js +0 -161
  122. package/src/handlers/stripe/addonManagePost.js +0 -240
  123. package/src/handlers/stripe/billingPortalPost.js +0 -93
  124. package/src/handlers/stripe/enterpriseCheckoutPost.js +0 -272
  125. package/src/handlers/stripe/seatsUpdatePost.js +0 -185
  126. package/src/handlers/stripe/subscriptionCancelDelete.js +0 -169
  127. package/src/handlers/stripe/subscriptionCreatePost.js +0 -221
  128. package/src/handlers/stripe/subscriptionUpdatePut.js +0 -163
  129. package/src/handlers/stripe/webhookPost.js +0 -482
  130. package/src/handlers/user/apiTokenCreate.js +0 -71
  131. package/src/handlers/user/apiTokenList.js +0 -64
  132. package/src/handlers/user/userSplashAck.js +0 -91
  133. package/src/handlers/user/userSplashGet.js +0 -211
  134. package/src/handlers/users/cognitoPostConfirmation.js +0 -186
  135. package/src/handlers/users/cognitoPreSignUp.js +0 -114
  136. package/src/handlers/users/userEntitlementsGet.js +0 -89
  137. package/src/handlers/users/userGet.js +0 -118
  138. package/src/handlers/users/userProfilePut.js +0 -77
  139. 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
- }