pumuki-ast-hooks 5.5.0 → 5.5.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki-ast-hooks",
|
|
3
|
-
"version": "5.5.
|
|
3
|
+
"version": "5.5.2",
|
|
4
4
|
"description": "Enterprise-grade AST Intelligence System with multi-platform support (iOS, Android, Backend, Frontend) and Feature-First + DDD + Clean Architecture enforcement. Includes dynamic violations API for intelligent querying.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -964,169 +964,189 @@ async function handleMcpMessage(message) {
|
|
|
964
964
|
}
|
|
965
965
|
}
|
|
966
966
|
|
|
967
|
+
// Flag to track if MCP has been initialized
|
|
968
|
+
let mcpInitialized = false;
|
|
969
|
+
|
|
967
970
|
// Start protocol handler
|
|
968
|
-
protocolHandler.start(
|
|
971
|
+
protocolHandler.start(async (message) => {
|
|
972
|
+
const response = await handleMcpMessage(message);
|
|
973
|
+
|
|
974
|
+
// Start polling loops ONLY after receiving 'initialized' notification from Windsurf
|
|
975
|
+
if (!mcpInitialized && message.includes('"method":"initialized"')) {
|
|
976
|
+
mcpInitialized = true;
|
|
977
|
+
process.stderr.write(`[MCP] Received 'initialized' - starting background loops\n`);
|
|
978
|
+
startPollingLoops();
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
return response;
|
|
982
|
+
});
|
|
983
|
+
|
|
984
|
+
// Log MCP ready
|
|
985
|
+
process.stderr.write(`[MCP] Server ready for ${REPO_ROOT}\n`);
|
|
969
986
|
|
|
970
987
|
/**
|
|
971
|
-
*
|
|
988
|
+
* Start polling loops for background notifications and automations
|
|
989
|
+
* Called ONLY after MCP handshake is complete
|
|
972
990
|
*/
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
if (
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
// 2. Evidence Freshness Guard
|
|
1002
|
-
if (evidenceMonitor.isStale() && (now - lastEvidenceNotification > NOTIFICATION_COOLDOWN)) {
|
|
1003
|
-
try {
|
|
1004
|
-
await evidenceMonitor.refresh();
|
|
1005
|
-
sendNotification('🔄 Evidence Auto-Updated', 'AI Evidence has been refreshed automatically', 'Purr');
|
|
1006
|
-
} catch (err) {
|
|
1007
|
-
sendNotification('⚠️ Evidence Stale', `Failed to auto-refresh evidence: ${err.message}`, 'Basso');
|
|
991
|
+
function startPollingLoops() {
|
|
992
|
+
setInterval(async () => {
|
|
993
|
+
try {
|
|
994
|
+
const now = Date.now();
|
|
995
|
+
const gitFlowService = getCompositionRoot().getGitFlowService();
|
|
996
|
+
const gitQuery = getCompositionRoot().getGitQueryAdapter();
|
|
997
|
+
const evidenceMonitor = getCompositionRoot().getEvidenceMonitor();
|
|
998
|
+
const orchestrator = getCompositionRoot().getOrchestrator();
|
|
999
|
+
|
|
1000
|
+
const currentBranch = gitFlowService.getCurrentBranch();
|
|
1001
|
+
const baseBranch = process.env.AST_BASE_BRANCH || 'develop';
|
|
1002
|
+
const isProtectedBranch = ['main', 'master', baseBranch].includes(currentBranch);
|
|
1003
|
+
|
|
1004
|
+
const uncommittedChanges = gitQuery.getUncommittedChanges();
|
|
1005
|
+
const hasUncommittedChanges = uncommittedChanges && uncommittedChanges.length > 0;
|
|
1006
|
+
|
|
1007
|
+
// 1. Protected Branch Guard
|
|
1008
|
+
if (isProtectedBranch && hasUncommittedChanges) {
|
|
1009
|
+
if (now - lastGitFlowNotification > NOTIFICATION_COOLDOWN) {
|
|
1010
|
+
const state = gitQuery.getBranchState(currentBranch);
|
|
1011
|
+
sendNotification(
|
|
1012
|
+
'⚠️ Git Flow Violation',
|
|
1013
|
+
`branch=${currentBranch} changes detected on protected branch. Create a feature branch.`,
|
|
1014
|
+
'Basso'
|
|
1015
|
+
);
|
|
1016
|
+
lastGitFlowNotification = now;
|
|
1017
|
+
}
|
|
1008
1018
|
}
|
|
1009
|
-
lastEvidenceNotification = now;
|
|
1010
|
-
}
|
|
1011
1019
|
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
const decision = await orchestrator.analyzeContext();
|
|
1015
|
-
if (decision.action === 'auto-execute' && decision.platforms.length > 0) {
|
|
1020
|
+
// 2. Evidence Freshness Guard
|
|
1021
|
+
if (evidenceMonitor.isStale() && (now - lastEvidenceNotification > NOTIFICATION_COOLDOWN)) {
|
|
1016
1022
|
try {
|
|
1017
1023
|
await evidenceMonitor.refresh();
|
|
1018
|
-
sendNotification('
|
|
1019
|
-
} catch (
|
|
1020
|
-
sendNotification('
|
|
1024
|
+
sendNotification('🔄 Evidence Auto-Updated', 'AI Evidence has been refreshed automatically', 'Purr');
|
|
1025
|
+
} catch (err) {
|
|
1026
|
+
sendNotification('⚠️ Evidence Stale', `Failed to auto-refresh evidence: ${err.message}`, 'Basso');
|
|
1021
1027
|
}
|
|
1028
|
+
lastEvidenceNotification = now;
|
|
1022
1029
|
}
|
|
1023
|
-
}
|
|
1024
1030
|
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1031
|
+
// 3. Autonomous Orchestration
|
|
1032
|
+
if (orchestrator.shouldReanalyze()) {
|
|
1033
|
+
const decision = await orchestrator.analyzeContext();
|
|
1034
|
+
if (decision.action === 'auto-execute' && decision.platforms.length > 0) {
|
|
1035
|
+
try {
|
|
1036
|
+
await evidenceMonitor.refresh();
|
|
1037
|
+
sendNotification('✅ AI Start Executed', `Platforms: ${decision.platforms.map(p => p.platform.toUpperCase()).join(', ')}`, 'Glass');
|
|
1038
|
+
} catch (e) {
|
|
1039
|
+
sendNotification('❌ AI Start Error', `Failed to execute: ${e.message}`, 'Basso');
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1029
1043
|
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
}
|
|
1044
|
+
} catch (error) {
|
|
1045
|
+
if (process.env.DEBUG) console.error('[MCP] Polling loop error:', error);
|
|
1046
|
+
}
|
|
1047
|
+
}, 30000);
|
|
1035
1048
|
|
|
1036
|
-
|
|
1037
|
-
|
|
1049
|
+
// AUTO-COMMIT: Only for project code changes (no node_modules, no library)
|
|
1050
|
+
setInterval(async () => {
|
|
1051
|
+
if (!AUTO_COMMIT_ENABLED) {
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1038
1054
|
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
const gitQuery = getCompositionRoot().getGitQueryAdapter();
|
|
1042
|
-
const gitCommand = getCompositionRoot().getGitCommandAdapter();
|
|
1055
|
+
const now = Date.now();
|
|
1056
|
+
if (now - lastAutoCommitTime < AUTO_COMMIT_INTERVAL) return;
|
|
1043
1057
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1058
|
+
try {
|
|
1059
|
+
const gitFlowService = getCompositionRoot().getGitFlowService();
|
|
1060
|
+
const gitQuery = getCompositionRoot().getGitQueryAdapter();
|
|
1061
|
+
const gitCommand = getCompositionRoot().getGitCommandAdapter();
|
|
1046
1062
|
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
}
|
|
1063
|
+
const currentBranch = gitFlowService.getCurrentBranch();
|
|
1064
|
+
const isFeatureBranch = currentBranch.match(/^(feature|fix|hotfix)\//);
|
|
1050
1065
|
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1066
|
+
if (!isFeatureBranch) {
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1054
1069
|
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
// Detect library installation path
|
|
1059
|
-
const libraryPath = getLibraryInstallPath();
|
|
1060
|
-
|
|
1061
|
-
// Filter changes: project code only
|
|
1062
|
-
const filesToCommit = uncommittedChanges.filter(file => {
|
|
1063
|
-
// Exclude noise
|
|
1064
|
-
if (file.startsWith('node_modules/') ||
|
|
1065
|
-
file.includes('package-lock.json') ||
|
|
1066
|
-
file.startsWith('.git/') ||
|
|
1067
|
-
file.startsWith('.cursor/') ||
|
|
1068
|
-
file.startsWith('.ast-intelligence/') ||
|
|
1069
|
-
file.startsWith('.vscode/') ||
|
|
1070
|
-
file.startsWith('.idea/')) {
|
|
1071
|
-
return false;
|
|
1070
|
+
if (gitFlowService.isClean()) {
|
|
1071
|
+
return;
|
|
1072
1072
|
}
|
|
1073
1073
|
|
|
1074
|
-
//
|
|
1075
|
-
|
|
1076
|
-
|
|
1074
|
+
// Get uncommitted changes
|
|
1075
|
+
const uncommittedChanges = gitQuery.getUncommittedChanges();
|
|
1076
|
+
|
|
1077
|
+
// Detect library installation path
|
|
1078
|
+
const libraryPath = getLibraryInstallPath();
|
|
1079
|
+
|
|
1080
|
+
// Filter changes: project code only
|
|
1081
|
+
const filesToCommit = uncommittedChanges.filter(file => {
|
|
1082
|
+
// Exclude noise
|
|
1083
|
+
if (file.startsWith('node_modules/') ||
|
|
1084
|
+
file.includes('package-lock.json') ||
|
|
1085
|
+
file.startsWith('.git/') ||
|
|
1086
|
+
file.startsWith('.cursor/') ||
|
|
1087
|
+
file.startsWith('.ast-intelligence/') ||
|
|
1088
|
+
file.startsWith('.vscode/') ||
|
|
1089
|
+
file.startsWith('.idea/')) {
|
|
1090
|
+
return false;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
// Exclude library itself
|
|
1094
|
+
if (libraryPath && file.startsWith(libraryPath + '/')) {
|
|
1095
|
+
return false;
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// Code/Doc files only
|
|
1099
|
+
const codeExtensions = ['.ts', '.tsx', '.js', '.jsx', '.swift', '.kt', '.py', '.java', '.go', '.rs', '.md', '.json', '.yaml', '.yml'];
|
|
1100
|
+
return codeExtensions.some(ext => file.endsWith(ext));
|
|
1101
|
+
});
|
|
1102
|
+
|
|
1103
|
+
if (filesToCommit.length === 0) {
|
|
1104
|
+
return;
|
|
1077
1105
|
}
|
|
1078
1106
|
|
|
1079
|
-
//
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1107
|
+
// Stage files
|
|
1108
|
+
filesToCommit.forEach(file => {
|
|
1109
|
+
gitCommand.add(file);
|
|
1110
|
+
});
|
|
1083
1111
|
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1112
|
+
const branchType = currentBranch.split('/')[0];
|
|
1113
|
+
const branchName = currentBranch.split('/').slice(1).join('/');
|
|
1114
|
+
const commitMessage = `${branchType}(auto): ${branchName} - ${filesToCommit.length} files`;
|
|
1087
1115
|
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
gitCommand.add(file);
|
|
1091
|
-
});
|
|
1116
|
+
// Commit
|
|
1117
|
+
gitCommand.commit(commitMessage);
|
|
1092
1118
|
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
const commitMessage = `${branchType}(auto): ${branchName} - ${filesToCommit.length} files`;
|
|
1119
|
+
sendNotification('✅ Auto-Commit', `${filesToCommit.length} files in ${currentBranch}`, 'Purr');
|
|
1120
|
+
lastAutoCommitTime = now;
|
|
1096
1121
|
|
|
1097
|
-
|
|
1098
|
-
|
|
1122
|
+
if (AUTO_PUSH_ENABLED) {
|
|
1123
|
+
if (gitFlowService.isGitHubAvailable()) {
|
|
1124
|
+
try {
|
|
1125
|
+
gitCommand.push('origin', currentBranch);
|
|
1126
|
+
sendNotification('✅ Auto-Push', `Pushed to origin/${currentBranch}`, 'Glass');
|
|
1099
1127
|
|
|
1100
|
-
|
|
1101
|
-
|
|
1128
|
+
if (AUTO_PR_ENABLED) {
|
|
1129
|
+
const baseBranch = process.env.AST_BASE_BRANCH || 'develop';
|
|
1130
|
+
const branchState = gitQuery.getBranchState(currentBranch);
|
|
1102
1131
|
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
if (AUTO_PR_ENABLED) {
|
|
1110
|
-
const baseBranch = process.env.AST_BASE_BRANCH || 'develop';
|
|
1111
|
-
const branchState = gitQuery.getBranchState(currentBranch);
|
|
1112
|
-
|
|
1113
|
-
if (branchState.ahead >= 3) {
|
|
1114
|
-
const prTitle = `Auto-PR: ${branchName}`;
|
|
1115
|
-
const prUrl = gitFlowService.createPullRequest(currentBranch, baseBranch, prTitle, 'Automated PR by Pumuki Git Flow');
|
|
1116
|
-
if (prUrl) {
|
|
1117
|
-
sendNotification('✅ Auto-PR Created', prTitle, 'Hero');
|
|
1132
|
+
if (branchState.ahead >= 3) {
|
|
1133
|
+
const prTitle = `Auto-PR: ${branchName}`;
|
|
1134
|
+
const prUrl = gitFlowService.createPullRequest(currentBranch, baseBranch, prTitle, 'Automated PR by Pumuki Git Flow');
|
|
1135
|
+
if (prUrl) {
|
|
1136
|
+
sendNotification('✅ Auto-PR Created', prTitle, 'Hero');
|
|
1137
|
+
}
|
|
1118
1138
|
}
|
|
1119
1139
|
}
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1140
|
+
} catch (e) {
|
|
1141
|
+
if (!e.message.includes('No remote')) {
|
|
1142
|
+
sendNotification('⚠️ Auto-Push Failed', 'Push manual required', 'Basso');
|
|
1143
|
+
}
|
|
1124
1144
|
}
|
|
1125
1145
|
}
|
|
1126
1146
|
}
|
|
1127
|
-
}
|
|
1128
1147
|
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
}, AUTO_COMMIT_INTERVAL);
|
|
1148
|
+
} catch (error) {
|
|
1149
|
+
if (process.env.DEBUG) console.error('[MCP] Auto-commit error:', error);
|
|
1150
|
+
}
|
|
1151
|
+
}, AUTO_COMMIT_INTERVAL);
|
|
1152
|
+
}
|
|
@@ -11,6 +11,9 @@ class McpProtocolHandler {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
start(messageHandler) {
|
|
14
|
+
// Log that we're starting
|
|
15
|
+
process.stderr.write('[MCP] Protocol handler starting...\n');
|
|
16
|
+
|
|
14
17
|
this.inputStream.on('data', (chunk) => {
|
|
15
18
|
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk), 'utf8');
|
|
16
19
|
void this._handleChunk(buf, messageHandler);
|
|
@@ -25,6 +28,9 @@ class McpProtocolHandler {
|
|
|
25
28
|
if (this.logger) this.logger.error('MCP_STDIN_ERROR', { error: err.message });
|
|
26
29
|
process.exit(1);
|
|
27
30
|
});
|
|
31
|
+
|
|
32
|
+
// Log that we're ready
|
|
33
|
+
process.stderr.write('[MCP] Protocol handler ready\n');
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
async _handleChunk(chunk, messageHandler) {
|