@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/hooks/session-start.js
CHANGED
|
@@ -101,6 +101,80 @@ async function detectGitRemote() {
|
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Detect git HEAD SHA and branch for project identity (Spec §6.5)
|
|
106
|
+
* @returns {Promise<{commitSha: string|null, gitBranch: string|null}>}
|
|
107
|
+
*/
|
|
108
|
+
async function detectGitMetadata() {
|
|
109
|
+
const { execSync } = require('child_process');
|
|
110
|
+
const opts = { cwd: process.cwd(), encoding: 'utf-8', timeout: 2000, stdio: ['pipe', 'pipe', 'pipe'] };
|
|
111
|
+
let commitSha = null;
|
|
112
|
+
let gitBranch = null;
|
|
113
|
+
try {
|
|
114
|
+
commitSha = execSync('git rev-parse HEAD', opts).trim() || null;
|
|
115
|
+
} catch (_) { /* not a git repo */ }
|
|
116
|
+
try {
|
|
117
|
+
gitBranch = execSync('git branch --show-current', opts).trim() || null;
|
|
118
|
+
} catch (_) { /* detached HEAD or not a git repo */ }
|
|
119
|
+
return { commitSha, gitBranch };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Read Claude Code memory files for drift detection (Spec §5.2)
|
|
124
|
+
* Reads MEMORY.md index + all referenced .md files from the project memory dir
|
|
125
|
+
* @returns {Promise<string|null>} Concatenated memory content or null
|
|
126
|
+
*/
|
|
127
|
+
async function readClaudeMemory() {
|
|
128
|
+
const os = require('os');
|
|
129
|
+
const encodedCwd = process.cwd().replace(/\//g, '-');
|
|
130
|
+
const memoryDir = path.join(os.homedir(), '.claude', 'projects', encodedCwd, 'memory');
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
await fs.access(memoryDir);
|
|
134
|
+
} catch (_) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
const files = await fs.readdir(memoryDir);
|
|
140
|
+
const mdFiles = files.filter(f => f.endsWith('.md'));
|
|
141
|
+
if (mdFiles.length === 0) return null;
|
|
142
|
+
|
|
143
|
+
const contents = [];
|
|
144
|
+
// Read MEMORY.md first if it exists (it's the index)
|
|
145
|
+
if (mdFiles.includes('MEMORY.md')) {
|
|
146
|
+
const content = await fs.readFile(path.join(memoryDir, 'MEMORY.md'), 'utf-8');
|
|
147
|
+
contents.push(`--- MEMORY.md ---\n${content}`);
|
|
148
|
+
}
|
|
149
|
+
for (const file of mdFiles) {
|
|
150
|
+
if (file === 'MEMORY.md') continue;
|
|
151
|
+
try {
|
|
152
|
+
const content = await fs.readFile(path.join(memoryDir, file), 'utf-8');
|
|
153
|
+
contents.push(`--- ${file} ---\n${content}`);
|
|
154
|
+
} catch (_) { /* skip unreadable files */ }
|
|
155
|
+
}
|
|
156
|
+
return contents.join('\n\n');
|
|
157
|
+
} catch (err) {
|
|
158
|
+
console.error('[MindMeld] Memory read failed (non-fatal):', err.message);
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Read CLAUDE.md files for drift detection
|
|
165
|
+
* Reads project-level CLAUDE.md and any parent CLAUDE.md files
|
|
166
|
+
* @returns {Promise<string|null>} Concatenated CLAUDE.md content or null
|
|
167
|
+
*/
|
|
168
|
+
async function readClaudeMd() {
|
|
169
|
+
try {
|
|
170
|
+
const claudeMdPath = path.join(process.cwd(), 'CLAUDE.md');
|
|
171
|
+
const content = await fs.readFile(claudeMdPath, 'utf-8');
|
|
172
|
+
return content || null;
|
|
173
|
+
} catch (_) {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
104
178
|
/**
|
|
105
179
|
* Get project name from git remote URL or directory name
|
|
106
180
|
* @param {string|null} gitRemote - Git remote URL
|
|
@@ -628,6 +702,81 @@ async function fetchRelevantStandardsFromAPI(apiUrl, authToken, characteristics,
|
|
|
628
702
|
});
|
|
629
703
|
}
|
|
630
704
|
|
|
705
|
+
/**
|
|
706
|
+
* Call mindmeld_init_session via JSON-RPC to get banded injection (Band A/B/C).
|
|
707
|
+
* This converges the hook injection path with the MCP tool path — one deterministic
|
|
708
|
+
* banded output regardless of whether the MCP tool also fires during the session.
|
|
709
|
+
*/
|
|
710
|
+
async function callBandedInitSession(apiUrl, authToken, params) {
|
|
711
|
+
if (!authToken) {
|
|
712
|
+
throw new Error('No auth token available');
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
const https = require('https');
|
|
716
|
+
const url = require('url');
|
|
717
|
+
|
|
718
|
+
const jsonRpcPayload = JSON.stringify({
|
|
719
|
+
jsonrpc: '2.0',
|
|
720
|
+
id: 1,
|
|
721
|
+
method: 'tools/call',
|
|
722
|
+
params: {
|
|
723
|
+
name: 'mindmeld_init_session',
|
|
724
|
+
arguments: params
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
const parsedUrl = url.parse(`${apiUrl}/api/mcp/mindmeld`);
|
|
729
|
+
|
|
730
|
+
const options = {
|
|
731
|
+
hostname: parsedUrl.hostname,
|
|
732
|
+
port: parsedUrl.port || 443,
|
|
733
|
+
path: parsedUrl.path,
|
|
734
|
+
method: 'POST',
|
|
735
|
+
headers: {
|
|
736
|
+
'Content-Type': 'application/json',
|
|
737
|
+
'Authorization': `Bearer ${authToken}`,
|
|
738
|
+
'Content-Length': Buffer.byteLength(jsonRpcPayload)
|
|
739
|
+
},
|
|
740
|
+
timeout: 8000
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
return new Promise((resolve, reject) => {
|
|
744
|
+
const req = https.request(options, (res) => {
|
|
745
|
+
let data = '';
|
|
746
|
+
res.on('data', chunk => { data += chunk; });
|
|
747
|
+
res.on('end', () => {
|
|
748
|
+
try {
|
|
749
|
+
const rpcResponse = JSON.parse(data);
|
|
750
|
+
if (rpcResponse.error) {
|
|
751
|
+
const err = new Error(rpcResponse.error.message || 'JSON-RPC error');
|
|
752
|
+
err.statusCode = res.statusCode;
|
|
753
|
+
reject(err);
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
const content = rpcResponse.result?.content;
|
|
757
|
+
if (!content || !content[0] || content[0].type !== 'text') {
|
|
758
|
+
reject(new Error('Unexpected init_session response shape'));
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
const initResult = JSON.parse(content[0].text);
|
|
762
|
+
resolve(initResult);
|
|
763
|
+
} catch (e) {
|
|
764
|
+
reject(new Error('Invalid banded init_session response'));
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
req.on('error', reject);
|
|
770
|
+
req.on('timeout', () => {
|
|
771
|
+
req.destroy();
|
|
772
|
+
reject(new Error('Banded init_session timeout'));
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
req.write(jsonRpcPayload);
|
|
776
|
+
req.end();
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
|
|
631
780
|
/**
|
|
632
781
|
* Fetch weekly splash data from API
|
|
633
782
|
* Returns splash summary with stats, or null if already acknowledged
|
|
@@ -744,11 +893,14 @@ async function injectContext() {
|
|
|
744
893
|
}
|
|
745
894
|
|
|
746
895
|
// 1. Parallel local reads (no network, no DB)
|
|
747
|
-
const [fingerprintConfig, authToken, apiConfig, preferences] = await Promise.all([
|
|
896
|
+
const [fingerprintConfig, authToken, apiConfig, preferences, gitMetadata, memoryContent, claudeMdContent] = await Promise.all([
|
|
748
897
|
loadFingerprintConfig(),
|
|
749
898
|
loadAuthToken(),
|
|
750
899
|
loadApiConfig(),
|
|
751
|
-
loadStandardsPreferences()
|
|
900
|
+
loadStandardsPreferences(),
|
|
901
|
+
detectGitMetadata(),
|
|
902
|
+
readClaudeMemory(),
|
|
903
|
+
readClaudeMd()
|
|
752
904
|
]);
|
|
753
905
|
|
|
754
906
|
// 2. Initialize MindmeldClient (used for project detection, session tracking, team context)
|
|
@@ -795,7 +947,9 @@ async function injectContext() {
|
|
|
795
947
|
companyId: context.companyId,
|
|
796
948
|
userEmail: context.userEmail,
|
|
797
949
|
startedAt: new Date().toISOString(),
|
|
798
|
-
transcriptPath: transcriptPath
|
|
950
|
+
transcriptPath: transcriptPath,
|
|
951
|
+
commitSha: gitMetadata.commitSha,
|
|
952
|
+
gitBranch: gitMetadata.gitBranch,
|
|
799
953
|
};
|
|
800
954
|
const mindmeldDir = path.join(process.cwd(), '.mindmeld');
|
|
801
955
|
await fs.mkdir(mindmeldDir, { recursive: true });
|
|
@@ -842,89 +996,66 @@ async function injectContext() {
|
|
|
842
996
|
console.error(`[MindMeld] Watcher spawn failed (non-fatal): ${err.message}`);
|
|
843
997
|
}
|
|
844
998
|
|
|
845
|
-
// 4.
|
|
846
|
-
|
|
999
|
+
// 4. Banded injection path (converged with MCP init_session)
|
|
1000
|
+
// Band B content gated on resolved companyId — null scope would mis-match snapshots
|
|
1001
|
+
const bandBGated = !!context.companyId;
|
|
1002
|
+
const initParams = {
|
|
1003
|
+
project_path: process.cwd(),
|
|
1004
|
+
commit_sha: gitMetadata.commitSha || undefined,
|
|
1005
|
+
git_branch: gitMetadata.gitBranch || undefined,
|
|
1006
|
+
git_root: gitMetadata.gitRoot || undefined,
|
|
1007
|
+
memory_content: bandBGated ? (memoryContent || undefined) : undefined,
|
|
1008
|
+
claude_md_content: bandBGated ? (claudeMdContent || undefined) : undefined
|
|
1009
|
+
};
|
|
847
1010
|
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
fetchRelevantStandardsFromAPI(apiConfig.apiUrl, authToken, characteristics, context.projectId, preferences),
|
|
851
|
-
mindmeld.loadProjectContext(context.projectId),
|
|
1011
|
+
const [bandedResult, splashResult] = await Promise.allSettled([
|
|
1012
|
+
callBandedInitSession(apiConfig.apiUrl, authToken, initParams),
|
|
852
1013
|
fetchSplashData(apiConfig.apiUrl, authToken)
|
|
853
1014
|
]);
|
|
854
1015
|
|
|
855
|
-
// 6. Resolve standards: API → file-based fallback (only for network errors, not subscription blocks)
|
|
856
|
-
let relevantStandards = [];
|
|
857
|
-
if (standardsResult.status === 'fulfilled' && standardsResult.value.length > 0) {
|
|
858
|
-
relevantStandards = standardsResult.value;
|
|
859
|
-
console.error(`[MindMeld] ${relevantStandards.length} standards from API`);
|
|
860
|
-
} else if (standardsResult.status === 'rejected' &&
|
|
861
|
-
standardsResult.reason.message.includes('subscription required')) {
|
|
862
|
-
// Subscription enforcement — do NOT fall through to file-based injection
|
|
863
|
-
console.error('[MindMeld] Active subscription required. Subscribe at app.mindmeld.dev');
|
|
864
|
-
return '';
|
|
865
|
-
} else if (standardsResult.status === 'rejected' &&
|
|
866
|
-
(standardsResult.reason.statusCode === 401 || standardsResult.reason.message === 'Unauthorized')) {
|
|
867
|
-
// Auth token expired or invalid — trigger re-auth and use file-based fallback
|
|
868
|
-
console.error('[MindMeld] Auth token expired. Triggering re-authentication...');
|
|
869
|
-
spawnBackgroundLogin();
|
|
870
|
-
const categories = mindmeld.relevanceDetector.mapCharacteristicsToCategories(characteristics);
|
|
871
|
-
relevantStandards = await mindmeld.relevanceDetector.loadStandardsFromFiles(categories, characteristics);
|
|
872
|
-
console.error(`[MindMeld] ${relevantStandards.length} standards from file fallback (scored)`);
|
|
873
|
-
} else {
|
|
874
|
-
if (standardsResult.status === 'rejected') {
|
|
875
|
-
console.error(`[MindMeld] API fallback: ${standardsResult.reason.message}`);
|
|
876
|
-
}
|
|
877
|
-
const categories = mindmeld.relevanceDetector.mapCharacteristicsToCategories(characteristics);
|
|
878
|
-
relevantStandards = await mindmeld.relevanceDetector.loadStandardsFromFiles(categories, characteristics);
|
|
879
|
-
console.error(`[MindMeld] ${relevantStandards.length} standards from file fallback (scored)`);
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
// 7. Filter by project preferences
|
|
883
|
-
if (preferences) {
|
|
884
|
-
relevantStandards = filterStandardsByPreferences(relevantStandards, preferences);
|
|
885
|
-
console.error(`[MindMeld] Filtered to ${relevantStandards.length} standards by preferences`);
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
// Take top 10 most relevant after filtering
|
|
889
|
-
relevantStandards = relevantStandards.slice(0, 10);
|
|
890
|
-
|
|
891
|
-
// 8. Record standards shown (fire-and-forget, non-blocking)
|
|
892
|
-
mindmeld.recordStandardsShown(sessionId, relevantStandards, context.projectId, context.userEmail);
|
|
893
|
-
|
|
894
|
-
// 9. Resolve team context from parallel API call
|
|
895
|
-
const projectContext = projectContextResult.status === 'fulfilled' ? projectContextResult.value : null;
|
|
896
|
-
const teamPatterns = projectContext && projectContext.patterns
|
|
897
|
-
? projectContext.patterns.filter(p => p.confidence > 0.7)
|
|
898
|
-
: [];
|
|
899
|
-
const recentLearning = projectContext && projectContext.recentLearning
|
|
900
|
-
? projectContext.recentLearning
|
|
901
|
-
: [];
|
|
902
|
-
|
|
903
|
-
// 10. Resolve splash data
|
|
904
1016
|
const splashData = splashResult.status === 'fulfilled' ? splashResult.value : null;
|
|
905
|
-
|
|
906
|
-
// Auto-acknowledge splash (fire-and-forget) so it doesn't show again this week
|
|
907
|
-
if (splashData && splashData.show_splash && splashData.summary) {
|
|
1017
|
+
if (splashData?.show_splash && splashData?.summary) {
|
|
908
1018
|
acknowledgeSplash(apiConfig.apiUrl, authToken, splashData.summary.week_start);
|
|
909
1019
|
}
|
|
910
1020
|
|
|
911
|
-
//
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
1021
|
+
// 6. Resolve injection: banded → flat fallback
|
|
1022
|
+
let injection;
|
|
1023
|
+
if (bandedResult.status === 'fulfilled' && bandedResult.value.formatted_injection) {
|
|
1024
|
+
const initResponse = bandedResult.value;
|
|
1025
|
+
const summary = initResponse.injection_summary || {};
|
|
1026
|
+
console.error(`[MindMeld] Banded injection: A=${summary.band_a_tokens || 0}t B=${summary.band_b_tokens || 0}t C=${summary.band_c_tokens || 0}t / ${summary.budget_tokens || 400}t budget`);
|
|
1027
|
+
|
|
1028
|
+
injection = formatConvergedInjection({
|
|
1029
|
+
bandedMarkdown: initResponse.formatted_injection,
|
|
1030
|
+
collaborators: context.collaborators,
|
|
1031
|
+
splash: splashData
|
|
1032
|
+
});
|
|
922
1033
|
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1034
|
+
try {
|
|
1035
|
+
const standardsForCache = (initResponse.injected_rules || []).map(r => ({
|
|
1036
|
+
pattern_id: r.rule_id, element: r.standard, rule: r.text,
|
|
1037
|
+
maturity: r.maturity, relevance_score: r.relevance_score
|
|
1038
|
+
}));
|
|
1039
|
+
await cacheSubagentContext(standardsForCache, [], context.projectName);
|
|
1040
|
+
} catch (cacheError) {
|
|
1041
|
+
console.error('[MindMeld] Subagent context cache failed (non-fatal):', cacheError.message);
|
|
1042
|
+
}
|
|
1043
|
+
} else {
|
|
1044
|
+
const bandedError = bandedResult.status === 'rejected' ? bandedResult.reason : null;
|
|
1045
|
+
if (bandedError) {
|
|
1046
|
+
if (bandedError.message.includes('subscription required')) {
|
|
1047
|
+
console.error('[MindMeld] Active subscription required. Subscribe at app.mindmeld.dev');
|
|
1048
|
+
return '';
|
|
1049
|
+
}
|
|
1050
|
+
if (bandedError.statusCode === 401 || bandedError.message === 'Unauthorized') {
|
|
1051
|
+
console.error('[MindMeld] Auth token expired. Triggering re-authentication...');
|
|
1052
|
+
spawnBackgroundLogin();
|
|
1053
|
+
}
|
|
1054
|
+
console.error(`[MindMeld] API unavailable: ${bandedError.message}`);
|
|
1055
|
+
} else {
|
|
1056
|
+
console.error('[MindMeld] API returned empty injection');
|
|
1057
|
+
}
|
|
1058
|
+
injection = '<!-- MindMeld: API unavailable — this session is proceeding without governance injection. Standards were not loaded. -->';
|
|
928
1059
|
}
|
|
929
1060
|
|
|
930
1061
|
const elapsed = Date.now() - startTime;
|
|
@@ -933,9 +1064,8 @@ async function injectContext() {
|
|
|
933
1064
|
return injection;
|
|
934
1065
|
|
|
935
1066
|
} catch (error) {
|
|
936
|
-
// Graceful degradation - never block Claude Code session
|
|
937
1067
|
console.error('[MindMeld] Hook error (non-fatal):', error.message);
|
|
938
|
-
return '';
|
|
1068
|
+
return '<!-- MindMeld: hook error — this session is proceeding without governance injection. Standards were not loaded. -->';
|
|
939
1069
|
}
|
|
940
1070
|
}
|
|
941
1071
|
|
|
@@ -956,6 +1086,27 @@ async function checkMindmeldConfiguration() {
|
|
|
956
1086
|
}
|
|
957
1087
|
}
|
|
958
1088
|
|
|
1089
|
+
/**
|
|
1090
|
+
* Validate that a standard has enough content to be useful when injected.
|
|
1091
|
+
* Mirrors StandardsIngestion.isViableStandard() for the output path.
|
|
1092
|
+
*/
|
|
1093
|
+
function isViableForInjection(standard) {
|
|
1094
|
+
const el = (standard.element || '').trim();
|
|
1095
|
+
const rl = (standard.rule || '').trim();
|
|
1096
|
+
|
|
1097
|
+
if (!el || !rl) return false;
|
|
1098
|
+
if (/^(USE|ALWAYS|NEVER|PREFER|AVOID|DO|DON'T|SET|ADD|RUN|CALL)$/i.test(el)) return false;
|
|
1099
|
+
if (/^(Standard Pattern|Core Principle)$/i.test(el) && rl.length < 30) return false;
|
|
1100
|
+
if (/^(ALWAYS|NEVER|USE|PREFER|AVOID)[:\s]*$/i.test(rl)) return false;
|
|
1101
|
+
if (rl.length < 15) return false;
|
|
1102
|
+
|
|
1103
|
+
// Word-count guard: element must have >=2 words or be a compound term (>=8 chars)
|
|
1104
|
+
const wordCount = el.split(/[\s.]+/).filter(w => w.length > 0).length;
|
|
1105
|
+
if (wordCount < 2 && el.length < 8) return false;
|
|
1106
|
+
|
|
1107
|
+
return true;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
959
1110
|
/**
|
|
960
1111
|
* Cache condensed context for subagent injection
|
|
961
1112
|
* Subagents don't get session-start hooks, so we cache the essentials
|
|
@@ -975,25 +1126,55 @@ async function cacheSubagentContext(standards, teamPatterns, projectName) {
|
|
|
975
1126
|
sections.push('Follow these standards when writing or modifying code:');
|
|
976
1127
|
sections.push('');
|
|
977
1128
|
|
|
978
|
-
|
|
1129
|
+
let injectedCount = 0;
|
|
1130
|
+
let skippedCount = 0;
|
|
1131
|
+
|
|
979
1132
|
if (standards && standards.length > 0) {
|
|
980
1133
|
for (const s of standards) {
|
|
981
|
-
|
|
1134
|
+
if (!isViableForInjection(s)) {
|
|
1135
|
+
skippedCount++;
|
|
1136
|
+
continue;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
const maturity = s.maturity_tier || s.maturity || 'validated';
|
|
1140
|
+
const prefix = maturity === 'reinforced' ? '[MUST]' :
|
|
1141
|
+
maturity === 'enforced' ? '[MUST]' :
|
|
1142
|
+
maturity === 'solidified' ? '[SHOULD]' : '[CONSIDER]';
|
|
1143
|
+
|
|
1144
|
+
sections.push(`### ${prefix} ${s.element}`);
|
|
1145
|
+
sections.push(`**Category**: ${s.category}`);
|
|
1146
|
+
sections.push(`**Rule**: ${s.rule}`);
|
|
1147
|
+
|
|
1148
|
+
// Include first anti-pattern if available — high signal-to-token ratio
|
|
1149
|
+
if (s.anti_patterns && s.anti_patterns.length > 0) {
|
|
1150
|
+
const ap = s.anti_patterns[0];
|
|
1151
|
+
const desc = typeof ap === 'string' ? ap : (ap.description || '');
|
|
1152
|
+
if (desc) {
|
|
1153
|
+
sections.push(`**Avoid**: ${desc}`);
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
sections.push('');
|
|
1158
|
+
injectedCount++;
|
|
982
1159
|
}
|
|
983
|
-
sections.push('');
|
|
984
1160
|
}
|
|
985
1161
|
|
|
986
|
-
// Include team patterns
|
|
1162
|
+
// Include team patterns with validation
|
|
987
1163
|
if (teamPatterns && teamPatterns.length > 0) {
|
|
988
1164
|
sections.push('## Team Patterns');
|
|
989
1165
|
for (const p of teamPatterns) {
|
|
1166
|
+
if (!isViableForInjection(p)) {
|
|
1167
|
+
skippedCount++;
|
|
1168
|
+
continue;
|
|
1169
|
+
}
|
|
990
1170
|
sections.push(`- **${p.element}** (${(p.correlation * 100).toFixed(0)}% correlation): ${p.rule}`);
|
|
1171
|
+
injectedCount++;
|
|
991
1172
|
}
|
|
992
1173
|
sections.push('');
|
|
993
1174
|
}
|
|
994
1175
|
|
|
995
1176
|
await fs.writeFile(cachePath, sections.join('\n'));
|
|
996
|
-
console.error(`[MindMeld] Cached subagent context (${
|
|
1177
|
+
console.error(`[MindMeld] Cached subagent context (${injectedCount} standards, ${skippedCount} skipped as non-viable)`);
|
|
997
1178
|
}
|
|
998
1179
|
|
|
999
1180
|
/**
|
|
@@ -1011,7 +1192,53 @@ function getMaturityPrefix(maturityTier) {
|
|
|
1011
1192
|
}
|
|
1012
1193
|
|
|
1013
1194
|
/**
|
|
1014
|
-
*
|
|
1195
|
+
* Wrap banded init_session output with hook-only sections (splash, team).
|
|
1196
|
+
* The banded markdown is self-contained (header, copyright, bands A/B/C, footer).
|
|
1197
|
+
* Hook-only content is inserted between the copyright block and the first band heading.
|
|
1198
|
+
*/
|
|
1199
|
+
function formatConvergedInjection({ bandedMarkdown, collaborators, splash }) {
|
|
1200
|
+
const extraSections = [];
|
|
1201
|
+
|
|
1202
|
+
if (splash && splash.show_splash) {
|
|
1203
|
+
if (splash.is_greenfield && splash.tips && splash.tips.length > 0) {
|
|
1204
|
+
extraSections.push('## Getting Started with MindMeld');
|
|
1205
|
+
for (const tip of splash.tips) extraSections.push(`- ${tip}`);
|
|
1206
|
+
extraSections.push('');
|
|
1207
|
+
} else if (splash.summary) {
|
|
1208
|
+
const s = splash.summary;
|
|
1209
|
+
const formatDate = (dateStr) => {
|
|
1210
|
+
const d = new Date(dateStr + 'T00:00:00Z');
|
|
1211
|
+
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', timeZone: 'UTC' });
|
|
1212
|
+
};
|
|
1213
|
+
const hours = Math.floor(s.total_duration_minutes / 60);
|
|
1214
|
+
const mins = s.total_duration_minutes % 60;
|
|
1215
|
+
const duration = hours > 0 ? `${hours}h ${mins}m` : `${mins}m`;
|
|
1216
|
+
extraSections.push(`## Weekly Recap (${formatDate(s.week_start)} - ${formatDate(s.week_end)})`);
|
|
1217
|
+
extraSections.push(`${s.sessions_count} sessions | ${duration} total | ${s.active_projects} project${s.active_projects !== 1 ? 's' : ''}`);
|
|
1218
|
+
extraSections.push(`${s.standards_injected} standards injected, ${s.standards_followed} followed, ${s.violations_detected} violation${s.violations_detected !== 1 ? 's' : ''}`);
|
|
1219
|
+
if (s.patterns_harvested > 0) extraSections.push(`${s.patterns_harvested} patterns harvested`);
|
|
1220
|
+
extraSections.push('');
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
if (collaborators && collaborators.length > 0) {
|
|
1225
|
+
extraSections.push('## Team');
|
|
1226
|
+
extraSections.push(collaborators.map(c => `- ${c.name} (${c.email})`).join('\n'));
|
|
1227
|
+
extraSections.push('');
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
if (extraSections.length === 0) return bandedMarkdown;
|
|
1231
|
+
|
|
1232
|
+
const firstBandHeading = bandedMarkdown.indexOf('\n## ');
|
|
1233
|
+
if (firstBandHeading === -1) return bandedMarkdown;
|
|
1234
|
+
|
|
1235
|
+
return bandedMarkdown.slice(0, firstBandHeading + 1) +
|
|
1236
|
+
extraSections.join('\n') + '\n' +
|
|
1237
|
+
bandedMarkdown.slice(firstBandHeading + 1);
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
/**
|
|
1241
|
+
* Format flat context injection for Claude Code (fallback when banded path unavailable)
|
|
1015
1242
|
* @param {object} data - Context data to inject
|
|
1016
1243
|
* @param {object} data.fingerprint - Fingerprint config {userId, companyId, tier}
|
|
1017
1244
|
*/
|
package/package.json
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@equilateral_ai/mindmeld",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.2",
|
|
4
4
|
"description": "Intelligent standards injection for AI coding sessions - context-aware, self-documenting, scales to large codebases",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"mindmeld": "
|
|
7
|
+
"mindmeld": "scripts/init-project.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
|
-
"src/",
|
|
10
|
+
"src/index.js",
|
|
11
|
+
"src/core/AuthManager.js",
|
|
12
|
+
"src/core/PatternValidator.js",
|
|
13
|
+
"src/core/LLMPatternDetector.js",
|
|
14
|
+
"src/client/dbShim.js",
|
|
15
|
+
"src/handlers/helpers/dbOperations.js",
|
|
16
|
+
"src/utils/piiMask.js",
|
|
11
17
|
"hooks/",
|
|
12
18
|
"scripts/init-project.js",
|
|
13
19
|
"scripts/auth-login.js",
|
|
14
|
-
"scripts/inject.js",
|
|
15
|
-
"scripts/harvest.js",
|
|
16
20
|
"scripts/repo-analyzer.js",
|
|
17
|
-
"scripts/
|
|
18
|
-
"scripts/standards.js",
|
|
21
|
+
"scripts/harvest.js",
|
|
19
22
|
"README.md"
|
|
20
23
|
],
|
|
21
24
|
"publishConfig": {
|
|
@@ -44,7 +47,9 @@
|
|
|
44
47
|
"test:benchmark": "node scripts/test-claude-hooks.js --benchmark",
|
|
45
48
|
"test:smoke": "jest --selectProjects smoke --testTimeout=15000",
|
|
46
49
|
"test:e2e": "cd e2e && npx playwright test",
|
|
47
|
-
"test:e2e:admin": "cd frontend/admin && npx playwright test"
|
|
50
|
+
"test:e2e:admin": "cd frontend/admin && npx playwright test",
|
|
51
|
+
"prepack": "mkdir -p src/handlers/helpers && cp src/client/dbShim.js src/handlers/helpers/dbOperations.js",
|
|
52
|
+
"postpack": "git checkout -- src/handlers/helpers/dbOperations.js"
|
|
48
53
|
},
|
|
49
54
|
"claudeCode": {
|
|
50
55
|
"hooks": {
|
|
@@ -80,14 +85,17 @@
|
|
|
80
85
|
},
|
|
81
86
|
"homepage": "https://mindmeld.dev",
|
|
82
87
|
"dependencies": {
|
|
83
|
-
"
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
"
|
|
88
|
+
"js-yaml": "^4.1.1"
|
|
89
|
+
},
|
|
90
|
+
"optionalDependencies": {
|
|
91
|
+
"@aws-sdk/client-bedrock-runtime": "^3.460.0"
|
|
87
92
|
},
|
|
88
93
|
"devDependencies": {
|
|
89
94
|
"@aws-sdk/client-s3": "^3.460.0",
|
|
90
|
-
"
|
|
95
|
+
"@aws-sdk/client-ses": "^3.985.0",
|
|
96
|
+
"dotenv": "^17.4.2",
|
|
97
|
+
"jest": "^29.7.0",
|
|
98
|
+
"pg": "^8.18.0"
|
|
91
99
|
},
|
|
92
100
|
"engines": {
|
|
93
101
|
"node": ">=18.0.0"
|
package/scripts/init-project.js
CHANGED
|
@@ -702,14 +702,9 @@ if (args.command === 'init') {
|
|
|
702
702
|
process.exit(1);
|
|
703
703
|
});
|
|
704
704
|
} else if (args.command === 'inject') {
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
.then(() => process.exit(0))
|
|
709
|
-
.catch(error => {
|
|
710
|
-
console.error('\n❌ Error:', error.message);
|
|
711
|
-
process.exit(1);
|
|
712
|
-
});
|
|
705
|
+
console.error('The inject subcommand has been removed in v4.0.0.');
|
|
706
|
+
console.error('Standards injection is now handled automatically by the session-start hook via the MindMeld API.');
|
|
707
|
+
process.exit(1);
|
|
713
708
|
} else if (args.command === 'harvest') {
|
|
714
709
|
const { harvest, showHarvestHelp } = require('./harvest');
|
|
715
710
|
if (args.help) { showHarvestHelp(); process.exit(0); }
|
|
@@ -741,8 +736,9 @@ if (args.command === 'init') {
|
|
|
741
736
|
process.exit(1);
|
|
742
737
|
});
|
|
743
738
|
} else if (args.command === 'standards') {
|
|
744
|
-
|
|
745
|
-
|
|
739
|
+
console.error('The standards subcommand has been removed in v4.0.0.');
|
|
740
|
+
console.error('Configure standards at https://app.mindmeld.dev');
|
|
741
|
+
process.exit(1);
|
|
746
742
|
} else if (args.command === 'open') {
|
|
747
743
|
openWebApp(args.projectPath || 'dashboard')
|
|
748
744
|
.then(() => process.exit(0))
|
|
@@ -751,19 +747,9 @@ if (args.command === 'init') {
|
|
|
751
747
|
process.exit(1);
|
|
752
748
|
});
|
|
753
749
|
} else if (args.command === 'mcp') {
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
if (!token) {
|
|
758
|
-
console.error('\nMindMeld MCP bridge requires an API token.\n');
|
|
759
|
-
console.error('Set one of:');
|
|
760
|
-
console.error(' 1. MINDMELD_TOKEN environment variable');
|
|
761
|
-
console.error(' 2. --token CLI argument');
|
|
762
|
-
console.error(' 3. Save to ~/.mindmeld/api-token\n');
|
|
763
|
-
console.error('Create a token at: https://app.mindmeld.dev/api-tokens\n');
|
|
764
|
-
process.exit(1);
|
|
765
|
-
}
|
|
766
|
-
startBridge(token);
|
|
750
|
+
console.error('The mcp subcommand has been removed in v4.0.0.');
|
|
751
|
+
console.error('Use the MindMeld MCP server directly via Claude Code settings.');
|
|
752
|
+
process.exit(1);
|
|
767
753
|
} else {
|
|
768
754
|
console.error(`Unknown command: ${args.command}`);
|
|
769
755
|
console.error('Run "mindmeld --help" for usage.');
|