@equilateral_ai/mindmeld 3.5.3 → 4.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/hooks/session-start.js +312 -85
- package/package.json +21 -13
- package/scripts/init-project.js +9 -23
- package/scripts/repo-analyzer.js +118 -2
- 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/inject.js +0 -409
- package/scripts/mcp-bridge.js +0 -220
- package/scripts/standards.js +0 -285
- 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
package/scripts/repo-analyzer.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
const fs = require('fs').promises;
|
|
16
16
|
const path = require('path');
|
|
17
|
+
const { execSync } = require('child_process');
|
|
17
18
|
|
|
18
19
|
// ============================================================================
|
|
19
20
|
// SECURITY ANTI-PATTERNS (from securityScanner.js)
|
|
@@ -335,7 +336,8 @@ class RepoAnalyzer {
|
|
|
335
336
|
low: [],
|
|
336
337
|
summary: {}
|
|
337
338
|
},
|
|
338
|
-
recommendations: []
|
|
339
|
+
recommendations: [],
|
|
340
|
+
contributors: []
|
|
339
341
|
};
|
|
340
342
|
}
|
|
341
343
|
|
|
@@ -371,7 +373,11 @@ class RepoAnalyzer {
|
|
|
371
373
|
await this.scanSecurity(maxFiles);
|
|
372
374
|
}
|
|
373
375
|
|
|
374
|
-
// 5.
|
|
376
|
+
// 5. Extract contributor profiles from git history
|
|
377
|
+
if (verbose) console.log('👥 Extracting contributor profiles...');
|
|
378
|
+
await this.extractContributors();
|
|
379
|
+
|
|
380
|
+
// 6. Generate recommendations
|
|
375
381
|
if (verbose) console.log('💡 Generating recommendations...');
|
|
376
382
|
this.generateRecommendations();
|
|
377
383
|
|
|
@@ -633,6 +639,109 @@ class RepoAnalyzer {
|
|
|
633
639
|
};
|
|
634
640
|
}
|
|
635
641
|
|
|
642
|
+
/**
|
|
643
|
+
* Extract contributor profiles from git history
|
|
644
|
+
*/
|
|
645
|
+
async extractContributors() {
|
|
646
|
+
try {
|
|
647
|
+
execSync('git rev-parse --is-inside-work-tree', { cwd: this.projectPath, stdio: 'pipe' });
|
|
648
|
+
} catch (_) {
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
try {
|
|
653
|
+
const shortlog = execSync('git shortlog -sne --all', {
|
|
654
|
+
cwd: this.projectPath,
|
|
655
|
+
encoding: 'utf-8',
|
|
656
|
+
timeout: 30000
|
|
657
|
+
}).trim();
|
|
658
|
+
|
|
659
|
+
if (!shortlog) return;
|
|
660
|
+
|
|
661
|
+
const contributors = [];
|
|
662
|
+
for (const line of shortlog.split('\n')) {
|
|
663
|
+
const match = line.trim().match(/^\s*(\d+)\s+(.+?)\s+<(.+?)>\s*$/);
|
|
664
|
+
if (!match) continue;
|
|
665
|
+
contributors.push({
|
|
666
|
+
total_commits: parseInt(match[1], 10),
|
|
667
|
+
name: match[2],
|
|
668
|
+
email: match[3]
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
for (const contributor of contributors.slice(0, 50)) {
|
|
673
|
+
try {
|
|
674
|
+
const firstCommit = execSync(
|
|
675
|
+
`git log --reverse --format=%aI --author="${contributor.email}" | head -1`,
|
|
676
|
+
{ cwd: this.projectPath, encoding: 'utf-8', timeout: 10000, shell: true }
|
|
677
|
+
).trim();
|
|
678
|
+
|
|
679
|
+
const lastCommit = execSync(
|
|
680
|
+
`git log --format=%aI --author="${contributor.email}" -1`,
|
|
681
|
+
{ cwd: this.projectPath, encoding: 'utf-8', timeout: 10000 }
|
|
682
|
+
).trim();
|
|
683
|
+
|
|
684
|
+
contributor.first_commit_date = firstCommit || null;
|
|
685
|
+
contributor.last_commit_date = lastCommit || null;
|
|
686
|
+
|
|
687
|
+
if (firstCommit) {
|
|
688
|
+
const first = new Date(firstCommit);
|
|
689
|
+
const now = new Date();
|
|
690
|
+
contributor.tenure_months = Math.floor(
|
|
691
|
+
(now.getTime() - first.getTime()) / (30.44 * 24 * 3600 * 1000)
|
|
692
|
+
);
|
|
693
|
+
} else {
|
|
694
|
+
contributor.tenure_months = 0;
|
|
695
|
+
}
|
|
696
|
+
} catch (_) {
|
|
697
|
+
contributor.first_commit_date = null;
|
|
698
|
+
contributor.last_commit_date = null;
|
|
699
|
+
contributor.tenure_months = 0;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// File ownership — sample up to 200 files
|
|
704
|
+
const ownership = {};
|
|
705
|
+
try {
|
|
706
|
+
const files = execSync('git ls-files | head -200', {
|
|
707
|
+
cwd: this.projectPath,
|
|
708
|
+
encoding: 'utf-8',
|
|
709
|
+
timeout: 10000,
|
|
710
|
+
shell: true
|
|
711
|
+
}).trim().split('\n').filter(Boolean);
|
|
712
|
+
|
|
713
|
+
for (const file of files) {
|
|
714
|
+
try {
|
|
715
|
+
const author = execSync(`git log --format=%ae -1 -- "${file}"`, {
|
|
716
|
+
cwd: this.projectPath,
|
|
717
|
+
encoding: 'utf-8',
|
|
718
|
+
timeout: 5000
|
|
719
|
+
}).trim();
|
|
720
|
+
if (author) {
|
|
721
|
+
ownership[author] = (ownership[author] || 0) + 1;
|
|
722
|
+
}
|
|
723
|
+
} catch (_) { /* skip file */ }
|
|
724
|
+
}
|
|
725
|
+
} catch (_) { /* file ownership optional */ }
|
|
726
|
+
|
|
727
|
+
for (const contributor of contributors.slice(0, 50)) {
|
|
728
|
+
contributor.files_owned = ownership[contributor.email] || 0;
|
|
729
|
+
|
|
730
|
+
if (contributor.total_commits >= 10 && contributor.tenure_months >= 3) {
|
|
731
|
+
contributor.computed_maturity = 'contributor';
|
|
732
|
+
contributor.maturity_basis = `Git bootstrap: ${contributor.total_commits} commits, ${contributor.tenure_months} months tenure`;
|
|
733
|
+
} else {
|
|
734
|
+
contributor.computed_maturity = 'observer';
|
|
735
|
+
contributor.maturity_basis = `${contributor.total_commits} commits, ${contributor.tenure_months} months tenure`;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
this.results.contributors = contributors.slice(0, 50);
|
|
740
|
+
} catch (err) {
|
|
741
|
+
console.error('Git contributor extraction failed:', err.message);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
636
745
|
/**
|
|
637
746
|
* Generate recommendations based on analysis
|
|
638
747
|
*/
|
|
@@ -823,6 +932,13 @@ class RepoAnalyzer {
|
|
|
823
932
|
lines.push(`\n✅ No security issues found in ${sec.filesScanned} files scanned`);
|
|
824
933
|
}
|
|
825
934
|
|
|
935
|
+
if (r.contributors && r.contributors.length > 0) {
|
|
936
|
+
lines.push(`\n👥 Contributors: ${r.contributors.length} found`);
|
|
937
|
+
for (const c of r.contributors.slice(0, 10)) {
|
|
938
|
+
lines.push(` - ${c.name} <${c.email}> (${c.total_commits} commits, ${c.tenure_months}mo, ${c.computed_maturity})`);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
826
942
|
if (r.recommendations.length) {
|
|
827
943
|
lines.push(`\n💡 Recommendations:`);
|
|
828
944
|
for (const rec of r.recommendations) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database operations shim for NPM client package.
|
|
3
|
+
* Core modules import dbOperations at the top level, but hooks never
|
|
4
|
+
* execute DB queries — they communicate via HTTP to the MindMeld API.
|
|
5
|
+
* This shim satisfies the require() without shipping pg or connection logic.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
async function executeQuery() {
|
|
9
|
+
throw new Error('Database operations are not available in the client package. Use the MindMeld API instead.');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function getPool() {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = { executeQuery, getPool };
|
package/src/core/AuthManager.js
CHANGED
|
@@ -32,6 +32,7 @@ class AuthManager {
|
|
|
32
32
|
region: options.cognitoRegion || 'us-east-2'
|
|
33
33
|
},
|
|
34
34
|
callbackPorts: options.callbackPorts || [9876, 9877, 9878, 9879, 9880],
|
|
35
|
+
// AEGIS review: path is config-driven (homedir + hardcoded), not HTTP user input — no traversal risk
|
|
35
36
|
authFile: options.authFile || path.join(os.homedir(), '.mindmeld', 'auth.json')
|
|
36
37
|
};
|
|
37
38
|
}
|
|
@@ -58,8 +59,8 @@ class AuthManager {
|
|
|
58
59
|
*/
|
|
59
60
|
async saveAuth(auth) {
|
|
60
61
|
const dir = path.dirname(this.config.authFile);
|
|
61
|
-
await fsPromises.mkdir(dir, { recursive: true });
|
|
62
|
-
await fsPromises.writeFile(this.config.authFile, JSON.stringify(auth, null, 2));
|
|
62
|
+
await fsPromises.mkdir(dir, { recursive: true, mode: 0o700 });
|
|
63
|
+
await fsPromises.writeFile(this.config.authFile, JSON.stringify(auth, null, 2), { mode: 0o600 });
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
/**
|
|
@@ -1,53 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Database
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* Follows lambda_database_standards.md
|
|
2
|
+
* Database operations shim for NPM client package.
|
|
3
|
+
* Core modules import dbOperations at the top level, but hooks never
|
|
4
|
+
* execute DB queries — they communicate via HTTP to the MindMeld API.
|
|
5
|
+
* This shim satisfies the require() without shipping pg or connection logic.
|
|
7
6
|
*/
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
// Cached client for connection reuse across warm invocations
|
|
12
|
-
let cachedClient = null;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Get database client (cached)
|
|
16
|
-
* Reuses connection across Lambda invocations
|
|
17
|
-
*/
|
|
18
|
-
async function getClient() {
|
|
19
|
-
if (cachedClient && cachedClient._connected) {
|
|
20
|
-
return cachedClient;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Create new client with environment variables (resolved at deployment)
|
|
24
|
-
// AWS RDS requires SSL for all connections
|
|
25
|
-
cachedClient = new Client({
|
|
26
|
-
host: process.env.DB_HOST,
|
|
27
|
-
port: parseInt(process.env.DB_PORT || '5432'),
|
|
28
|
-
database: process.env.DB_NAME,
|
|
29
|
-
user: process.env.DB_USER,
|
|
30
|
-
password: process.env.DB_PASS || process.env.DB_PASSWORD, // Support both naming conventions
|
|
31
|
-
ssl: { rejectUnauthorized: false } // Required for AWS RDS
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
await cachedClient.connect();
|
|
35
|
-
cachedClient._connected = true;
|
|
36
|
-
|
|
37
|
-
console.log('Database client connected');
|
|
38
|
-
|
|
39
|
-
return cachedClient;
|
|
8
|
+
async function executeQuery() {
|
|
9
|
+
throw new Error('Database operations are not available in the client package. Use the MindMeld API instead.');
|
|
40
10
|
}
|
|
41
11
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
* @param {string} query - SQL query
|
|
45
|
-
* @param {array} params - Query parameters
|
|
46
|
-
* @returns {Promise<object>} Query result
|
|
47
|
-
*/
|
|
48
|
-
async function executeQuery(query, params = []) {
|
|
49
|
-
const client = await getClient();
|
|
50
|
-
return client.query(query, params);
|
|
12
|
+
function getPool() {
|
|
13
|
+
return null;
|
|
51
14
|
}
|
|
52
15
|
|
|
53
|
-
module.exports = { executeQuery };
|
|
16
|
+
module.exports = { executeQuery, getPool };
|
package/src/index.js
CHANGED
|
@@ -4,16 +4,13 @@
|
|
|
4
4
|
* Intelligent standards injection for AI coding sessions.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const TeamLoadBearingDetector = require('./core/TeamLoadBearingDetector');
|
|
8
|
-
const CollaborationPrompt = require('./collaboration/CollaborationPrompt');
|
|
9
|
-
const { RelevanceDetector } = require('./core/RelevanceDetector');
|
|
10
7
|
const { PatternValidator } = require('./core/PatternValidator');
|
|
11
|
-
const { StandardsIngestion } = require('./core/StandardsIngestion');
|
|
12
8
|
const { AuthManager } = require('./core/AuthManager');
|
|
13
9
|
|
|
14
10
|
class MindmeldClient {
|
|
15
11
|
constructor(config = {}) {
|
|
16
12
|
this.config = {
|
|
13
|
+
// AEGIS review: projectPath is config-driven (CLI cwd), not HTTP user input — no traversal risk
|
|
17
14
|
projectPath: config.projectPath || process.cwd(),
|
|
18
15
|
userId: config.userId || process.env.USER || 'unknown',
|
|
19
16
|
apiUrl: config.apiUrl || process.env.MINDMELD_API_URL || 'https://api.mindmeld.dev',
|
|
@@ -23,19 +20,7 @@ class MindmeldClient {
|
|
|
23
20
|
...config
|
|
24
21
|
};
|
|
25
22
|
|
|
26
|
-
// Initialize components
|
|
27
|
-
this.teamDetector = new TeamLoadBearingDetector();
|
|
28
|
-
this.collaborationPrompt = new CollaborationPrompt();
|
|
29
|
-
|
|
30
|
-
// Phase 2-5 components (Claude Code integration)
|
|
31
|
-
this.relevanceDetector = new RelevanceDetector({
|
|
32
|
-
workingDirectory: this.config.projectPath,
|
|
33
|
-
standardsPath: this.config.standardsPath
|
|
34
|
-
});
|
|
35
23
|
this.patternValidator = new PatternValidator();
|
|
36
|
-
this.standardsIngestion = new StandardsIngestion({
|
|
37
|
-
standardsPath: this.config.standardsPath
|
|
38
|
-
});
|
|
39
24
|
|
|
40
25
|
// Project context
|
|
41
26
|
this.currentProject = null;
|
|
@@ -167,129 +152,6 @@ class MindmeldClient {
|
|
|
167
152
|
return null;
|
|
168
153
|
}
|
|
169
154
|
|
|
170
|
-
/**
|
|
171
|
-
* Initialize new project
|
|
172
|
-
* Creates project via API if authenticated, stores locally as fallback
|
|
173
|
-
* @param {string} projectName - Name of the project
|
|
174
|
-
* @param {Object} options - Project options
|
|
175
|
-
* @param {Array} options.collaborators - List of collaborator emails
|
|
176
|
-
* @param {boolean} options.private - Whether project is private
|
|
177
|
-
* @param {string} options.description - Project description
|
|
178
|
-
* @param {string} options.repoUrl - Repository URL
|
|
179
|
-
* @returns {Promise<Object>} Created project object
|
|
180
|
-
*/
|
|
181
|
-
async initializeProject(projectName, options = {}) {
|
|
182
|
-
const project = this.collaborationPrompt.createProjectConfig(
|
|
183
|
-
projectName,
|
|
184
|
-
options.collaborators || [],
|
|
185
|
-
{
|
|
186
|
-
private: options.private || false
|
|
187
|
-
}
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
// Try API first if authenticated
|
|
191
|
-
if (this.config.authToken && this.config.companyId) {
|
|
192
|
-
try {
|
|
193
|
-
const apiUrl = this.config.apiUrl;
|
|
194
|
-
|
|
195
|
-
// Build request payload matching projectCreate handler
|
|
196
|
-
const payload = {
|
|
197
|
-
Company_ID: this.config.companyId,
|
|
198
|
-
project_name: projectName,
|
|
199
|
-
description: options.description || `Project for ${projectName}`,
|
|
200
|
-
private: options.private || false,
|
|
201
|
-
repo_url: options.repoUrl || null
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
const response = await this._makeApiRequest(
|
|
205
|
-
`${apiUrl}/api/projects`,
|
|
206
|
-
'POST',
|
|
207
|
-
payload
|
|
208
|
-
);
|
|
209
|
-
|
|
210
|
-
if (response.error) {
|
|
211
|
-
// Handle specific error cases
|
|
212
|
-
if (response.status === 409) {
|
|
213
|
-
console.log('[Rapport] Project already exists, fetching existing...');
|
|
214
|
-
const existing = await this.getProject(projectName);
|
|
215
|
-
if (existing) return existing;
|
|
216
|
-
}
|
|
217
|
-
console.error('[Rapport] initializeProject API error:', response.error);
|
|
218
|
-
// Fall through to local storage
|
|
219
|
-
} else if (response.Records && response.Records.length > 0) {
|
|
220
|
-
const created = response.Records[0];
|
|
221
|
-
|
|
222
|
-
// Add collaborators if specified
|
|
223
|
-
if (options.collaborators && options.collaborators.length > 0) {
|
|
224
|
-
for (const email of options.collaborators) {
|
|
225
|
-
await this._addCollaborator(created.project_id, email);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return {
|
|
230
|
-
projectId: created.project_id,
|
|
231
|
-
projectName: created.project_name,
|
|
232
|
-
companyId: created.company_id,
|
|
233
|
-
description: created.description,
|
|
234
|
-
private: created.private,
|
|
235
|
-
collaborators: options.collaborators || [],
|
|
236
|
-
createdAt: created.created_at
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
} catch (error) {
|
|
240
|
-
console.error('[Rapport] initializeProject API call failed:', error.message);
|
|
241
|
-
// Fall through to local storage
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Fallback: store locally
|
|
246
|
-
await this.storeProjectLocally(project);
|
|
247
|
-
return project;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Add collaborator to project (internal helper)
|
|
252
|
-
* @private
|
|
253
|
-
*/
|
|
254
|
-
async _addCollaborator(projectId, email, role = 'collaborator') {
|
|
255
|
-
try {
|
|
256
|
-
const apiUrl = this.config.apiUrl;
|
|
257
|
-
await this._makeApiRequest(
|
|
258
|
-
`${apiUrl}/api/collaborators`,
|
|
259
|
-
'POST',
|
|
260
|
-
{ project_id: projectId, email, role }
|
|
261
|
-
);
|
|
262
|
-
} catch (error) {
|
|
263
|
-
console.error(`[Rapport] Failed to add collaborator ${email}:`, error.message);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Store project configuration locally
|
|
269
|
-
*/
|
|
270
|
-
async storeProjectLocally(project) {
|
|
271
|
-
const fs = require('fs').promises;
|
|
272
|
-
const path = require('path');
|
|
273
|
-
|
|
274
|
-
const projectDir = path.join(__dirname, '../projects', project.projectId);
|
|
275
|
-
await fs.mkdir(projectDir, { recursive: true });
|
|
276
|
-
|
|
277
|
-
const configPath = path.join(projectDir, 'config.json');
|
|
278
|
-
await fs.writeFile(configPath, JSON.stringify(project, null, 2));
|
|
279
|
-
|
|
280
|
-
// Create collaborators file
|
|
281
|
-
const collabPath = path.join(projectDir, 'collaborators.json');
|
|
282
|
-
await fs.writeFile(collabPath, JSON.stringify({
|
|
283
|
-
projectId: project.projectId,
|
|
284
|
-
collaborators: project.collaborators,
|
|
285
|
-
updated: new Date().toISOString()
|
|
286
|
-
}, null, 2));
|
|
287
|
-
|
|
288
|
-
// Create sessions directory
|
|
289
|
-
const sessionsDir = path.join(projectDir, 'sessions');
|
|
290
|
-
await fs.mkdir(sessionsDir, { recursive: true });
|
|
291
|
-
}
|
|
292
|
-
|
|
293
155
|
/**
|
|
294
156
|
* Load project context including team patterns, load-bearing elements, and recent learning
|
|
295
157
|
* Fetches from API if authenticated, falls back to local storage
|
|
@@ -404,79 +266,6 @@ class MindmeldClient {
|
|
|
404
266
|
}
|
|
405
267
|
}
|
|
406
268
|
|
|
407
|
-
/**
|
|
408
|
-
* Analyze handoff (team-wide)
|
|
409
|
-
*/
|
|
410
|
-
async analyzeHandoff(handoff, outcome) {
|
|
411
|
-
if (!this.currentProject) {
|
|
412
|
-
throw new Error('No project initialized. Call detectProject() first.');
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
return this.teamDetector.analyzeHandoff(
|
|
416
|
-
this.config.userId,
|
|
417
|
-
handoff,
|
|
418
|
-
outcome
|
|
419
|
-
);
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
/**
|
|
423
|
-
* Get team load-bearing elements
|
|
424
|
-
*/
|
|
425
|
-
getTeamLoadBearing() {
|
|
426
|
-
return this.teamDetector.getTeamLoadBearing();
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
/**
|
|
430
|
-
* Get team summary
|
|
431
|
-
*/
|
|
432
|
-
getTeamSummary() {
|
|
433
|
-
return this.teamDetector.getTeamSummary();
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
// ============================================================================
|
|
437
|
-
// Phase 5: Claude Code Hook Methods
|
|
438
|
-
// ============================================================================
|
|
439
|
-
|
|
440
|
-
/**
|
|
441
|
-
* Ensure standards are ingested (cached check)
|
|
442
|
-
* Fast execution for session-start hook
|
|
443
|
-
*/
|
|
444
|
-
async ensureStandardsIngested() {
|
|
445
|
-
try {
|
|
446
|
-
const check = await this.standardsIngestion.needsIngestion();
|
|
447
|
-
|
|
448
|
-
if (check.needed) {
|
|
449
|
-
console.log(`[Rapport] Standards ingestion needed: ${check.reason}`);
|
|
450
|
-
await this.standardsIngestion.ingestEquilateralStandards();
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
return check;
|
|
454
|
-
} catch (error) {
|
|
455
|
-
console.error('[Rapport] ensureStandardsIngested error:', error.message);
|
|
456
|
-
return { needed: false, error: error.message };
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
/**
|
|
461
|
-
* Get relevant standards for current project context
|
|
462
|
-
* Used by session-start hook
|
|
463
|
-
*/
|
|
464
|
-
async getRelevantStandards(projectContext) {
|
|
465
|
-
try {
|
|
466
|
-
const context = {
|
|
467
|
-
workingDirectory: projectContext.workingDirectory || this.config.projectPath,
|
|
468
|
-
currentFile: projectContext.currentFile || null,
|
|
469
|
-
recentFiles: projectContext.recentFiles || [],
|
|
470
|
-
gitHistory: projectContext.gitHistory || null
|
|
471
|
-
};
|
|
472
|
-
|
|
473
|
-
return await this.relevanceDetector.detectRelevantStandards(context);
|
|
474
|
-
} catch (error) {
|
|
475
|
-
console.error('[Rapport] getRelevantStandards error:', error.message);
|
|
476
|
-
return { standards: [], characteristics: {}, categories: [] };
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
269
|
/**
|
|
481
270
|
* Get recent learning for project (last N days)
|
|
482
271
|
*/
|
|
@@ -964,9 +753,5 @@ module.exports = {
|
|
|
964
753
|
MindmeldClient,
|
|
965
754
|
RapportClient,
|
|
966
755
|
AuthManager,
|
|
967
|
-
|
|
968
|
-
CollaborationPrompt,
|
|
969
|
-
RelevanceDetector,
|
|
970
|
-
PatternValidator,
|
|
971
|
-
StandardsIngestion
|
|
756
|
+
PatternValidator
|
|
972
757
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PII masking utilities for safe logging.
|
|
3
|
+
* Prevents email, user IDs from leaking to CloudWatch/log aggregators.
|
|
4
|
+
*/
|
|
5
|
+
const maskEmail = (email) => {
|
|
6
|
+
if (!email) return '[no-email]';
|
|
7
|
+
const [local, domain] = email.split('@');
|
|
8
|
+
return `${local.substring(0, 2)}***@${domain || '***'}`;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const maskUserId = (id) => {
|
|
12
|
+
if (!id) return '[no-id]';
|
|
13
|
+
return typeof id === 'string' && id.length > 8 ? `${id.substring(0, 8)}...` : String(id);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
module.exports = { maskEmail, maskUserId };
|