@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,166 +0,0 @@
1
- /**
2
- * Maturity Update Scheduled Job
3
- * Auto-progresses/demotes standards maturity based on 30-day compliance evidence
4
- *
5
- * Schedule: Daily
6
- * Auth: None (Lambda scheduled event)
7
- *
8
- * Maturity progression rules (based on session_standards compliance data):
9
- * - >90% followed + >50 sessions → enforced
10
- * - >70% followed + >20 sessions → validated
11
- * - <30% followed + >20 sessions → demote to provisional
12
- *
13
- * Records all transitions in standards_audit_trail for traceability
14
- */
15
-
16
- const { wrapHandler, executeQuery, createSuccessResponse } = require('./helpers');
17
-
18
- /**
19
- * Main handler
20
- */
21
- exports.handler = wrapHandler(async (event, context) => {
22
- console.log('[MaturityUpdateJob] Starting maturity auto-progression...');
23
-
24
- const lookbackDays = (event && event.lookbackDays) || 30;
25
- const dryRun = (event && event.dryRun) || false;
26
-
27
- const summary = {
28
- startedAt: new Date().toISOString(),
29
- standardsEvaluated: 0,
30
- promotions: 0,
31
- demotions: 0,
32
- unchanged: 0,
33
- transitions: [],
34
- errors: []
35
- };
36
-
37
- try {
38
- // Query 30-day compliance rates per standard from session_standards
39
- const complianceQuery = `
40
- SELECT
41
- ss.standard_id,
42
- ss.standard_name,
43
- COUNT(*) as total_sessions,
44
- COUNT(*) FILTER (WHERE ss.followed = true) as followed_count,
45
- COUNT(*) FILTER (WHERE ss.violated = true) as violated_count,
46
- ROUND(
47
- COUNT(*) FILTER (WHERE ss.followed = true)::decimal /
48
- NULLIF(COUNT(*), 0),
49
- 3
50
- ) as follow_rate,
51
- sp.maturity as current_maturity
52
- FROM rapport.session_standards ss
53
- LEFT JOIN rapport.standards_patterns sp ON sp.pattern_id = ss.standard_id
54
- WHERE ss.shown_at > NOW() - INTERVAL '${lookbackDays} days'
55
- GROUP BY ss.standard_id, ss.standard_name, sp.maturity
56
- HAVING COUNT(*) >= 10
57
- ORDER BY total_sessions DESC
58
- `;
59
-
60
- const complianceResult = await executeQuery(complianceQuery);
61
- const standards = complianceResult.rows;
62
-
63
- summary.standardsEvaluated = standards.length;
64
- console.log(`[MaturityUpdateJob] Evaluating ${standards.length} standards with sufficient data`);
65
-
66
- for (const standard of standards) {
67
- const totalSessions = parseInt(standard.total_sessions);
68
- const followRate = parseFloat(standard.follow_rate) || 0;
69
- const currentMaturity = standard.current_maturity || 'provisional';
70
-
71
- // Determine target maturity
72
- let targetMaturity = currentMaturity;
73
-
74
- if (followRate > 0.90 && totalSessions >= 50) {
75
- targetMaturity = 'enforced';
76
- } else if (followRate > 0.70 && totalSessions >= 20) {
77
- targetMaturity = 'validated';
78
- } else if (followRate < 0.30 && totalSessions >= 20) {
79
- targetMaturity = 'provisional';
80
- }
81
-
82
- if (targetMaturity === currentMaturity) {
83
- summary.unchanged++;
84
- continue;
85
- }
86
-
87
- // Determine if this is a promotion or demotion
88
- const maturityOrder = { provisional: 0, validated: 1, reinforced: 2, enforced: 3 };
89
- const isPromotion = (maturityOrder[targetMaturity] || 0) > (maturityOrder[currentMaturity] || 0);
90
-
91
- const transition = {
92
- standard_id: standard.standard_id,
93
- standard_name: standard.standard_name,
94
- from: currentMaturity,
95
- to: targetMaturity,
96
- direction: isPromotion ? 'promotion' : 'demotion',
97
- follow_rate: followRate,
98
- total_sessions: totalSessions
99
- };
100
-
101
- summary.transitions.push(transition);
102
-
103
- if (dryRun) {
104
- console.log(`[MaturityUpdateJob] DRY RUN: Would ${transition.direction} ${standard.standard_id}: ${currentMaturity} → ${targetMaturity} (${(followRate * 100).toFixed(1)}% followed, ${totalSessions} sessions)`);
105
- if (isPromotion) summary.promotions++;
106
- else summary.demotions++;
107
- continue;
108
- }
109
-
110
- try {
111
- // Update standards_patterns maturity
112
- await executeQuery(`
113
- UPDATE rapport.standards_patterns
114
- SET maturity = $1, last_updated = NOW()
115
- WHERE pattern_id = $2
116
- `, [targetMaturity, standard.standard_id]);
117
-
118
- // Record in audit trail
119
- await executeQuery(`
120
- INSERT INTO rapport.standards_audit_trail (
121
- standard_id, action, old_state, new_state,
122
- user_email, reason, metadata, created_at
123
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())
124
- `, [
125
- standard.standard_id,
126
- isPromotion ? 'auto_promote' : 'auto_demote',
127
- currentMaturity,
128
- targetMaturity,
129
- 'system@mindmeld.dev',
130
- `Auto-${transition.direction}: ${(followRate * 100).toFixed(1)}% follow rate across ${totalSessions} sessions (30-day window)`,
131
- JSON.stringify({
132
- follow_rate: followRate,
133
- total_sessions: totalSessions,
134
- followed_count: parseInt(standard.followed_count),
135
- violated_count: parseInt(standard.violated_count),
136
- lookback_days: lookbackDays
137
- })
138
- ]);
139
-
140
- if (isPromotion) summary.promotions++;
141
- else summary.demotions++;
142
-
143
- console.log(`[MaturityUpdateJob] ${transition.direction}: ${standard.standard_id} ${currentMaturity} → ${targetMaturity}`);
144
-
145
- } catch (error) {
146
- console.error(`[MaturityUpdateJob] Error updating ${standard.standard_id}:`, error.message);
147
- summary.errors.push({
148
- standard_id: standard.standard_id,
149
- error: error.message
150
- });
151
- }
152
- }
153
-
154
- } catch (error) {
155
- console.error('[MaturityUpdateJob] Job failed:', error);
156
- summary.errors.push({ step: 'main', error: error.message });
157
- }
158
-
159
- summary.completedAt = new Date().toISOString();
160
- console.log(`[MaturityUpdateJob] Complete: ${summary.promotions} promotions, ${summary.demotions} demotions, ${summary.unchanged} unchanged`);
161
-
162
- return createSuccessResponse(
163
- summary,
164
- `Maturity update complete: ${summary.promotions} promotions, ${summary.demotions} demotions`
165
- );
166
- });
@@ -1,21 +0,0 @@
1
- /**
2
- * Refresh Activity Scheduled Job
3
- * Refreshes materialized views for developer and project activity
4
- *
5
- * Schedule: Every 15 minutes
6
- * Auth: None (Lambda scheduled event)
7
- */
8
-
9
- const { wrapHandler, executeQuery, createSuccessResponse } = require('./helpers');
10
-
11
- exports.handler = wrapHandler(async (event, context) => {
12
- // Refresh developer activity view
13
- await executeQuery('SELECT rapport.refresh_developer_activity()');
14
-
15
- // Refresh project activity view
16
- await executeQuery('SELECT rapport.refresh_project_activity()');
17
-
18
- return createSuccessResponse({
19
- views_refreshed: ['developer_activity', 'project_activity']
20
- }, 'Activity views refreshed');
21
- });
@@ -1,334 +0,0 @@
1
- /**
2
- * Standards Compliance Scanner
3
- * Scans committed code for standards patterns and anti-patterns
4
- *
5
- * Schedule: Daily after git history analysis
6
- * Auth: None (Lambda scheduled event)
7
- *
8
- * Requires: GitHub token with contents:read permission
9
- */
10
-
11
- const { wrapHandler, executeQuery, createSuccessResponse } = require('./helpers');
12
- const https = require('https');
13
-
14
- exports.handler = wrapHandler(async (event, context) => {
15
- // Check if GitHub token is available
16
- if (!process.env.GITHUB_TOKEN) {
17
- return createSuccessResponse({ scanned: false }, 'Compliance scan skipped - no GitHub token');
18
- }
19
-
20
- // Get patterns to scan for
21
- const patterns = await getActivePatterns();
22
-
23
- // Get recent commits that need scanning
24
- const commits = await getUnscannedCommits();
25
-
26
- if (commits.length === 0) {
27
- return createSuccessResponse({ scanned: 0 }, 'No commits to scan');
28
- }
29
-
30
- const results = {
31
- commits_scanned: 0,
32
- files_scanned: 0,
33
- patterns_found: 0,
34
- anti_patterns_found: 0
35
- };
36
-
37
- for (const commit of commits) {
38
- try {
39
- const scanResult = await scanCommit(commit, patterns);
40
- results.commits_scanned++;
41
- results.files_scanned += scanResult.files_scanned;
42
- results.patterns_found += scanResult.patterns_found;
43
- results.anti_patterns_found += scanResult.anti_patterns_found;
44
-
45
- // Update developer metrics with compliance data
46
- await updateComplianceMetrics(commit, scanResult);
47
-
48
- } catch (error) {
49
- // Continue scanning other commits even if one fails
50
- }
51
- }
52
-
53
- // Refresh team summary view
54
- await executeQuery('SELECT rapport.refresh_team_summary()');
55
-
56
- return createSuccessResponse(results, 'Compliance scan complete');
57
- });
58
-
59
- /**
60
- * Get active patterns from database
61
- */
62
- async function getActivePatterns() {
63
- const result = await executeQuery(`
64
- SELECT pattern_id, pattern_name, pattern_type, language,
65
- regex_pattern, severity, source
66
- FROM rapport.code_patterns
67
- WHERE enabled = true
68
- `);
69
- return result.rows;
70
- }
71
-
72
- /**
73
- * Get commits that haven't been scanned yet
74
- */
75
- async function getUnscannedCommits() {
76
- const result = await executeQuery(`
77
- SELECT c.commit_sha, c.repo_id, c.author_email,
78
- r.repo_url, r.repo_name
79
- FROM rapport.commits c
80
- JOIN rapport.git_repositories r ON c.repo_id = r.repo_id
81
- WHERE c.scanned_at IS NULL
82
- AND c.committed_at >= NOW() - INTERVAL '7 days'
83
- ORDER BY c.committed_at DESC
84
- LIMIT 100
85
- `);
86
- return result.rows;
87
- }
88
-
89
- /**
90
- * Scan a single commit for patterns
91
- */
92
- async function scanCommit(commit, patterns) {
93
- const result = {
94
- files_scanned: 0,
95
- patterns_found: 0,
96
- anti_patterns_found: 0,
97
- findings: []
98
- };
99
-
100
- // Parse owner/repo from URL
101
- const match = commit.repo_url.match(/github\.com[/:]([^/]+)\/([^/.]+)/);
102
- if (!match) {
103
- console.warn(`Cannot parse GitHub URL: ${commit.repo_url}`);
104
- return result;
105
- }
106
-
107
- const [, owner, repoName] = match;
108
-
109
- // Get commit details with file changes
110
- const commitDetails = await fetchCommitDetails(owner, repoName, commit.commit_sha);
111
- if (!commitDetails || !commitDetails.files) {
112
- return result;
113
- }
114
-
115
- for (const file of commitDetails.files) {
116
- // Skip non-code files
117
- if (!isCodeFile(file.filename)) continue;
118
-
119
- // Get file content
120
- const content = await fetchFileContent(owner, repoName, commit.commit_sha, file.filename);
121
- if (!content) continue;
122
-
123
- result.files_scanned++;
124
-
125
- // Detect language from extension
126
- const language = detectLanguage(file.filename);
127
-
128
- // Scan for patterns
129
- const relevantPatterns = patterns.filter(p =>
130
- p.language === language || p.language === 'any'
131
- );
132
-
133
- for (const pattern of relevantPatterns) {
134
- try {
135
- const regex = new RegExp(pattern.regex_pattern, 'gm');
136
- const matches = content.match(regex);
137
-
138
- if (matches && matches.length > 0) {
139
- if (pattern.pattern_type === 'standard') {
140
- result.patterns_found += matches.length;
141
- } else {
142
- result.anti_patterns_found += matches.length;
143
- }
144
-
145
- result.findings.push({
146
- file: file.filename,
147
- pattern_name: pattern.pattern_name,
148
- pattern_type: pattern.pattern_type,
149
- severity: pattern.severity,
150
- count: matches.length
151
- });
152
- }
153
- } catch (regexError) {
154
- console.warn(`Invalid regex for pattern ${pattern.pattern_name}:`, regexError.message);
155
- }
156
- }
157
- }
158
-
159
- // Mark commit as scanned
160
- await executeQuery(`
161
- UPDATE rapport.commits
162
- SET scanned_at = NOW(),
163
- compliance_data = $2
164
- WHERE commit_sha = $1
165
- `, [commit.commit_sha, JSON.stringify(result.findings)]);
166
-
167
- return result;
168
- }
169
-
170
- /**
171
- * Fetch commit details from GitHub API
172
- */
173
- async function fetchCommitDetails(owner, repo, sha) {
174
- return new Promise((resolve, reject) => {
175
- const options = {
176
- hostname: 'api.github.com',
177
- path: `/repos/${owner}/${repo}/commits/${sha}`,
178
- method: 'GET',
179
- headers: {
180
- 'User-Agent': 'MindMeld-ComplianceScanner/1.0',
181
- 'Accept': 'application/vnd.github.v3+json',
182
- 'Authorization': `token ${process.env.GITHUB_TOKEN}`
183
- },
184
- timeout: 30000
185
- };
186
-
187
- const req = https.request(options, (res) => {
188
- let data = '';
189
- res.on('data', chunk => data += chunk);
190
- res.on('end', () => {
191
- try {
192
- if (res.statusCode !== 200) {
193
- console.warn(`GitHub API returned ${res.statusCode} for commit ${sha}`);
194
- resolve(null);
195
- return;
196
- }
197
- resolve(JSON.parse(data));
198
- } catch (e) {
199
- reject(e);
200
- }
201
- });
202
- });
203
-
204
- req.on('error', reject);
205
- req.on('timeout', () => {
206
- req.destroy();
207
- reject(new Error('GitHub API timeout'));
208
- });
209
-
210
- req.end();
211
- });
212
- }
213
-
214
- /**
215
- * Fetch file content from GitHub API
216
- */
217
- async function fetchFileContent(owner, repo, sha, path) {
218
- return new Promise((resolve, reject) => {
219
- const options = {
220
- hostname: 'api.github.com',
221
- path: `/repos/${owner}/${repo}/contents/${encodeURIComponent(path)}?ref=${sha}`,
222
- method: 'GET',
223
- headers: {
224
- 'User-Agent': 'MindMeld-ComplianceScanner/1.0',
225
- 'Accept': 'application/vnd.github.v3.raw',
226
- 'Authorization': `token ${process.env.GITHUB_TOKEN}`
227
- },
228
- timeout: 30000
229
- };
230
-
231
- const req = https.request(options, (res) => {
232
- let data = '';
233
- res.on('data', chunk => data += chunk);
234
- res.on('end', () => {
235
- if (res.statusCode !== 200) {
236
- resolve(null);
237
- return;
238
- }
239
- resolve(data);
240
- });
241
- });
242
-
243
- req.on('error', () => resolve(null));
244
- req.on('timeout', () => {
245
- req.destroy();
246
- resolve(null);
247
- });
248
-
249
- req.end();
250
- });
251
- }
252
-
253
- /**
254
- * Check if file is a code file worth scanning
255
- */
256
- function isCodeFile(filename) {
257
- const codeExtensions = [
258
- '.js', '.ts', '.tsx', '.jsx',
259
- '.py', '.pyw',
260
- '.go',
261
- '.java',
262
- '.rb',
263
- '.php',
264
- '.cs',
265
- '.swift',
266
- '.kt', '.kts',
267
- '.rs',
268
- '.c', '.cpp', '.h', '.hpp',
269
- '.sql',
270
- '.yaml', '.yml'
271
- ];
272
- return codeExtensions.some(ext => filename.endsWith(ext));
273
- }
274
-
275
- /**
276
- * Detect language from file extension
277
- */
278
- function detectLanguage(filename) {
279
- const ext = filename.split('.').pop().toLowerCase();
280
- const languageMap = {
281
- 'js': 'javascript',
282
- 'jsx': 'javascript',
283
- 'ts': 'javascript',
284
- 'tsx': 'javascript',
285
- 'py': 'python',
286
- 'pyw': 'python',
287
- 'go': 'go',
288
- 'java': 'java',
289
- 'rb': 'ruby',
290
- 'php': 'php',
291
- 'cs': 'csharp',
292
- 'swift': 'swift',
293
- 'kt': 'kotlin',
294
- 'kts': 'kotlin',
295
- 'rs': 'rust',
296
- 'c': 'c',
297
- 'cpp': 'cpp',
298
- 'h': 'c',
299
- 'hpp': 'cpp',
300
- 'sql': 'sql',
301
- 'yaml': 'yaml',
302
- 'yml': 'yaml'
303
- };
304
- return languageMap[ext] || 'unknown';
305
- }
306
-
307
- /**
308
- * Update developer metrics with compliance data
309
- */
310
- async function updateComplianceMetrics(commit, scanResult) {
311
- const isCompliant = scanResult.anti_patterns_found === 0 && scanResult.patterns_found > 0;
312
-
313
- await executeQuery(`
314
- UPDATE rapport.developer_metrics
315
- SET
316
- standards_compliant_commits = standards_compliant_commits + $3,
317
- anti_pattern_commits = anti_pattern_commits + $4,
318
- compliance_score = CASE
319
- WHEN commit_count > 0 THEN
320
- (standards_compliant_commits + $3)::DECIMAL / commit_count * 100
321
- ELSE 0
322
- END
323
- WHERE repo_id = $1
324
- AND developer_email = $2
325
- AND period_end >= CURRENT_DATE
326
- `, [
327
- commit.repo_id,
328
- commit.author_email,
329
- isCompliant ? 1 : 0,
330
- scanResult.anti_patterns_found > 0 ? 1 : 0
331
- ]);
332
- }
333
-
334
- // Removed manual success/failure helpers - using wrapHandler pattern
@@ -1,180 +0,0 @@
1
- /**
2
- * Session End Handler
3
- * Records session completion and outcomes
4
- *
5
- * POST /api/sessions/end
6
- * Body: { session_id, project_id?, user_id?, patterns_used, patterns_learned, duration_seconds?, session_data? }
7
- *
8
- * Called by: pre-compact.js hook at end of session
9
- */
10
-
11
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
12
-
13
- /**
14
- * Record session end and update metrics
15
- */
16
- async function recordSessionEnd({ body: requestBody = {}, requestContext }) {
17
- try {
18
- const Request_ID = requestContext?.requestId || 'unknown';
19
-
20
- const {
21
- session_id,
22
- project_id,
23
- user_id,
24
- patterns_used = 0,
25
- patterns_learned = 0,
26
- duration_seconds,
27
- working_directory,
28
- git_branch,
29
- session_data = {}
30
- } = requestBody;
31
-
32
- // Validate required fields
33
- if (!session_id) {
34
- return createErrorResponse(400, 'session_id is required');
35
- }
36
-
37
- // Try to update existing session, or create if not exists
38
- const upsertQuery = `
39
- INSERT INTO rapport.sessions (
40
- session_id,
41
- project_id,
42
- email_address,
43
- started_at,
44
- ended_at,
45
- duration_seconds,
46
- working_directory,
47
- git_branch,
48
- patterns_used,
49
- patterns_learned,
50
- session_data
51
- ) VALUES (
52
- $1, $2, $3,
53
- NOW() - INTERVAL '1 second' * COALESCE($4, 0),
54
- NOW(),
55
- $4, $5, $6, $7, $8, $9
56
- )
57
- ON CONFLICT (session_id) DO UPDATE SET
58
- ended_at = NOW(),
59
- duration_seconds = COALESCE(EXCLUDED.duration_seconds,
60
- EXTRACT(EPOCH FROM (NOW() - rapport.sessions.started_at))::INTEGER),
61
- working_directory = COALESCE(EXCLUDED.working_directory, rapport.sessions.working_directory),
62
- git_branch = COALESCE(EXCLUDED.git_branch, rapport.sessions.git_branch),
63
- patterns_used = EXCLUDED.patterns_used,
64
- patterns_learned = EXCLUDED.patterns_learned,
65
- session_data = rapport.sessions.session_data || EXCLUDED.session_data
66
- RETURNING
67
- session_id,
68
- project_id,
69
- email_address,
70
- started_at,
71
- ended_at,
72
- duration_seconds,
73
- patterns_used,
74
- patterns_learned
75
- `;
76
-
77
- let result;
78
- try {
79
- result = await executeQuery(upsertQuery, [
80
- session_id,
81
- project_id || null,
82
- user_id || 'anonymous',
83
- duration_seconds || null,
84
- working_directory || null,
85
- git_branch || null,
86
- patterns_used,
87
- patterns_learned,
88
- JSON.stringify(session_data)
89
- ]);
90
- } catch (insertError) {
91
- // FK constraint likely failed (project_id or user doesn't exist)
92
- console.error('[sessionEndPost] Session upsert failed:', insertError.message);
93
-
94
- // Return success anyway - we tried
95
- return createSuccessResponse(
96
- {
97
- Records: [{
98
- session_id,
99
- recorded: false,
100
- error: 'Session record failed - project or user not found'
101
- }]
102
- },
103
- 'Session end recorded (without DB record)',
104
- {
105
- Request_ID,
106
- Timestamp: new Date().toISOString()
107
- }
108
- );
109
- }
110
-
111
- const session = result.rows[0];
112
-
113
- // Update session_standards to mark followed (standards shown but not violated)
114
- const updateFollowedQuery = `
115
- UPDATE rapport.session_standards
116
- SET followed = CASE WHEN violated IS NULL OR violated = false THEN true ELSE false END
117
- WHERE session_id = $1 AND followed IS NULL
118
- `;
119
-
120
- try {
121
- await executeQuery(updateFollowedQuery, [session_id]);
122
- } catch (updateError) {
123
- // Non-critical - just log
124
- console.error('[sessionEndPost] Failed to update followed status:', updateError.message);
125
- }
126
-
127
- // Get summary of standards compliance for this session
128
- const complianceQuery = `
129
- SELECT
130
- COUNT(*) as total_shown,
131
- COUNT(*) FILTER (WHERE followed = true) as followed,
132
- COUNT(*) FILTER (WHERE violated = true) as violated
133
- FROM rapport.session_standards
134
- WHERE session_id = $1
135
- `;
136
-
137
- let compliance = { total_shown: 0, followed: 0, violated: 0 };
138
- try {
139
- const complianceResult = await executeQuery(complianceQuery, [session_id]);
140
- if (complianceResult.rows.length > 0) {
141
- compliance = complianceResult.rows[0];
142
- }
143
- } catch (complianceError) {
144
- // Non-critical
145
- }
146
-
147
- return createSuccessResponse(
148
- {
149
- Records: [{
150
- session_id: session.session_id,
151
- project_id: session.project_id,
152
- email_address: session.email_address,
153
- started_at: session.started_at,
154
- ended_at: session.ended_at,
155
- duration_seconds: session.duration_seconds,
156
- patterns_used: session.patterns_used,
157
- patterns_learned: session.patterns_learned,
158
- standards_compliance: {
159
- total_shown: parseInt(compliance.total_shown) || 0,
160
- followed: parseInt(compliance.followed) || 0,
161
- violated: parseInt(compliance.violated) || 0
162
- },
163
- recorded: true
164
- }]
165
- },
166
- 'Session end recorded',
167
- {
168
- Total_Records: 1,
169
- Request_ID,
170
- Timestamp: new Date().toISOString()
171
- }
172
- );
173
-
174
- } catch (error) {
175
- console.error('Handler Error:', error);
176
- return handleError(error);
177
- }
178
- }
179
-
180
- exports.handler = wrapHandler(recordSessionEnd);