@equilateral_ai/mindmeld 3.5.2 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/hooks/session-end.js +25 -0
  2. package/hooks/session-start.js +363 -83
  3. package/hooks/session-watcher.js +585 -0
  4. package/package.json +19 -13
  5. package/scripts/init-project.js +9 -23
  6. package/src/client/dbShim.js +16 -0
  7. package/src/core/AuthManager.js +3 -2
  8. package/src/handlers/helpers/dbOperations.js +9 -46
  9. package/src/index.js +2 -217
  10. package/src/utils/piiMask.js +16 -0
  11. package/scripts/harvest.js +0 -601
  12. package/scripts/inject.js +0 -409
  13. package/scripts/mcp-bridge.js +0 -220
  14. package/scripts/repo-analyzer.js +0 -870
  15. package/src/collaboration/CollaborationPrompt.js +0 -460
  16. package/src/core/AlertEngine.js +0 -813
  17. package/src/core/AlertNotifier.js +0 -363
  18. package/src/core/CorrelationAnalyzer.js +0 -931
  19. package/src/core/CrossReferenceEngine.js +0 -624
  20. package/src/core/CurationEngine.js +0 -688
  21. package/src/core/DeprecationScheduler.js +0 -183
  22. package/src/core/LoadBearingDetector.js +0 -242
  23. package/src/core/NotificationService.js +0 -1032
  24. package/src/core/RapportOrchestrator.js +0 -632
  25. package/src/core/RelevanceDetector.js +0 -694
  26. package/src/core/StandardLifecycle.js +0 -244
  27. package/src/core/StandardsIngestion.js +0 -991
  28. package/src/core/TeamLoadBearingDetector.js +0 -431
  29. package/src/core/parsers/adrParser.js +0 -479
  30. package/src/core/parsers/cursorRulesParser.js +0 -564
  31. package/src/core/parsers/eslintParser.js +0 -439
  32. package/src/database/dbOperations.js +0 -105
  33. package/src/handlers/activity/activityGetMe.js +0 -98
  34. package/src/handlers/activity/activityGetTeam.js +0 -175
  35. package/src/handlers/admin/adminSetup.js +0 -216
  36. package/src/handlers/alerts/alertsAcknowledge.js +0 -92
  37. package/src/handlers/alerts/alertsGet.js +0 -250
  38. package/src/handlers/analytics/activitySummaryGet.js +0 -234
  39. package/src/handlers/analytics/coachingGet.js +0 -361
  40. package/src/handlers/analytics/convergenceGet.js +0 -236
  41. package/src/handlers/analytics/developerScoreGet.js +0 -137
  42. package/src/handlers/collaborators/collaboratorAdd.js +0 -200
  43. package/src/handlers/collaborators/collaboratorInvite.js +0 -219
  44. package/src/handlers/collaborators/collaboratorList.js +0 -82
  45. package/src/handlers/collaborators/collaboratorRemove.js +0 -128
  46. package/src/handlers/collaborators/inviteAccept.js +0 -122
  47. package/src/handlers/company/companyUsersDelete.js +0 -141
  48. package/src/handlers/company/companyUsersGet.js +0 -90
  49. package/src/handlers/company/companyUsersPost.js +0 -267
  50. package/src/handlers/company/companyUsersPut.js +0 -76
  51. package/src/handlers/context/contextGet.js +0 -57
  52. package/src/handlers/context/invariantsGet.js +0 -74
  53. package/src/handlers/context/loopsGet.js +0 -82
  54. package/src/handlers/context/notesCreate.js +0 -74
  55. package/src/handlers/context/purposeGet.js +0 -78
  56. package/src/handlers/correlations/correlationsDeveloperGet.js +0 -227
  57. package/src/handlers/correlations/correlationsGet.js +0 -93
  58. package/src/handlers/correlations/correlationsProjectGet.js +0 -153
  59. package/src/handlers/enterprise/controlTowerGet.js +0 -224
  60. package/src/handlers/enterprise/enterpriseAuditGet.js +0 -108
  61. package/src/handlers/enterprise/enterpriseContributorsGet.js +0 -85
  62. package/src/handlers/enterprise/enterpriseKnowledgeCategoriesGet.js +0 -53
  63. package/src/handlers/enterprise/enterpriseKnowledgeCreate.js +0 -77
  64. package/src/handlers/enterprise/enterpriseKnowledgeDelete.js +0 -71
  65. package/src/handlers/enterprise/enterpriseKnowledgeGet.js +0 -87
  66. package/src/handlers/enterprise/enterpriseKnowledgeUpdate.js +0 -122
  67. package/src/handlers/enterprise/enterpriseOnboardingComplete.js +0 -77
  68. package/src/handlers/enterprise/enterpriseOnboardingInvite.js +0 -138
  69. package/src/handlers/enterprise/enterpriseOnboardingSetup.js +0 -128
  70. package/src/handlers/enterprise/enterpriseOnboardingStatus.js +0 -88
  71. package/src/handlers/github/githubConnectionStatus.js +0 -49
  72. package/src/handlers/github/githubDiscoverPatterns.js +0 -621
  73. package/src/handlers/github/githubOAuthCallback.js +0 -178
  74. package/src/handlers/github/githubOAuthStart.js +0 -59
  75. package/src/handlers/github/githubPatternsReview.js +0 -76
  76. package/src/handlers/github/githubReposList.js +0 -105
  77. package/src/handlers/health/healthGet.js +0 -55
  78. package/src/handlers/helpers/auditLogger.js +0 -201
  79. package/src/handlers/helpers/checkSuperAdmin.js +0 -84
  80. package/src/handlers/helpers/decisionFrames.js +0 -29
  81. package/src/handlers/helpers/errorHandler.js +0 -49
  82. package/src/handlers/helpers/index.js +0 -138
  83. package/src/handlers/helpers/lambdaWrapper.js +0 -60
  84. package/src/handlers/helpers/mindmeldMcpCore.js +0 -1103
  85. package/src/handlers/helpers/predictiveCache.js +0 -51
  86. package/src/handlers/helpers/projectAccess.js +0 -88
  87. package/src/handlers/helpers/responseUtil.js +0 -55
  88. package/src/handlers/helpers/subscriptionTiers.js +0 -1168
  89. package/src/handlers/mcp/mcpHandler.js +0 -569
  90. package/src/handlers/mcp/mindmeldMcpHandler.js +0 -124
  91. package/src/handlers/mcp/mindmeldMcpStreamHandler.js +0 -342
  92. package/src/handlers/notifications/getPreferences.js +0 -84
  93. package/src/handlers/notifications/sendNotification.js +0 -170
  94. package/src/handlers/notifications/updatePreferences.js +0 -316
  95. package/src/handlers/patterns/patternEvaluatePromotionPost.js +0 -173
  96. package/src/handlers/patterns/patternUsagePost.js +0 -182
  97. package/src/handlers/patterns/patternViolationPost.js +0 -185
  98. package/src/handlers/projects/projectCreate.js +0 -248
  99. package/src/handlers/projects/projectDelete.js +0 -82
  100. package/src/handlers/projects/projectGet.js +0 -95
  101. package/src/handlers/projects/projectUpdate.js +0 -117
  102. package/src/handlers/reports/aiLeverage.js +0 -210
  103. package/src/handlers/reports/engineeringInvestment.js +0 -132
  104. package/src/handlers/reports/riskForecast.js +0 -206
  105. package/src/handlers/reports/standardsRoi.js +0 -254
  106. package/src/handlers/scheduled/analyzeCorrelations.js +0 -178
  107. package/src/handlers/scheduled/analyzeGitHistory.js +0 -510
  108. package/src/handlers/scheduled/generateAlerts.js +0 -135
  109. package/src/handlers/scheduled/maturityUpdateJob.js +0 -166
  110. package/src/handlers/scheduled/refreshActivity.js +0 -21
  111. package/src/handlers/scheduled/scanCompliance.js +0 -334
  112. package/src/handlers/sessions/sessionEndPost.js +0 -180
  113. package/src/handlers/sessions/sessionStandardsPost.js +0 -171
  114. package/src/handlers/standards/catalogGet.js +0 -185
  115. package/src/handlers/standards/catalogSync.js +0 -120
  116. package/src/handlers/standards/discoveriesGet.js +0 -89
  117. package/src/handlers/standards/projectStandardsGet.js +0 -129
  118. package/src/handlers/standards/projectStandardsPut.js +0 -151
  119. package/src/handlers/standards/standardsAuditGet.js +0 -65
  120. package/src/handlers/standards/standardsParseUpload.js +0 -149
  121. package/src/handlers/standards/standardsRelevantPost.js +0 -405
  122. package/src/handlers/standards/standardsTransition.js +0 -161
  123. package/src/handlers/stripe/addonManagePost.js +0 -240
  124. package/src/handlers/stripe/billingPortalPost.js +0 -93
  125. package/src/handlers/stripe/enterpriseCheckoutPost.js +0 -272
  126. package/src/handlers/stripe/seatsUpdatePost.js +0 -185
  127. package/src/handlers/stripe/subscriptionCancelDelete.js +0 -169
  128. package/src/handlers/stripe/subscriptionCreatePost.js +0 -221
  129. package/src/handlers/stripe/subscriptionUpdatePut.js +0 -163
  130. package/src/handlers/stripe/webhookPost.js +0 -482
  131. package/src/handlers/user/apiTokenCreate.js +0 -71
  132. package/src/handlers/user/apiTokenList.js +0 -64
  133. package/src/handlers/user/userSplashAck.js +0 -91
  134. package/src/handlers/user/userSplashGet.js +0 -211
  135. package/src/handlers/users/cognitoPostConfirmation.js +0 -186
  136. package/src/handlers/users/cognitoPreSignUp.js +0 -114
  137. package/src/handlers/users/userEntitlementsGet.js +0 -89
  138. package/src/handlers/users/userGet.js +0 -118
  139. package/src/handlers/users/userProfilePut.js +0 -77
  140. package/src/handlers/webhooks/githubWebhook.js +0 -215
@@ -1,870 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * MindMeld Repository Analyzer
5
- *
6
- * Rich repository analysis for CLI onboarding. Detects:
7
- * - Technology stack (languages, frameworks, build systems)
8
- * - Project structure and architecture patterns
9
- * - Security anti-patterns (linked to remediation standards)
10
- * - Existing documentation and standards
11
- *
12
- * Based on EquilateralAgents analysis patterns.
13
- */
14
-
15
- const fs = require('fs').promises;
16
- const path = require('path');
17
-
18
- // ============================================================================
19
- // SECURITY ANTI-PATTERNS (from securityScanner.js)
20
- // Each links to relevant standards for remediation
21
- // ============================================================================
22
-
23
- const SECURITY_PATTERNS = {
24
- critical: [
25
- {
26
- name: 'Hardcoded AWS Credentials',
27
- pattern: /(?:AWS_ACCESS_KEY_ID|AWS_SECRET_ACCESS_KEY)\s*=\s*["'][A-Z0-9]{20,}["']/gi,
28
- description: 'AWS credentials hardcoded in source code',
29
- recommendation: 'Use environment variables or AWS Secrets Manager',
30
- cwe: 'CWE-798',
31
- standardsLink: 'serverless-saas-aws/security/secrets_management.md'
32
- },
33
- {
34
- name: 'Hardcoded API Keys',
35
- pattern: /(?:api[_-]?key|apikey|api[_-]?secret)\s*=\s*["'][a-zA-Z0-9_\-]{20,}["']/gi,
36
- description: 'API keys hardcoded in source code',
37
- recommendation: 'Store API keys in environment variables or secure secret management',
38
- cwe: 'CWE-798',
39
- standardsLink: 'serverless-saas-aws/security/secrets_management.md'
40
- },
41
- {
42
- name: 'Hardcoded Private Keys',
43
- pattern: /-----BEGIN (?:RSA |EC )?PRIVATE KEY-----/gi,
44
- description: 'Private cryptographic key found in source code',
45
- recommendation: 'Never commit private keys. Use secure key management',
46
- cwe: 'CWE-798',
47
- standardsLink: 'serverless-saas-aws/security/secrets_management.md'
48
- },
49
- {
50
- name: 'SQL Injection Risk',
51
- pattern: /(?:execute|query)\s*\(\s*["'](?:SELECT|INSERT|UPDATE|DELETE)[^"']*["']\s*\+/gi,
52
- description: 'SQL query using string concatenation',
53
- recommendation: 'Use parameterized queries or prepared statements',
54
- cwe: 'CWE-89',
55
- standardsLink: 'serverless-saas-aws/backend_handler_standards.md'
56
- },
57
- {
58
- name: 'Command Injection Risk',
59
- pattern: /(?:exec|spawn|system)\s*\([^)]*\$\{[^}]+\}[^)]*\)/gi,
60
- description: 'Command execution with template literal interpolation',
61
- recommendation: 'Validate and sanitize all input before command execution',
62
- cwe: 'CWE-78',
63
- standardsLink: 'serverless-saas-aws/security/input_validation.md'
64
- }
65
- ],
66
- high: [
67
- {
68
- name: 'Hardcoded Passwords',
69
- pattern: /(?:password|passwd|pwd)\s*=\s*["'][^"']{8,}["']/gi,
70
- description: 'Password hardcoded in source code',
71
- recommendation: 'Use environment variables or secure credential management',
72
- cwe: 'CWE-798',
73
- standardsLink: 'serverless-saas-aws/security/secrets_management.md'
74
- },
75
- {
76
- name: 'Weak Hash - MD5',
77
- pattern: /createHash\s*\(\s*["']md5["']\s*\)/gi,
78
- description: 'MD5 hash algorithm used (cryptographically broken)',
79
- recommendation: 'Use SHA-256 or stronger for security-critical operations',
80
- cwe: 'CWE-327',
81
- standardsLink: 'serverless-saas-aws/security/cryptography.md'
82
- },
83
- {
84
- name: 'Weak Hash - SHA1',
85
- pattern: /createHash\s*\(\s*["']sha1["']\s*\)/gi,
86
- description: 'SHA1 hash algorithm used (cryptographically weak)',
87
- recommendation: 'Use SHA-256 or stronger for security-critical operations',
88
- cwe: 'CWE-327',
89
- standardsLink: 'serverless-saas-aws/security/cryptography.md'
90
- },
91
- {
92
- name: 'Insecure Random',
93
- pattern: /Math\.random\(\)/gi,
94
- description: 'Math.random() used (not cryptographically secure)',
95
- recommendation: 'Use crypto.randomBytes() for security-sensitive operations',
96
- cwe: 'CWE-338',
97
- standardsLink: 'serverless-saas-aws/security/cryptography.md'
98
- },
99
- {
100
- name: 'Path Traversal Risk',
101
- pattern: /(?:readFile|writeFile|unlink|rmdir)\s*\([^)]*\.\.[\/\\]/gi,
102
- description: 'Potential path traversal vulnerability',
103
- recommendation: 'Validate paths with path.normalize() and check bounds',
104
- cwe: 'CWE-22',
105
- standardsLink: 'serverless-saas-aws/security/input_validation.md'
106
- }
107
- ],
108
- medium: [
109
- {
110
- name: 'eval() Usage',
111
- pattern: /\beval\s*\(/gi,
112
- description: 'eval() can execute arbitrary code',
113
- recommendation: 'Avoid eval(). Use JSON.parse() for JSON or safer alternatives',
114
- cwe: 'CWE-95',
115
- standardsLink: 'serverless-saas-aws/security/input_validation.md'
116
- },
117
- {
118
- name: 'Insecure Deserialization',
119
- pattern: /(?:unserialize|pickle\.loads|yaml\.load\()/gi,
120
- description: 'Insecure deserialization pattern',
121
- recommendation: 'Use safe deserialization methods and validate input',
122
- cwe: 'CWE-502',
123
- standardsLink: 'serverless-saas-aws/security/input_validation.md'
124
- },
125
- {
126
- name: 'HTTP instead of HTTPS',
127
- pattern: /["']http:\/\/(?!localhost|127\.0\.0\.1)/gi,
128
- description: 'HTTP URL found (unencrypted communication)',
129
- recommendation: 'Use HTTPS for all external communications',
130
- cwe: 'CWE-319',
131
- standardsLink: 'serverless-saas-aws/security/transport.md'
132
- },
133
- {
134
- name: 'Disabled Certificate Validation',
135
- pattern: /rejectUnauthorized\s*:\s*false/gi,
136
- description: 'SSL/TLS certificate validation disabled',
137
- recommendation: 'Enable certificate validation for HTTPS connections',
138
- cwe: 'CWE-295',
139
- standardsLink: 'serverless-saas-aws/security/transport.md'
140
- }
141
- ],
142
- low: [
143
- {
144
- name: 'Console.log in Production',
145
- pattern: /console\.(log|debug|info)\(/gi,
146
- description: 'Console logging may expose sensitive information',
147
- recommendation: 'Use proper logging frameworks, avoid logging sensitive data',
148
- cwe: 'CWE-532',
149
- standardsLink: 'serverless-saas-aws/logging_standards.md'
150
- },
151
- {
152
- name: 'Security TODO Comments',
153
- pattern: /\/\/\s*(?:TODO|FIXME).*(?:security|password|key|token|auth)/gi,
154
- description: 'Security-related TODO/FIXME comment found',
155
- recommendation: 'Address security TODOs before production deployment',
156
- cwe: 'CWE-1188',
157
- standardsLink: null
158
- }
159
- ]
160
- };
161
-
162
- // ============================================================================
163
- // TECHNOLOGY DETECTION (from PatternHarvestingAgent.js)
164
- // ============================================================================
165
-
166
- const TECH_SIGNATURES = {
167
- // Languages
168
- javascript: {
169
- extensions: ['.js', '.mjs', '.cjs'],
170
- configFiles: ['package.json', 'jsconfig.json'],
171
- frameworks: {
172
- react: { indicators: ['react', 'react-dom'], configFiles: [] },
173
- vue: { indicators: ['vue'], configFiles: ['vue.config.js'] },
174
- angular: { indicators: ['@angular/core'], configFiles: ['angular.json'] },
175
- express: { indicators: ['express'], configFiles: [] },
176
- nest: { indicators: ['@nestjs/core'], configFiles: ['nest-cli.json'] },
177
- next: { indicators: ['next'], configFiles: ['next.config.js', 'next.config.mjs'] },
178
- gatsby: { indicators: ['gatsby'], configFiles: ['gatsby-config.js'] }
179
- }
180
- },
181
- typescript: {
182
- extensions: ['.ts', '.tsx'],
183
- configFiles: ['tsconfig.json'],
184
- frameworks: {} // Same as JS
185
- },
186
- python: {
187
- extensions: ['.py'],
188
- configFiles: ['requirements.txt', 'pyproject.toml', 'setup.py', 'Pipfile'],
189
- frameworks: {
190
- django: { indicators: ['django'], configFiles: ['manage.py'] },
191
- flask: { indicators: ['flask'], configFiles: [] },
192
- fastapi: { indicators: ['fastapi'], configFiles: [] }
193
- }
194
- },
195
- java: {
196
- extensions: ['.java'],
197
- configFiles: ['pom.xml', 'build.gradle', 'build.gradle.kts'],
198
- frameworks: {
199
- spring: { indicators: ['springframework', 'spring-boot'], configFiles: [] }
200
- }
201
- },
202
- csharp: {
203
- extensions: ['.cs'],
204
- configFiles: ['*.csproj', '*.sln'],
205
- frameworks: {
206
- aspnet: { indicators: ['Microsoft.AspNetCore'], configFiles: [] }
207
- }
208
- },
209
- go: {
210
- extensions: ['.go'],
211
- configFiles: ['go.mod', 'go.sum'],
212
- frameworks: {}
213
- },
214
- rust: {
215
- extensions: ['.rs'],
216
- configFiles: ['Cargo.toml'],
217
- frameworks: {}
218
- }
219
- };
220
-
221
- // Build systems and infrastructure
222
- const INFRASTRUCTURE_SIGNATURES = {
223
- docker: {
224
- configFiles: ['Dockerfile', 'docker-compose.yml', 'docker-compose.yaml', '.dockerignore']
225
- },
226
- kubernetes: {
227
- configFiles: ['*.yaml', '*.yml'],
228
- patterns: [/kind:\s*(?:Deployment|Service|Pod|ConfigMap)/]
229
- },
230
- terraform: {
231
- configFiles: ['*.tf', 'terraform.tfvars']
232
- },
233
- sam: {
234
- configFiles: ['template.yaml', 'template.yml', 'samconfig.toml']
235
- },
236
- serverless: {
237
- configFiles: ['serverless.yml', 'serverless.yaml']
238
- },
239
- cloudformation: {
240
- configFiles: ['cloudformation.yaml', 'cloudformation.yml', 'cfn-*.yaml']
241
- }
242
- };
243
-
244
- // ============================================================================
245
- // PROJECT STRUCTURE PATTERNS (from ProjectStructureDiscoveryAgent.js)
246
- // ============================================================================
247
-
248
- const DIRECTORY_PATTERNS = {
249
- backend: ['src/backend', 'backend', 'server', 'api', 'src/server', 'lambda', 'functions'],
250
- handlers: ['src/handlers', 'handlers', 'api/handlers', 'lambda/handlers', 'functions'],
251
- helpers: ['src/helpers', 'helpers', 'utils', 'lib', 'common', 'shared'],
252
- frontend: ['src/frontend', 'frontend', 'client', 'web', 'ui', 'app', 'src/pages', 'src/components'],
253
- database: ['database', 'db', 'migrations', 'prisma', 'sql', 'schemas'],
254
- tests: ['tests', 'test', '__tests__', 'spec', 'e2e', 'integration'],
255
- deployment: ['deployment', 'deploy', 'infrastructure', '.aws-sam', 'cloudformation', 'terraform', 'k8s'],
256
- standards: ['.equilateral-standards', '.clinerules', 'standards', '.standards'],
257
- docs: ['docs', 'documentation', 'doc']
258
- };
259
-
260
- // ============================================================================
261
- // DOCUMENTATION PATTERNS (from LibrarianAgent.js)
262
- // ============================================================================
263
-
264
- const DOC_PATTERNS = {
265
- readme: {
266
- patterns: [/^readme/i, /^README/],
267
- description: 'Project README'
268
- },
269
- adr: {
270
- patterns: [/adr[-_]?\d+/i, /decision[-_]record/i, /architecture[-_]decision/i],
271
- description: 'Architecture Decision Records'
272
- },
273
- standards: {
274
- patterns: [/standards/i, /guidelines/i, /conventions/i, /coding[-_]style/i],
275
- description: 'Coding standards/guidelines'
276
- },
277
- api: {
278
- patterns: [/^api/i, /swagger/i, /openapi/i],
279
- description: 'API documentation'
280
- },
281
- contributing: {
282
- patterns: [/contributing/i, /contribution/i],
283
- description: 'Contribution guidelines'
284
- },
285
- changelog: {
286
- patterns: [/changelog/i, /history/i, /releases/i],
287
- description: 'Change log'
288
- }
289
- };
290
-
291
- // ============================================================================
292
- // SCANNABLE FILES CONFIG
293
- // ============================================================================
294
-
295
- const SCANNABLE_EXTENSIONS = [
296
- '.js', '.jsx', '.ts', '.tsx', '.py', '.java', '.go', '.rb', '.php',
297
- '.cpp', '.c', '.h', '.cs', '.swift', '.kt', '.rs', '.scala'
298
- ];
299
-
300
- const SKIP_DIRECTORIES = [
301
- 'node_modules', '.git', 'dist', 'build', 'coverage', '.next',
302
- 'target', 'vendor', '__pycache__', '.gradle', 'bin', 'obj',
303
- '.aws-sam', '.serverless', '.terraform'
304
- ];
305
-
306
- // ============================================================================
307
- // ANALYZER CLASS
308
- // ============================================================================
309
-
310
- class RepoAnalyzer {
311
- constructor(projectPath) {
312
- this.projectPath = projectPath;
313
- this.results = {
314
- timestamp: new Date().toISOString(),
315
- projectPath,
316
- projectName: path.basename(projectPath),
317
- techStack: {
318
- languages: [],
319
- frameworks: [],
320
- buildSystems: [],
321
- infrastructure: []
322
- },
323
- structure: {
324
- type: 'unknown',
325
- directories: {},
326
- hasTests: false,
327
- hasDocs: false,
328
- hasStandards: false
329
- },
330
- documentation: [],
331
- securityFindings: {
332
- critical: [],
333
- high: [],
334
- medium: [],
335
- low: [],
336
- summary: {}
337
- },
338
- recommendations: []
339
- };
340
- }
341
-
342
- /**
343
- * Run full repository analysis
344
- */
345
- async analyze(options = {}) {
346
- const {
347
- scanSecurity = true,
348
- maxFiles = 500,
349
- verbose = false
350
- } = options;
351
-
352
- console.log(`\nšŸ” Analyzing repository: ${this.results.projectName}`);
353
- console.log('─'.repeat(50));
354
-
355
- try {
356
- // 1. Detect technology stack
357
- if (verbose) console.log('\nšŸ“Š Detecting technology stack...');
358
- await this.detectTechStack();
359
-
360
- // 2. Analyze project structure
361
- if (verbose) console.log('šŸ“ Analyzing project structure...');
362
- await this.analyzeStructure();
363
-
364
- // 3. Discover documentation
365
- if (verbose) console.log('šŸ“š Discovering documentation...');
366
- await this.discoverDocumentation();
367
-
368
- // 4. Scan for security anti-patterns
369
- if (scanSecurity) {
370
- if (verbose) console.log('šŸ”’ Scanning for security anti-patterns...');
371
- await this.scanSecurity(maxFiles);
372
- }
373
-
374
- // 5. Generate recommendations
375
- if (verbose) console.log('šŸ’” Generating recommendations...');
376
- this.generateRecommendations();
377
-
378
- return this.results;
379
- } catch (error) {
380
- // Preserve full error context for debugging
381
- this.results.error = {
382
- message: error.message,
383
- code: error.code || 'UNKNOWN',
384
- phase: 'analysis'
385
- };
386
- console.error('Analysis error:', error.message);
387
- return this.results;
388
- }
389
- }
390
-
391
- /**
392
- * Detect languages, frameworks, and build systems
393
- */
394
- async detectTechStack() {
395
- const files = await this.getAllFiles(this.projectPath, 2); // Shallow scan first
396
- const fileCounts = {};
397
- const detectedFrameworks = new Set();
398
- const detectedInfra = new Set();
399
-
400
- // Count files by extension
401
- for (const file of files) {
402
- const ext = path.extname(file).toLowerCase();
403
- fileCounts[ext] = (fileCounts[ext] || 0) + 1;
404
- }
405
-
406
- // Detect languages
407
- for (const [language, config] of Object.entries(TECH_SIGNATURES)) {
408
- const hasExtensions = config.extensions.some(ext => fileCounts[ext] > 0);
409
- const hasConfig = await this.hasAnyFile(config.configFiles);
410
-
411
- if (hasExtensions || hasConfig) {
412
- const count = config.extensions.reduce((sum, ext) => sum + (fileCounts[ext] || 0), 0);
413
- this.results.techStack.languages.push({
414
- name: language,
415
- fileCount: count,
416
- hasConfig
417
- });
418
-
419
- // Check for frameworks
420
- for (const [framework, fwConfig] of Object.entries(config.frameworks || {})) {
421
- const hasFramework = await this.detectFramework(framework, fwConfig);
422
- if (hasFramework) {
423
- detectedFrameworks.add(framework);
424
- }
425
- }
426
- }
427
- }
428
-
429
- // Detect infrastructure
430
- for (const [infra, config] of Object.entries(INFRASTRUCTURE_SIGNATURES)) {
431
- const hasInfra = await this.hasAnyFile(config.configFiles);
432
- if (hasInfra) {
433
- detectedInfra.add(infra);
434
- }
435
- }
436
-
437
- this.results.techStack.frameworks = Array.from(detectedFrameworks);
438
- this.results.techStack.infrastructure = Array.from(detectedInfra);
439
-
440
- // Sort languages by file count
441
- this.results.techStack.languages.sort((a, b) => b.fileCount - a.fileCount);
442
- }
443
-
444
- /**
445
- * Detect a specific framework
446
- */
447
- async detectFramework(name, config) {
448
- // Check config files
449
- if (config.configFiles?.length) {
450
- const hasConfig = await this.hasAnyFile(config.configFiles);
451
- if (hasConfig) return true;
452
- }
453
-
454
- // Check package.json dependencies
455
- if (config.indicators?.length) {
456
- const packageJson = await this.readPackageJson();
457
- if (packageJson) {
458
- const allDeps = {
459
- ...(packageJson.dependencies || {}),
460
- ...(packageJson.devDependencies || {})
461
- };
462
- return config.indicators.some(ind => allDeps[ind]);
463
- }
464
- }
465
-
466
- return false;
467
- }
468
-
469
- /**
470
- * Analyze project structure
471
- */
472
- async analyzeStructure() {
473
- const discovered = {};
474
-
475
- for (const [category, patterns] of Object.entries(DIRECTORY_PATTERNS)) {
476
- for (const pattern of patterns) {
477
- const fullPath = path.join(this.projectPath, pattern);
478
- try {
479
- const stats = await fs.stat(fullPath);
480
- if (stats.isDirectory()) {
481
- if (!discovered[category]) {
482
- discovered[category] = [];
483
- }
484
- discovered[category].push(pattern);
485
- }
486
- } catch (error) {
487
- // Expected: directory doesn't exist (ENOENT) or not accessible (EACCES)
488
- if (error.code !== 'ENOENT' && error.code !== 'EACCES') {
489
- console.error(`Unexpected error checking ${pattern}:`, error.message);
490
- }
491
- }
492
- }
493
- }
494
-
495
- this.results.structure.directories = discovered;
496
- this.results.structure.hasTests = !!discovered.tests?.length;
497
- this.results.structure.hasDocs = !!discovered.docs?.length;
498
- this.results.structure.hasStandards = !!discovered.standards?.length;
499
-
500
- // Detect project type
501
- this.results.structure.type = this.detectProjectType(discovered);
502
- }
503
-
504
- /**
505
- * Detect project type based on structure
506
- */
507
- detectProjectType(directories) {
508
- const hasBackend = !!directories.backend?.length || !!directories.handlers?.length;
509
- const hasFrontend = !!directories.frontend?.length;
510
- const hasInfra = this.results.techStack.infrastructure.length > 0;
511
-
512
- if (hasBackend && hasFrontend) return 'fullstack';
513
- if (hasBackend && hasInfra) return 'serverless-backend';
514
- if (hasBackend) return 'backend';
515
- if (hasFrontend) return 'frontend';
516
- return 'library';
517
- }
518
-
519
- /**
520
- * Discover existing documentation
521
- */
522
- async discoverDocumentation() {
523
- const docs = [];
524
-
525
- // Scan root and docs directories
526
- const dirsToScan = [this.projectPath];
527
- if (this.results.structure.directories.docs) {
528
- for (const docDir of this.results.structure.directories.docs) {
529
- dirsToScan.push(path.join(this.projectPath, docDir));
530
- }
531
- }
532
-
533
- for (const dir of dirsToScan) {
534
- try {
535
- const entries = await fs.readdir(dir);
536
- for (const entry of entries) {
537
- if (entry.endsWith('.md') || entry.endsWith('.txt')) {
538
- const docType = this.categorizeDoc(entry);
539
- if (docType) {
540
- docs.push({
541
- path: path.join(dir, entry),
542
- relativePath: path.relative(this.projectPath, path.join(dir, entry)),
543
- name: entry,
544
- type: docType.type,
545
- description: docType.description
546
- });
547
- }
548
- }
549
- }
550
- } catch (error) {
551
- // Expected: directory doesn't exist or not readable
552
- if (error.code !== 'ENOENT' && error.code !== 'EACCES') {
553
- console.error(`Unexpected error scanning ${dir}:`, error.message);
554
- }
555
- }
556
- }
557
-
558
- this.results.documentation = docs;
559
- }
560
-
561
- /**
562
- * Categorize a documentation file
563
- */
564
- categorizeDoc(filename) {
565
- for (const [type, config] of Object.entries(DOC_PATTERNS)) {
566
- if (config.patterns.some(p => p.test(filename))) {
567
- return { type, description: config.description };
568
- }
569
- }
570
- // Generic markdown
571
- if (filename.endsWith('.md')) {
572
- return { type: 'other', description: 'Documentation' };
573
- }
574
- return null;
575
- }
576
-
577
- /**
578
- * Scan for security anti-patterns
579
- */
580
- async scanSecurity(maxFiles = 500) {
581
- const files = await this.getAllFiles(this.projectPath, 10);
582
- const scannableFiles = files
583
- .filter(f => SCANNABLE_EXTENSIONS.includes(path.extname(f).toLowerCase()))
584
- .slice(0, maxFiles);
585
-
586
- let scanned = 0;
587
- for (const file of scannableFiles) {
588
- try {
589
- const content = await fs.readFile(file, 'utf8');
590
- const lines = content.split('\n');
591
- const relativePath = path.relative(this.projectPath, file);
592
-
593
- for (const [severity, patterns] of Object.entries(SECURITY_PATTERNS)) {
594
- for (const pattern of patterns) {
595
- const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags);
596
-
597
- lines.forEach((line, lineIndex) => {
598
- if (regex.test(line)) {
599
- this.results.securityFindings[severity].push({
600
- file: relativePath,
601
- line: lineIndex + 1,
602
- finding: pattern.name,
603
- description: pattern.description,
604
- recommendation: pattern.recommendation,
605
- cwe: pattern.cwe,
606
- standardsLink: pattern.standardsLink,
607
- snippet: line.trim().substring(0, 100)
608
- });
609
- }
610
- });
611
- }
612
- }
613
- scanned++;
614
- } catch (error) {
615
- // Expected: file not readable or binary file
616
- if (error.code !== 'ENOENT' && error.code !== 'EACCES' && !error.message.includes('encoding')) {
617
- console.error(`Unexpected error scanning ${file}:`, error.message);
618
- }
619
- }
620
- }
621
-
622
- // Summary
623
- this.results.securityFindings.summary = {
624
- filesScanned: scanned,
625
- critical: this.results.securityFindings.critical.length,
626
- high: this.results.securityFindings.high.length,
627
- medium: this.results.securityFindings.medium.length,
628
- low: this.results.securityFindings.low.length,
629
- total: this.results.securityFindings.critical.length +
630
- this.results.securityFindings.high.length +
631
- this.results.securityFindings.medium.length +
632
- this.results.securityFindings.low.length
633
- };
634
- }
635
-
636
- /**
637
- * Generate recommendations based on analysis
638
- */
639
- generateRecommendations() {
640
- const recs = [];
641
-
642
- // Tech stack recommendations
643
- const primaryLang = this.results.techStack.languages[0]?.name;
644
- if (primaryLang) {
645
- recs.push({
646
- category: 'standards',
647
- priority: 'high',
648
- title: `${primaryLang} Standards Available`,
649
- description: `Apply ${primaryLang} coding standards and best practices`,
650
- standardsPack: `${primaryLang}-standards`
651
- });
652
- }
653
-
654
- // Infrastructure recommendations
655
- if (this.results.techStack.infrastructure.includes('sam')) {
656
- recs.push({
657
- category: 'standards',
658
- priority: 'high',
659
- title: 'AWS SAM Standards Available',
660
- description: 'Apply serverless Lambda handler patterns and SAM template standards',
661
- standardsPack: 'serverless-saas-aws'
662
- });
663
- }
664
-
665
- // Security recommendations
666
- const secSummary = this.results.securityFindings.summary;
667
- if (secSummary.critical > 0 || secSummary.high > 0) {
668
- recs.push({
669
- category: 'security',
670
- priority: 'critical',
671
- title: 'Security Issues Detected',
672
- description: `Found ${secSummary.critical} critical and ${secSummary.high} high severity security issues`,
673
- action: 'Review security findings and apply recommended fixes'
674
- });
675
- }
676
-
677
- // Structure recommendations
678
- if (!this.results.structure.hasTests) {
679
- recs.push({
680
- category: 'quality',
681
- priority: 'medium',
682
- title: 'No Test Directory Found',
683
- description: 'Consider adding automated tests',
684
- standardsPack: 'testing-standards'
685
- });
686
- }
687
-
688
- if (!this.results.structure.hasStandards && !this.results.structure.hasDocs) {
689
- recs.push({
690
- category: 'documentation',
691
- priority: 'low',
692
- title: 'No Standards or Documentation Found',
693
- description: 'Consider documenting coding standards and architecture decisions',
694
- action: 'MindMeld can inject standards based on your tech stack'
695
- });
696
- }
697
-
698
- this.results.recommendations = recs;
699
- }
700
-
701
- // ============================================================================
702
- // HELPER METHODS
703
- // ============================================================================
704
-
705
- /**
706
- * Get all files in a directory recursively
707
- */
708
- async getAllFiles(dirPath, maxDepth = 10, currentDepth = 0) {
709
- const files = [];
710
- if (currentDepth >= maxDepth) return files;
711
-
712
- try {
713
- const entries = await fs.readdir(dirPath, { withFileTypes: true });
714
-
715
- for (const entry of entries) {
716
- const fullPath = path.join(dirPath, entry.name);
717
-
718
- if (entry.isDirectory()) {
719
- if (!SKIP_DIRECTORIES.includes(entry.name)) {
720
- files.push(...await this.getAllFiles(fullPath, maxDepth, currentDepth + 1));
721
- }
722
- } else {
723
- files.push(fullPath);
724
- }
725
- }
726
- } catch (error) {
727
- // Expected: directory not readable or doesn't exist
728
- if (error.code !== 'ENOENT' && error.code !== 'EACCES') {
729
- console.error(`Unexpected error reading ${dirPath}:`, error.message);
730
- }
731
- }
732
-
733
- return files;
734
- }
735
-
736
- /**
737
- * Check if any of the given files exist
738
- */
739
- async hasAnyFile(patterns) {
740
- for (const pattern of patterns) {
741
- if (pattern.includes('*')) {
742
- // Glob pattern - simplified check
743
- const dir = path.dirname(pattern) || '.';
744
- const ext = path.extname(pattern);
745
- try {
746
- const entries = await fs.readdir(path.join(this.projectPath, dir));
747
- if (entries.some(e => e.endsWith(ext))) return true;
748
- } catch (error) {
749
- // Expected: directory doesn't exist
750
- if (error.code !== 'ENOENT' && error.code !== 'EACCES') {
751
- console.error(`Unexpected error checking ${dir}:`, error.message);
752
- }
753
- }
754
- } else {
755
- try {
756
- await fs.access(path.join(this.projectPath, pattern));
757
- return true;
758
- } catch (error) {
759
- // Expected: file doesn't exist
760
- if (error.code !== 'ENOENT' && error.code !== 'EACCES') {
761
- console.error(`Unexpected error checking ${pattern}:`, error.message);
762
- }
763
- }
764
- }
765
- }
766
- return false;
767
- }
768
-
769
- /**
770
- * Read package.json if it exists
771
- */
772
- async readPackageJson() {
773
- try {
774
- const content = await fs.readFile(path.join(this.projectPath, 'package.json'), 'utf8');
775
- return JSON.parse(content);
776
- } catch (error) {
777
- // Expected: package.json doesn't exist or is invalid JSON
778
- if (error.code !== 'ENOENT' && !(error instanceof SyntaxError)) {
779
- console.error('Unexpected error reading package.json:', error.message);
780
- }
781
- return null;
782
- }
783
- }
784
-
785
- /**
786
- * Generate a human-readable summary
787
- */
788
- getSummary() {
789
- const r = this.results;
790
- const lines = [];
791
-
792
- lines.push(`\nšŸ“¦ Project: ${r.projectName}`);
793
- lines.push(` Type: ${r.structure.type}`);
794
-
795
- if (r.techStack.languages.length) {
796
- const langs = r.techStack.languages.map(l => `${l.name}(${l.fileCount})`).join(', ');
797
- lines.push(`\nšŸ”§ Languages: ${langs}`);
798
- }
799
-
800
- if (r.techStack.frameworks.length) {
801
- lines.push(` Frameworks: ${r.techStack.frameworks.join(', ')}`);
802
- }
803
-
804
- if (r.techStack.infrastructure.length) {
805
- lines.push(` Infrastructure: ${r.techStack.infrastructure.join(', ')}`);
806
- }
807
-
808
- if (r.documentation.length) {
809
- lines.push(`\nšŸ“š Documentation found: ${r.documentation.length} files`);
810
- for (const doc of r.documentation.slice(0, 5)) {
811
- lines.push(` - ${doc.relativePath} (${doc.description})`);
812
- }
813
- }
814
-
815
- const sec = r.securityFindings.summary;
816
- if (sec.total > 0) {
817
- lines.push(`\nšŸ”’ Security findings:`);
818
- if (sec.critical) lines.push(` ā›” Critical: ${sec.critical}`);
819
- if (sec.high) lines.push(` šŸ”“ High: ${sec.high}`);
820
- if (sec.medium) lines.push(` 🟔 Medium: ${sec.medium}`);
821
- if (sec.low) lines.push(` 🟢 Low: ${sec.low}`);
822
- } else if (sec.filesScanned > 0) {
823
- lines.push(`\nāœ… No security issues found in ${sec.filesScanned} files scanned`);
824
- }
825
-
826
- if (r.recommendations.length) {
827
- lines.push(`\nšŸ’” Recommendations:`);
828
- for (const rec of r.recommendations) {
829
- const icon = rec.priority === 'critical' ? 'ā›”' : rec.priority === 'high' ? 'šŸ”¶' : 'šŸ’”';
830
- lines.push(` ${icon} ${rec.title}`);
831
- }
832
- }
833
-
834
- return lines.join('\n');
835
- }
836
- }
837
-
838
- // ============================================================================
839
- // CLI ENTRY POINT
840
- // ============================================================================
841
-
842
- async function main() {
843
- const args = process.argv.slice(2);
844
- const verbose = args.includes('--verbose') || args.includes('-v');
845
- const json = args.includes('--json');
846
-
847
- // Filter out flags to get the path
848
- const pathArg = args.find(a => !a.startsWith('-'));
849
- const projectPath = pathArg || process.cwd();
850
-
851
- const analyzer = new RepoAnalyzer(projectPath);
852
- const results = await analyzer.analyze({ verbose });
853
-
854
- if (json) {
855
- console.log(JSON.stringify(results, null, 2));
856
- } else {
857
- console.log(analyzer.getSummary());
858
- }
859
- }
860
-
861
- // Export for use as module
862
- module.exports = { RepoAnalyzer, SECURITY_PATTERNS, TECH_SIGNATURES, DIRECTORY_PATTERNS };
863
-
864
- // Run if called directly
865
- if (require.main === module) {
866
- main().catch(error => {
867
- console.error('Fatal error:', error.message);
868
- process.exit(1);
869
- });
870
- }