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