@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.
- package/hooks/session-end.js +25 -0
- package/hooks/session-start.js +363 -83
- package/hooks/session-watcher.js +585 -0
- package/package.json +19 -13
- package/scripts/init-project.js +9 -23
- package/src/client/dbShim.js +16 -0
- package/src/core/AuthManager.js +3 -2
- package/src/handlers/helpers/dbOperations.js +9 -46
- package/src/index.js +2 -217
- package/src/utils/piiMask.js +16 -0
- package/scripts/harvest.js +0 -601
- package/scripts/inject.js +0 -409
- package/scripts/mcp-bridge.js +0 -220
- package/scripts/repo-analyzer.js +0 -870
- package/src/collaboration/CollaborationPrompt.js +0 -460
- package/src/core/AlertEngine.js +0 -813
- package/src/core/AlertNotifier.js +0 -363
- package/src/core/CorrelationAnalyzer.js +0 -931
- package/src/core/CrossReferenceEngine.js +0 -624
- package/src/core/CurationEngine.js +0 -688
- package/src/core/DeprecationScheduler.js +0 -183
- package/src/core/LoadBearingDetector.js +0 -242
- package/src/core/NotificationService.js +0 -1032
- package/src/core/RapportOrchestrator.js +0 -632
- package/src/core/RelevanceDetector.js +0 -694
- package/src/core/StandardLifecycle.js +0 -244
- package/src/core/StandardsIngestion.js +0 -991
- package/src/core/TeamLoadBearingDetector.js +0 -431
- package/src/core/parsers/adrParser.js +0 -479
- package/src/core/parsers/cursorRulesParser.js +0 -564
- package/src/core/parsers/eslintParser.js +0 -439
- package/src/database/dbOperations.js +0 -105
- package/src/handlers/activity/activityGetMe.js +0 -98
- package/src/handlers/activity/activityGetTeam.js +0 -175
- package/src/handlers/admin/adminSetup.js +0 -216
- package/src/handlers/alerts/alertsAcknowledge.js +0 -92
- package/src/handlers/alerts/alertsGet.js +0 -250
- package/src/handlers/analytics/activitySummaryGet.js +0 -234
- package/src/handlers/analytics/coachingGet.js +0 -361
- package/src/handlers/analytics/convergenceGet.js +0 -236
- package/src/handlers/analytics/developerScoreGet.js +0 -137
- package/src/handlers/collaborators/collaboratorAdd.js +0 -200
- package/src/handlers/collaborators/collaboratorInvite.js +0 -219
- package/src/handlers/collaborators/collaboratorList.js +0 -82
- package/src/handlers/collaborators/collaboratorRemove.js +0 -128
- package/src/handlers/collaborators/inviteAccept.js +0 -122
- package/src/handlers/company/companyUsersDelete.js +0 -141
- package/src/handlers/company/companyUsersGet.js +0 -90
- package/src/handlers/company/companyUsersPost.js +0 -267
- package/src/handlers/company/companyUsersPut.js +0 -76
- package/src/handlers/context/contextGet.js +0 -57
- package/src/handlers/context/invariantsGet.js +0 -74
- package/src/handlers/context/loopsGet.js +0 -82
- package/src/handlers/context/notesCreate.js +0 -74
- package/src/handlers/context/purposeGet.js +0 -78
- package/src/handlers/correlations/correlationsDeveloperGet.js +0 -227
- package/src/handlers/correlations/correlationsGet.js +0 -93
- package/src/handlers/correlations/correlationsProjectGet.js +0 -153
- package/src/handlers/enterprise/controlTowerGet.js +0 -224
- package/src/handlers/enterprise/enterpriseAuditGet.js +0 -108
- package/src/handlers/enterprise/enterpriseContributorsGet.js +0 -85
- package/src/handlers/enterprise/enterpriseKnowledgeCategoriesGet.js +0 -53
- package/src/handlers/enterprise/enterpriseKnowledgeCreate.js +0 -77
- package/src/handlers/enterprise/enterpriseKnowledgeDelete.js +0 -71
- package/src/handlers/enterprise/enterpriseKnowledgeGet.js +0 -87
- package/src/handlers/enterprise/enterpriseKnowledgeUpdate.js +0 -122
- package/src/handlers/enterprise/enterpriseOnboardingComplete.js +0 -77
- package/src/handlers/enterprise/enterpriseOnboardingInvite.js +0 -138
- package/src/handlers/enterprise/enterpriseOnboardingSetup.js +0 -128
- package/src/handlers/enterprise/enterpriseOnboardingStatus.js +0 -88
- package/src/handlers/github/githubConnectionStatus.js +0 -49
- package/src/handlers/github/githubDiscoverPatterns.js +0 -621
- package/src/handlers/github/githubOAuthCallback.js +0 -178
- package/src/handlers/github/githubOAuthStart.js +0 -59
- package/src/handlers/github/githubPatternsReview.js +0 -76
- package/src/handlers/github/githubReposList.js +0 -105
- package/src/handlers/health/healthGet.js +0 -55
- package/src/handlers/helpers/auditLogger.js +0 -201
- package/src/handlers/helpers/checkSuperAdmin.js +0 -84
- package/src/handlers/helpers/decisionFrames.js +0 -29
- package/src/handlers/helpers/errorHandler.js +0 -49
- package/src/handlers/helpers/index.js +0 -138
- package/src/handlers/helpers/lambdaWrapper.js +0 -60
- package/src/handlers/helpers/mindmeldMcpCore.js +0 -1103
- package/src/handlers/helpers/predictiveCache.js +0 -51
- package/src/handlers/helpers/projectAccess.js +0 -88
- package/src/handlers/helpers/responseUtil.js +0 -55
- package/src/handlers/helpers/subscriptionTiers.js +0 -1168
- package/src/handlers/mcp/mcpHandler.js +0 -569
- package/src/handlers/mcp/mindmeldMcpHandler.js +0 -124
- package/src/handlers/mcp/mindmeldMcpStreamHandler.js +0 -342
- package/src/handlers/notifications/getPreferences.js +0 -84
- package/src/handlers/notifications/sendNotification.js +0 -170
- package/src/handlers/notifications/updatePreferences.js +0 -316
- package/src/handlers/patterns/patternEvaluatePromotionPost.js +0 -173
- package/src/handlers/patterns/patternUsagePost.js +0 -182
- package/src/handlers/patterns/patternViolationPost.js +0 -185
- package/src/handlers/projects/projectCreate.js +0 -248
- package/src/handlers/projects/projectDelete.js +0 -82
- package/src/handlers/projects/projectGet.js +0 -95
- package/src/handlers/projects/projectUpdate.js +0 -117
- package/src/handlers/reports/aiLeverage.js +0 -210
- package/src/handlers/reports/engineeringInvestment.js +0 -132
- package/src/handlers/reports/riskForecast.js +0 -206
- package/src/handlers/reports/standardsRoi.js +0 -254
- package/src/handlers/scheduled/analyzeCorrelations.js +0 -178
- package/src/handlers/scheduled/analyzeGitHistory.js +0 -510
- package/src/handlers/scheduled/generateAlerts.js +0 -135
- package/src/handlers/scheduled/maturityUpdateJob.js +0 -166
- package/src/handlers/scheduled/refreshActivity.js +0 -21
- package/src/handlers/scheduled/scanCompliance.js +0 -334
- package/src/handlers/sessions/sessionEndPost.js +0 -180
- package/src/handlers/sessions/sessionStandardsPost.js +0 -171
- package/src/handlers/standards/catalogGet.js +0 -185
- package/src/handlers/standards/catalogSync.js +0 -120
- package/src/handlers/standards/discoveriesGet.js +0 -89
- package/src/handlers/standards/projectStandardsGet.js +0 -129
- package/src/handlers/standards/projectStandardsPut.js +0 -151
- package/src/handlers/standards/standardsAuditGet.js +0 -65
- package/src/handlers/standards/standardsParseUpload.js +0 -149
- package/src/handlers/standards/standardsRelevantPost.js +0 -405
- package/src/handlers/standards/standardsTransition.js +0 -161
- package/src/handlers/stripe/addonManagePost.js +0 -240
- package/src/handlers/stripe/billingPortalPost.js +0 -93
- package/src/handlers/stripe/enterpriseCheckoutPost.js +0 -272
- package/src/handlers/stripe/seatsUpdatePost.js +0 -185
- package/src/handlers/stripe/subscriptionCancelDelete.js +0 -169
- package/src/handlers/stripe/subscriptionCreatePost.js +0 -221
- package/src/handlers/stripe/subscriptionUpdatePut.js +0 -163
- package/src/handlers/stripe/webhookPost.js +0 -482
- package/src/handlers/user/apiTokenCreate.js +0 -71
- package/src/handlers/user/apiTokenList.js +0 -64
- package/src/handlers/user/userSplashAck.js +0 -91
- package/src/handlers/user/userSplashGet.js +0 -211
- package/src/handlers/users/cognitoPostConfirmation.js +0 -186
- package/src/handlers/users/cognitoPreSignUp.js +0 -114
- package/src/handlers/users/userEntitlementsGet.js +0 -89
- package/src/handlers/users/userGet.js +0 -118
- package/src/handlers/users/userProfilePut.js +0 -77
- 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);
|