@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,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
- }