@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,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);