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.0",
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(handleMcpMessage);
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
- * Polling loop for background notifications and automations
988
+ * Start polling loops for background notifications and automations
989
+ * Called ONLY after MCP handshake is complete
972
990
  */
973
- setInterval(async () => {
974
- try {
975
- const now = Date.now();
976
- const gitFlowService = getCompositionRoot().getGitFlowService();
977
- const gitQuery = getCompositionRoot().getGitQueryAdapter();
978
- const evidenceMonitor = getCompositionRoot().getEvidenceMonitor();
979
- const orchestrator = getCompositionRoot().getOrchestrator();
980
-
981
- const currentBranch = gitFlowService.getCurrentBranch();
982
- const baseBranch = process.env.AST_BASE_BRANCH || 'develop';
983
- const isProtectedBranch = ['main', 'master', baseBranch].includes(currentBranch);
984
-
985
- const uncommittedChanges = gitQuery.getUncommittedChanges();
986
- const hasUncommittedChanges = uncommittedChanges && uncommittedChanges.length > 0;
987
-
988
- // 1. Protected Branch Guard
989
- if (isProtectedBranch && hasUncommittedChanges) {
990
- if (now - lastGitFlowNotification > NOTIFICATION_COOLDOWN) {
991
- const state = gitQuery.getBranchState(currentBranch);
992
- sendNotification(
993
- '⚠️ Git Flow Violation',
994
- `branch=${currentBranch} changes detected on protected branch. Create a feature branch.`,
995
- 'Basso'
996
- );
997
- lastGitFlowNotification = now;
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
- // 3. Autonomous Orchestration
1013
- if (orchestrator.shouldReanalyze()) {
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(' AI Start Executed', `Platforms: ${decision.platforms.map(p => p.platform.toUpperCase()).join(', ')}`, 'Glass');
1019
- } catch (e) {
1020
- sendNotification(' AI Start Error', `Failed to execute: ${e.message}`, 'Basso');
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
- } catch (error) {
1026
- if (process.env.DEBUG) console.error('[MCP] Polling loop error:', error);
1027
- }
1028
- }, 30000);
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
- // AUTO-COMMIT: Only for project code changes (no node_modules, no library)
1031
- setInterval(async () => {
1032
- if (!AUTO_COMMIT_ENABLED) {
1033
- return;
1034
- }
1044
+ } catch (error) {
1045
+ if (process.env.DEBUG) console.error('[MCP] Polling loop error:', error);
1046
+ }
1047
+ }, 30000);
1035
1048
 
1036
- const now = Date.now();
1037
- if (now - lastAutoCommitTime < AUTO_COMMIT_INTERVAL) return;
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
- try {
1040
- const gitFlowService = getCompositionRoot().getGitFlowService();
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
- const currentBranch = gitFlowService.getCurrentBranch();
1045
- const isFeatureBranch = currentBranch.match(/^(feature|fix|hotfix)\//);
1058
+ try {
1059
+ const gitFlowService = getCompositionRoot().getGitFlowService();
1060
+ const gitQuery = getCompositionRoot().getGitQueryAdapter();
1061
+ const gitCommand = getCompositionRoot().getGitCommandAdapter();
1046
1062
 
1047
- if (!isFeatureBranch) {
1048
- return;
1049
- }
1063
+ const currentBranch = gitFlowService.getCurrentBranch();
1064
+ const isFeatureBranch = currentBranch.match(/^(feature|fix|hotfix)\//);
1050
1065
 
1051
- if (gitFlowService.isClean()) {
1052
- return;
1053
- }
1066
+ if (!isFeatureBranch) {
1067
+ return;
1068
+ }
1054
1069
 
1055
- // Get uncommitted changes
1056
- const uncommittedChanges = gitQuery.getUncommittedChanges();
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
- // Exclude library itself
1075
- if (libraryPath && file.startsWith(libraryPath + '/')) {
1076
- return false;
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
- // Code/Doc files only
1080
- const codeExtensions = ['.ts', '.tsx', '.js', '.jsx', '.swift', '.kt', '.py', '.java', '.go', '.rs', '.md', '.json', '.yaml', '.yml'];
1081
- return codeExtensions.some(ext => file.endsWith(ext));
1082
- });
1107
+ // Stage files
1108
+ filesToCommit.forEach(file => {
1109
+ gitCommand.add(file);
1110
+ });
1083
1111
 
1084
- if (filesToCommit.length === 0) {
1085
- return;
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
- // Stage files
1089
- filesToCommit.forEach(file => {
1090
- gitCommand.add(file);
1091
- });
1116
+ // Commit
1117
+ gitCommand.commit(commitMessage);
1092
1118
 
1093
- const branchType = currentBranch.split('/')[0];
1094
- const branchName = currentBranch.split('/').slice(1).join('/');
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
- // Commit
1098
- gitCommand.commit(commitMessage);
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
- sendNotification('✅ Auto-Commit', `${filesToCommit.length} files in ${currentBranch}`, 'Purr');
1101
- lastAutoCommitTime = now;
1128
+ if (AUTO_PR_ENABLED) {
1129
+ const baseBranch = process.env.AST_BASE_BRANCH || 'develop';
1130
+ const branchState = gitQuery.getBranchState(currentBranch);
1102
1131
 
1103
- if (AUTO_PUSH_ENABLED) {
1104
- if (gitFlowService.isGitHubAvailable()) {
1105
- try {
1106
- gitCommand.push('origin', currentBranch);
1107
- sendNotification('✅ Auto-Push', `Pushed to origin/${currentBranch}`, 'Glass');
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
- } catch (e) {
1122
- if (!e.message.includes('No remote')) {
1123
- sendNotification('⚠️ Auto-Push Failed', 'Push manual required', 'Basso');
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
- } catch (error) {
1130
- if (process.env.DEBUG) console.error('[MCP] Auto-commit error:', error);
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) {