@equilateral_ai/mindmeld 3.5.3 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/hooks/session-start.js +312 -85
  2. package/package.json +21 -13
  3. package/scripts/init-project.js +9 -23
  4. package/scripts/repo-analyzer.js +118 -2
  5. package/src/client/dbShim.js +16 -0
  6. package/src/core/AuthManager.js +3 -2
  7. package/src/handlers/helpers/dbOperations.js +9 -46
  8. package/src/index.js +2 -217
  9. package/src/utils/piiMask.js +16 -0
  10. package/scripts/inject.js +0 -409
  11. package/scripts/mcp-bridge.js +0 -220
  12. package/scripts/standards.js +0 -285
  13. package/src/collaboration/CollaborationPrompt.js +0 -460
  14. package/src/core/AlertEngine.js +0 -813
  15. package/src/core/AlertNotifier.js +0 -363
  16. package/src/core/CorrelationAnalyzer.js +0 -931
  17. package/src/core/CrossReferenceEngine.js +0 -624
  18. package/src/core/CurationEngine.js +0 -688
  19. package/src/core/DeprecationScheduler.js +0 -183
  20. package/src/core/LoadBearingDetector.js +0 -242
  21. package/src/core/NotificationService.js +0 -1032
  22. package/src/core/RapportOrchestrator.js +0 -632
  23. package/src/core/RelevanceDetector.js +0 -694
  24. package/src/core/StandardLifecycle.js +0 -244
  25. package/src/core/StandardsIngestion.js +0 -991
  26. package/src/core/TeamLoadBearingDetector.js +0 -431
  27. package/src/core/parsers/adrParser.js +0 -479
  28. package/src/core/parsers/cursorRulesParser.js +0 -564
  29. package/src/core/parsers/eslintParser.js +0 -439
  30. package/src/database/dbOperations.js +0 -105
  31. package/src/handlers/activity/activityGetMe.js +0 -98
  32. package/src/handlers/activity/activityGetTeam.js +0 -175
  33. package/src/handlers/admin/adminSetup.js +0 -216
  34. package/src/handlers/alerts/alertsAcknowledge.js +0 -92
  35. package/src/handlers/alerts/alertsGet.js +0 -250
  36. package/src/handlers/analytics/activitySummaryGet.js +0 -234
  37. package/src/handlers/analytics/coachingGet.js +0 -361
  38. package/src/handlers/analytics/convergenceGet.js +0 -236
  39. package/src/handlers/analytics/developerScoreGet.js +0 -137
  40. package/src/handlers/collaborators/collaboratorAdd.js +0 -200
  41. package/src/handlers/collaborators/collaboratorInvite.js +0 -219
  42. package/src/handlers/collaborators/collaboratorList.js +0 -82
  43. package/src/handlers/collaborators/collaboratorRemove.js +0 -128
  44. package/src/handlers/collaborators/inviteAccept.js +0 -122
  45. package/src/handlers/company/companyUsersDelete.js +0 -141
  46. package/src/handlers/company/companyUsersGet.js +0 -90
  47. package/src/handlers/company/companyUsersPost.js +0 -267
  48. package/src/handlers/company/companyUsersPut.js +0 -76
  49. package/src/handlers/context/contextGet.js +0 -57
  50. package/src/handlers/context/invariantsGet.js +0 -74
  51. package/src/handlers/context/loopsGet.js +0 -82
  52. package/src/handlers/context/notesCreate.js +0 -74
  53. package/src/handlers/context/purposeGet.js +0 -78
  54. package/src/handlers/correlations/correlationsDeveloperGet.js +0 -227
  55. package/src/handlers/correlations/correlationsGet.js +0 -93
  56. package/src/handlers/correlations/correlationsProjectGet.js +0 -153
  57. package/src/handlers/enterprise/controlTowerGet.js +0 -224
  58. package/src/handlers/enterprise/enterpriseAuditGet.js +0 -108
  59. package/src/handlers/enterprise/enterpriseContributorsGet.js +0 -85
  60. package/src/handlers/enterprise/enterpriseKnowledgeCategoriesGet.js +0 -53
  61. package/src/handlers/enterprise/enterpriseKnowledgeCreate.js +0 -77
  62. package/src/handlers/enterprise/enterpriseKnowledgeDelete.js +0 -71
  63. package/src/handlers/enterprise/enterpriseKnowledgeGet.js +0 -87
  64. package/src/handlers/enterprise/enterpriseKnowledgeUpdate.js +0 -122
  65. package/src/handlers/enterprise/enterpriseOnboardingComplete.js +0 -77
  66. package/src/handlers/enterprise/enterpriseOnboardingInvite.js +0 -138
  67. package/src/handlers/enterprise/enterpriseOnboardingSetup.js +0 -128
  68. package/src/handlers/enterprise/enterpriseOnboardingStatus.js +0 -88
  69. package/src/handlers/github/githubConnectionStatus.js +0 -49
  70. package/src/handlers/github/githubDiscoverPatterns.js +0 -621
  71. package/src/handlers/github/githubOAuthCallback.js +0 -178
  72. package/src/handlers/github/githubOAuthStart.js +0 -59
  73. package/src/handlers/github/githubPatternsReview.js +0 -76
  74. package/src/handlers/github/githubReposList.js +0 -105
  75. package/src/handlers/health/healthGet.js +0 -55
  76. package/src/handlers/helpers/auditLogger.js +0 -201
  77. package/src/handlers/helpers/checkSuperAdmin.js +0 -84
  78. package/src/handlers/helpers/decisionFrames.js +0 -29
  79. package/src/handlers/helpers/errorHandler.js +0 -49
  80. package/src/handlers/helpers/index.js +0 -138
  81. package/src/handlers/helpers/lambdaWrapper.js +0 -60
  82. package/src/handlers/helpers/mindmeldMcpCore.js +0 -1103
  83. package/src/handlers/helpers/predictiveCache.js +0 -51
  84. package/src/handlers/helpers/projectAccess.js +0 -88
  85. package/src/handlers/helpers/responseUtil.js +0 -55
  86. package/src/handlers/helpers/subscriptionTiers.js +0 -1168
  87. package/src/handlers/mcp/mcpHandler.js +0 -569
  88. package/src/handlers/mcp/mindmeldMcpHandler.js +0 -124
  89. package/src/handlers/mcp/mindmeldMcpStreamHandler.js +0 -342
  90. package/src/handlers/notifications/getPreferences.js +0 -84
  91. package/src/handlers/notifications/sendNotification.js +0 -170
  92. package/src/handlers/notifications/updatePreferences.js +0 -316
  93. package/src/handlers/patterns/patternEvaluatePromotionPost.js +0 -173
  94. package/src/handlers/patterns/patternUsagePost.js +0 -182
  95. package/src/handlers/patterns/patternViolationPost.js +0 -185
  96. package/src/handlers/projects/projectCreate.js +0 -248
  97. package/src/handlers/projects/projectDelete.js +0 -82
  98. package/src/handlers/projects/projectGet.js +0 -95
  99. package/src/handlers/projects/projectUpdate.js +0 -117
  100. package/src/handlers/reports/aiLeverage.js +0 -210
  101. package/src/handlers/reports/engineeringInvestment.js +0 -132
  102. package/src/handlers/reports/riskForecast.js +0 -206
  103. package/src/handlers/reports/standardsRoi.js +0 -254
  104. package/src/handlers/scheduled/analyzeCorrelations.js +0 -178
  105. package/src/handlers/scheduled/analyzeGitHistory.js +0 -510
  106. package/src/handlers/scheduled/generateAlerts.js +0 -135
  107. package/src/handlers/scheduled/maturityUpdateJob.js +0 -166
  108. package/src/handlers/scheduled/refreshActivity.js +0 -21
  109. package/src/handlers/scheduled/scanCompliance.js +0 -334
  110. package/src/handlers/sessions/sessionEndPost.js +0 -180
  111. package/src/handlers/sessions/sessionStandardsPost.js +0 -171
  112. package/src/handlers/standards/catalogGet.js +0 -185
  113. package/src/handlers/standards/catalogSync.js +0 -120
  114. package/src/handlers/standards/discoveriesGet.js +0 -89
  115. package/src/handlers/standards/projectStandardsGet.js +0 -129
  116. package/src/handlers/standards/projectStandardsPut.js +0 -151
  117. package/src/handlers/standards/standardsAuditGet.js +0 -65
  118. package/src/handlers/standards/standardsParseUpload.js +0 -149
  119. package/src/handlers/standards/standardsRelevantPost.js +0 -405
  120. package/src/handlers/standards/standardsTransition.js +0 -161
  121. package/src/handlers/stripe/addonManagePost.js +0 -240
  122. package/src/handlers/stripe/billingPortalPost.js +0 -93
  123. package/src/handlers/stripe/enterpriseCheckoutPost.js +0 -272
  124. package/src/handlers/stripe/seatsUpdatePost.js +0 -185
  125. package/src/handlers/stripe/subscriptionCancelDelete.js +0 -169
  126. package/src/handlers/stripe/subscriptionCreatePost.js +0 -221
  127. package/src/handlers/stripe/subscriptionUpdatePut.js +0 -163
  128. package/src/handlers/stripe/webhookPost.js +0 -482
  129. package/src/handlers/user/apiTokenCreate.js +0 -71
  130. package/src/handlers/user/apiTokenList.js +0 -64
  131. package/src/handlers/user/userSplashAck.js +0 -91
  132. package/src/handlers/user/userSplashGet.js +0 -211
  133. package/src/handlers/users/cognitoPostConfirmation.js +0 -186
  134. package/src/handlers/users/cognitoPreSignUp.js +0 -114
  135. package/src/handlers/users/userEntitlementsGet.js +0 -89
  136. package/src/handlers/users/userGet.js +0 -118
  137. package/src/handlers/users/userProfilePut.js +0 -77
  138. package/src/handlers/webhooks/githubWebhook.js +0 -215
@@ -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. Generate recommendations
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 };
@@ -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 Operations Helper
3
- * Cached PostgreSQL client (Lambda database standards compliant)
4
- *
5
- * CRITICAL: Uses single cached client (NOT connection pool)
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
- const { Client } = require('pg');
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
- * Execute query using cached client
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
- TeamLoadBearingDetector,
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 };