pumuki-ast-hooks 5.5.65 → 5.6.0

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 (24) hide show
  1. package/README.md +152 -6
  2. package/bin/__tests__/check-version.spec.js +32 -57
  3. package/package.json +1 -1
  4. package/scripts/hooks-system/.audit_tmp/hook-metrics.jsonl +4 -0
  5. package/scripts/hooks-system/bin/__tests__/check-version.spec.js +37 -57
  6. package/scripts/hooks-system/bin/cli.js +109 -0
  7. package/scripts/hooks-system/infrastructure/ast/ast-core.js +124 -0
  8. package/scripts/hooks-system/infrastructure/ast/backend/ast-backend.js +3 -1
  9. package/scripts/hooks-system/infrastructure/ast/frontend/analyzers/__tests__/FrontendArchitectureDetector.spec.js +4 -1
  10. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/__tests__/iOSASTIntelligentAnalyzer.spec.js +3 -1
  11. package/scripts/hooks-system/infrastructure/ast/ios/detectors/ios-ast-intelligent-strategies.js +1 -1
  12. package/scripts/hooks-system/infrastructure/cascade-hooks/README.md +114 -0
  13. package/scripts/hooks-system/infrastructure/cascade-hooks/cascade-hooks-config.json +20 -0
  14. package/scripts/hooks-system/infrastructure/cascade-hooks/claude-code-hook.sh +127 -0
  15. package/scripts/hooks-system/infrastructure/cascade-hooks/post-write-code-hook.js +72 -0
  16. package/scripts/hooks-system/infrastructure/cascade-hooks/pre-write-code-hook.js +167 -0
  17. package/scripts/hooks-system/infrastructure/cascade-hooks/universal-hook-adapter.js +186 -0
  18. package/scripts/hooks-system/infrastructure/mcp/ast-intelligence-automation.js +739 -24
  19. package/scripts/hooks-system/infrastructure/observability/MetricsCollector.js +221 -0
  20. package/scripts/hooks-system/infrastructure/observability/index.js +23 -0
  21. package/scripts/hooks-system/infrastructure/orchestration/__tests__/intelligent-audit.spec.js +177 -0
  22. package/scripts/hooks-system/infrastructure/resilience/CircuitBreaker.js +229 -0
  23. package/scripts/hooks-system/infrastructure/resilience/RetryPolicy.js +141 -0
  24. package/scripts/hooks-system/infrastructure/resilience/index.js +34 -0
@@ -28,6 +28,275 @@ const env = require('../../config/env');
28
28
 
29
29
  const MCP_VERSION = '2024-11-05';
30
30
 
31
+ // =============================================================================
32
+ // ENTERPRISE MODULES (Lazy Loading)
33
+ // =============================================================================
34
+ let _metricsModule = null;
35
+ let _resilienceModule = null;
36
+
37
+ function getMetrics() {
38
+ if (!_metricsModule) {
39
+ try {
40
+ _metricsModule = require('../observability');
41
+ } catch (e) {
42
+ _metricsModule = {
43
+ gateCheckCounter: { inc: () => { } },
44
+ gateCheckDuration: { observe: () => { } },
45
+ mcpToolCallCounter: { inc: () => { } },
46
+ globalCollector: { toPrometheusFormat: () => '', getMetricsJSON: () => ({}) }
47
+ };
48
+ }
49
+ }
50
+ return _metricsModule;
51
+ }
52
+
53
+ function getResilience() {
54
+ if (!_resilienceModule) {
55
+ try {
56
+ _resilienceModule = require('../resilience');
57
+ } catch (e) {
58
+ _resilienceModule = {
59
+ mcpCircuit: { execute: fn => fn(), getState: () => ({ state: 'CLOSED' }) },
60
+ gitCircuit: { execute: fn => fn(), getState: () => ({ state: 'CLOSED' }) },
61
+ globalRegistry: { getAll: () => ({}) }
62
+ };
63
+ }
64
+ }
65
+ return _resilienceModule;
66
+ }
67
+
68
+ // =============================================================================
69
+ // RULES ENFORCEMENT: Force AI to actually APPLY rules, not just read them
70
+ // Revolutionary Pre-Flight Validation System inspired by tdd-guard
71
+ // =============================================================================
72
+ const rulesEnforcement = {
73
+ TOP_CRITICAL_RULES: [
74
+ '🔴 BDD→TDD: Crear test ANTES de implementar. Sin test = BLOQUEADO',
75
+ '🔴 No empty catch: Siempre loggear o propagar errores',
76
+ '🔴 No Singleton: Usar Inyección de Dependencias',
77
+ '🔴 No comentarios: Nombres autodescriptivos',
78
+ '🔴 Verificar que compila ANTES de sugerir código'
79
+ ],
80
+
81
+ sessionState: {
82
+ testsCreatedThisSession: [],
83
+ implementationsThisSession: [],
84
+ lastTestTimestamp: null,
85
+ tddCycleActive: false
86
+ },
87
+
88
+ CODE_PATTERNS_TO_BLOCK: [
89
+ { pattern: /catch\s*\([^)]*\)\s*\{\s*\}/g, rule: 'empty_catch', message: '❌ Empty catch block detected' },
90
+ { pattern: /\.shared\s*[,\);\n]/g, rule: 'singleton', message: '❌ Singleton pattern (.shared) detected' },
91
+ { pattern: /static\s+let\s+shared/g, rule: 'singleton', message: '❌ Singleton declaration detected' },
92
+ { pattern: /\/\/[^\n]+/g, rule: 'comments', message: '⚠️ Code comments detected (prefer self-documenting names)' },
93
+ { pattern: /\/\*[\s\S]*?\*\//g, rule: 'comments', message: '⚠️ Block comments detected' },
94
+ { pattern: /DispatchQueue\.(main|global)/g, rule: 'gcd_ios', message: '❌ [iOS] GCD detected - use async/await' },
95
+ { pattern: /@escaping\s+\([^)]*\)\s*->/g, rule: 'completion_handler', message: '❌ [iOS] Completion handler detected - use async/await' },
96
+ { pattern: /ObservableObject/g, rule: 'observable_object', message: '❌ [iOS] ObservableObject detected - use @Observable (iOS 17+)' },
97
+ { pattern: /AnyView/g, rule: 'any_view', message: '❌ [iOS] AnyView detected - affects performance' }
98
+ ],
99
+
100
+ getPreImplementationChecklist() {
101
+ return {
102
+ mandatory_checklist: [
103
+ '☐ ¿Existe test para esta funcionalidad? Si NO → CREAR TEST PRIMERO',
104
+ '☐ ¿El código sigue Clean Architecture?',
105
+ '☐ ¿Cumple SOLID (SRP, OCP, LSP, ISP, DIP)?',
106
+ '☐ ¿Usa guard/early returns?',
107
+ '☐ ¿Nombres autodescriptivos en inglés?',
108
+ '☐ ¿Compila sin errores?'
109
+ ],
110
+ blocking_patterns: [
111
+ '❌ Implementar sin test previo',
112
+ '❌ catch vacío o silenciar errores',
113
+ '❌ Usar Singleton en lugar de DI',
114
+ '❌ Añadir comentarios explicativos',
115
+ '❌ Código que no compila'
116
+ ],
117
+ instruction: '🚨 ANTES de escribir código, VERIFICA cada punto del checklist. Si falta test, CRÉALO PRIMERO.'
118
+ };
119
+ },
120
+
121
+ generateRulesReminder() {
122
+ const tddStatus = this.sessionState.tddCycleActive
123
+ ? '✅ TDD CYCLE ACTIVE'
124
+ : '❌ NO TEST CREATED YET';
125
+ return `
126
+ ╔══════════════════════════════════════════════════════════════════╗
127
+ ║ 🚨 REGLAS CRÍTICAS - DEBES APLICAR EN CADA IMPLEMENTACIÓN 🚨 ║
128
+ ╠══════════════════════════════════════════════════════════════════╣
129
+ ║ ${this.TOP_CRITICAL_RULES.join('\n║ ')}
130
+ ╠══════════════════════════════════════════════════════════════════╣
131
+ ║ TDD Status: ${tddStatus}
132
+ ║ Tests this session: ${this.sessionState.testsCreatedThisSession.length}
133
+ ╠══════════════════════════════════════════════════════════════════╣
134
+ ║ ⚠️ Si implementas sin test primero = VIOLACIÓN CRÍTICA ║
135
+ ║ ✅ Crea el test AHORA, antes de cualquier implementación ║
136
+ ╚══════════════════════════════════════════════════════════════════╝`;
137
+ },
138
+
139
+ validateProposedCode(code, targetFile, platform) {
140
+ const violations = [];
141
+
142
+ for (const patternDef of this.CODE_PATTERNS_TO_BLOCK) {
143
+ if (patternDef.rule.includes('ios') && platform !== 'ios') continue;
144
+
145
+ const matches = code.match(patternDef.pattern);
146
+ if (matches && matches.length > 0) {
147
+ violations.push({
148
+ rule: patternDef.rule,
149
+ severity: patternDef.message.startsWith('❌') ? 'CRITICAL' : 'WARNING',
150
+ message: patternDef.message,
151
+ occurrences: matches.length,
152
+ samples: matches.slice(0, 3).map(m => m.substring(0, 50))
153
+ });
154
+ }
155
+ }
156
+
157
+ return violations;
158
+ },
159
+
160
+ validateProposedAction(actionType, targetFile, proposedCode = null) {
161
+ const violations = [];
162
+ const isTestFile = /\.(spec|test)\.(js|ts|swift|kt)$/.test(targetFile || '');
163
+ const isImplementationFile = !isTestFile && /\.(js|ts|tsx|jsx|swift|kt)$/.test(targetFile || '');
164
+
165
+ if ((actionType === 'create_file' || actionType === 'edit') && isImplementationFile) {
166
+ if (!this.sessionState.tddCycleActive && this.sessionState.testsCreatedThisSession.length === 0) {
167
+ violations.push({
168
+ rule: 'BDD→TDD',
169
+ severity: 'CRITICAL',
170
+ message: `🚨 BLOQUEADO: Intentas crear/editar ${targetFile} sin haber creado un test primero.`,
171
+ suggestion: 'Crea primero el archivo .spec.js/.test.ts con el test que debe fallar (RED phase)',
172
+ action_required: 'CREATE_TEST_FIRST'
173
+ });
174
+ }
175
+ }
176
+
177
+ if (proposedCode) {
178
+ const platform = this.detectPlatformFromFile(targetFile);
179
+ const codeViolations = this.validateProposedCode(proposedCode, targetFile, platform);
180
+ violations.push(...codeViolations);
181
+ }
182
+
183
+ const hasCriticalViolations = violations.some(v => v.severity === 'CRITICAL');
184
+
185
+ return {
186
+ allowed: !hasCriticalViolations,
187
+ hasViolations: violations.length > 0,
188
+ violations,
189
+ tddStatus: {
190
+ active: this.sessionState.tddCycleActive,
191
+ testsCreated: this.sessionState.testsCreatedThisSession.length,
192
+ implementationsCount: this.sessionState.implementationsThisSession.length
193
+ },
194
+ reminder: this.generateRulesReminder(),
195
+ enforcement_message: hasCriticalViolations
196
+ ? '🚫 ACTION BLOCKED: Fix critical violations before proceeding'
197
+ : violations.length > 0
198
+ ? '⚠️ Warnings detected - review before proceeding'
199
+ : '✅ Pre-flight check passed'
200
+ };
201
+ },
202
+
203
+ detectPlatformFromFile(filePath) {
204
+ if (!filePath) return 'unknown';
205
+ if (filePath.endsWith('.swift')) return 'ios';
206
+ if (filePath.endsWith('.kt') || filePath.endsWith('.kts')) return 'android';
207
+ if (filePath.includes('/backend/') || filePath.includes('nestjs')) return 'backend';
208
+ if (filePath.endsWith('.tsx') || filePath.endsWith('.jsx')) return 'frontend';
209
+ return 'backend';
210
+ },
211
+
212
+ recordTestCreated(testFile) {
213
+ this.sessionState.testsCreatedThisSession.push({
214
+ file: testFile,
215
+ timestamp: Date.now()
216
+ });
217
+ this.sessionState.lastTestTimestamp = Date.now();
218
+ this.sessionState.tddCycleActive = true;
219
+ },
220
+
221
+ recordImplementation(implFile) {
222
+ this.sessionState.implementationsThisSession.push({
223
+ file: implFile,
224
+ timestamp: Date.now()
225
+ });
226
+ },
227
+
228
+ resetSession() {
229
+ this.sessionState = {
230
+ testsCreatedThisSession: [],
231
+ implementationsThisSession: [],
232
+ lastTestTimestamp: null,
233
+ tddCycleActive: false
234
+ };
235
+ }
236
+ };
237
+
238
+ // =============================================================================
239
+ // GATE ENFORCEMENT: Track if ai_gate_check was called this session
240
+ // =============================================================================
241
+ const gateSession = {
242
+ lastCheckTimestamp: null,
243
+ lastCheckResult: null,
244
+ sessionId: `session-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`,
245
+ checkCount: 0,
246
+ GATE_VALIDITY_MS: 10 * 60 * 1000, // Gate valid for 10 minutes
247
+
248
+ recordCheck(result) {
249
+ this.lastCheckTimestamp = Date.now();
250
+ this.lastCheckResult = result;
251
+ this.checkCount++;
252
+ },
253
+
254
+ isGateValid() {
255
+ if (!this.lastCheckTimestamp) return false;
256
+ const elapsed = Date.now() - this.lastCheckTimestamp;
257
+ return elapsed < this.GATE_VALIDITY_MS && this.lastCheckResult?.status === 'ALLOWED';
258
+ },
259
+
260
+ getEnforcementStatus() {
261
+ if (!this.lastCheckTimestamp) {
262
+ return {
263
+ enforced: true,
264
+ blocked: true,
265
+ reason: '🚨 GATE NOT EXECUTED: You MUST call ai_gate_check before using any other tool.',
266
+ action: 'Call ai_gate_check first'
267
+ };
268
+ }
269
+
270
+ const elapsed = Date.now() - this.lastCheckTimestamp;
271
+ if (elapsed > this.GATE_VALIDITY_MS) {
272
+ return {
273
+ enforced: true,
274
+ blocked: true,
275
+ reason: `🚨 GATE EXPIRED: Last check was ${Math.round(elapsed / 60000)} minutes ago. Re-run ai_gate_check.`,
276
+ action: 'Call ai_gate_check to refresh'
277
+ };
278
+ }
279
+
280
+ if (this.lastCheckResult?.status === 'BLOCKED') {
281
+ return {
282
+ enforced: true,
283
+ blocked: true,
284
+ reason: '🚨 GATE BLOCKED: Previous gate check failed. Fix violations first.',
285
+ violations: this.lastCheckResult.violations,
286
+ action: 'Fix violations and re-run ai_gate_check'
287
+ };
288
+ }
289
+
290
+ return {
291
+ enforced: true,
292
+ blocked: false,
293
+ reason: null,
294
+ gateStatus: this.lastCheckResult?.status,
295
+ validFor: Math.round((this.GATE_VALIDITY_MS - elapsed) / 60000) + ' minutes'
296
+ };
297
+ }
298
+ };
299
+
31
300
  // Configuration - LAZY LOADING to avoid blocking MCP initialization
32
301
  function safeGitRoot(startDir) {
33
302
  try {
@@ -577,44 +846,63 @@ function checkBranchChangesCoherence(branchName, uncommittedChanges) {
577
846
  }
578
847
 
579
848
  /**
580
- * Load platform rules from .cursor/rules or .windsurf/rules
581
- * Returns the content of the rules file for the detected platform
849
+ * Load ALL platform rules from .cursor/rules or .windsurf/rules
850
+ * ALWAYS loads: gold (base) + ios + android + backend + frontend
851
+ * Returns the complete content of ALL rules files
582
852
  */
583
853
  async function loadPlatformRules(platforms) {
584
854
  const DynamicRulesLoader = require('../../application/services/DynamicRulesLoader');
585
855
  const loader = new DynamicRulesLoader();
586
856
  const rules = {};
587
857
  const criticalRules = [];
858
+ const fullRulesContent = {};
859
+
860
+ const ALL_RULE_FILES = [
861
+ { key: 'gold', file: 'rulesgold.mdc', priority: 0 },
862
+ { key: 'ios', file: 'rulesios.mdc', priority: 1 },
863
+ { key: 'android', file: 'rulesandroid.mdc', priority: 1 },
864
+ { key: 'backend', file: 'rulesbackend.mdc', priority: 1 },
865
+ { key: 'frontend', file: 'rulesfront.mdc', priority: 1 }
866
+ ];
588
867
 
589
- for (const platform of platforms) {
868
+ for (const ruleFile of ALL_RULE_FILES) {
590
869
  try {
591
- const content = await loader.loadRule(`rules${platform}.mdc`);
870
+ const content = await loader.loadRule(ruleFile.file);
592
871
  if (content) {
593
- rules[platform] = content;
594
- const criticalPatterns = extractCriticalPatterns(content, platform);
872
+ rules[ruleFile.key] = true;
873
+ fullRulesContent[ruleFile.key] = content;
874
+ const criticalPatterns = extractCriticalPatterns(content, ruleFile.key);
595
875
  criticalRules.push(...criticalPatterns);
596
876
  }
597
877
  } catch (error) {
598
878
  if (process.env.DEBUG) {
599
- process.stderr.write(`[MCP] Failed to load rules for ${platform}: ${error.message}\n`);
879
+ process.stderr.write(`[MCP] Failed to load ${ruleFile.file}: ${error.message}\n`);
600
880
  }
601
881
  }
602
882
  }
603
883
 
604
- try {
605
- const goldContent = await loader.loadRule('rulesgold.mdc');
606
- if (goldContent) {
607
- rules.gold = goldContent;
608
- const goldPatterns = extractCriticalPatterns(goldContent, 'gold');
609
- criticalRules.push(...goldPatterns);
610
- }
611
- } catch (error) {
612
- if (process.env.DEBUG) {
613
- process.stderr.write(`[MCP] Failed to load gold rules: ${error.message}\n`);
614
- }
615
- }
884
+ const topCriticalRules = criticalRules
885
+ .filter(r => r.severity === 'CRITICAL')
886
+ .slice(0, 20)
887
+ .map(r => `[${r.platform.toUpperCase()}] ${r.rule}`);
888
+
889
+ const topMandatoryRules = criticalRules
890
+ .filter(r => r.severity === 'MANDATORY')
891
+ .slice(0, 20)
892
+ .map(r => `[${r.platform.toUpperCase()}] ${r.rule}`);
616
893
 
617
- return { rules, criticalRules };
894
+ return {
895
+ rules,
896
+ criticalRules,
897
+ fullRulesContent,
898
+ summary: {
899
+ filesLoaded: Object.keys(rules),
900
+ totalCriticalRules: criticalRules.filter(r => r.severity === 'CRITICAL').length,
901
+ totalMandatoryRules: criticalRules.filter(r => r.severity === 'MANDATORY').length,
902
+ topCriticalRules,
903
+ topMandatoryRules
904
+ }
905
+ };
618
906
  }
619
907
 
620
908
  /**
@@ -1034,6 +1322,20 @@ async function aiGateCheck() {
1034
1322
 
1035
1323
  const finalBlocked = isBlocked || !rulesLoadedSuccessfully;
1036
1324
 
1325
+ let humanIntent = null;
1326
+ let semanticSnapshot = null;
1327
+ try {
1328
+ if (fs.existsSync(EVIDENCE_FILE)) {
1329
+ const evidence = JSON.parse(fs.readFileSync(EVIDENCE_FILE, 'utf8'));
1330
+ humanIntent = evidence.human_intent || null;
1331
+ semanticSnapshot = evidence.semantic_snapshot || null;
1332
+ }
1333
+ } catch (evidenceReadError) {
1334
+ if (process.env.DEBUG) {
1335
+ process.stderr.write(`[MCP] Failed to read cognitive layers from evidence: ${evidenceReadError.message}\n`);
1336
+ }
1337
+ }
1338
+
1037
1339
  return {
1038
1340
  status: finalBlocked ? 'BLOCKED' : 'ALLOWED',
1039
1341
  timestamp: new Date().toISOString(),
@@ -1041,6 +1343,8 @@ async function aiGateCheck() {
1041
1343
  violations,
1042
1344
  warnings,
1043
1345
  autoFixes,
1346
+ human_intent: humanIntent,
1347
+ semantic_snapshot: semanticSnapshot,
1044
1348
  mandatory_rules: rulesLoadedSuccessfully
1045
1349
  ? { ...mandatoryRules, status: 'LOADED_OK' }
1046
1350
  : mandatoryRules,
@@ -1049,17 +1353,59 @@ async function aiGateCheck() {
1049
1353
  : `🚦 ALLOWED: Gate passed. ${mandatoryRules.totalRulesCount} critical rules loaded and verified.`,
1050
1354
  instructions: finalBlocked
1051
1355
  ? 'DO NOT proceed with user task. Announce violations and fix them first.'
1052
- : `✅ ${mandatoryRules.totalRulesCount} RULES LOADED. Sample: ${mandatoryRules.rulesSample.slice(0, 2).join(' | ')}... Review ALL rules in mandatory_rules.criticalRules before ANY code generation.`
1356
+ : `✅ ${mandatoryRules.totalRulesCount} RULES LOADED. Sample: ${mandatoryRules.rulesSample.slice(0, 2).join(' | ')}... Review ALL rules in mandatory_rules.criticalRules before ANY code generation.`,
1357
+ cognitive_context: humanIntent?.primary_goal
1358
+ ? `🎯 USER INTENT: ${humanIntent.primary_goal} (confidence: ${humanIntent.confidence_level || 'unset'})`
1359
+ : null
1053
1360
  };
1054
1361
  };
1055
1362
 
1056
1363
  const result = await runWithTimeout(core, gateTimeoutMs);
1057
1364
  if (result.ok) {
1058
- return result.value;
1365
+ gateSession.recordCheck(result.value);
1366
+
1367
+ const preChecklist = rulesEnforcement.getPreImplementationChecklist();
1368
+ const rulesReminder = rulesEnforcement.generateRulesReminder();
1369
+
1370
+ return {
1371
+ ...result.value,
1372
+ _enforcement: {
1373
+ session_id: gateSession.sessionId,
1374
+ check_count: gateSession.checkCount,
1375
+ valid_for_minutes: Math.round(gateSession.GATE_VALIDITY_MS / 60000)
1376
+ },
1377
+ _mandatory_pre_write_protocol: {
1378
+ enabled: true,
1379
+ requirement: '🚨 MANDATORY: You MUST call pre_flight_check BEFORE any edit/write_to_file/create_file operation',
1380
+ enforcement_level: 'BLOCKING',
1381
+ workflow: [
1382
+ '1️⃣ BEFORE ANY CODE WRITE: Call pre_flight_check({ action_type, target_file, proposed_code })',
1383
+ '2️⃣ IF pre_flight_check returns blocked=true: DO NOT WRITE. Fix violations first.',
1384
+ '3️⃣ IF pre_flight_check returns allowed=true: PROCEED with the write operation.',
1385
+ '4️⃣ NEVER skip pre_flight_check - violations will be caught at pre-commit and block your work.'
1386
+ ],
1387
+ consequence_of_skipping: 'If you skip pre_flight_check, the Git pre-commit hook will BLOCK the commit with all violations accumulated. This wastes user time and breaks the development flow.',
1388
+ ide_hooks_active: 'Windsurf/Claude Code/OpenCode have automatic blocking. For other IDEs, pre_flight_check is your ONLY way to catch violations early.'
1389
+ },
1390
+ _rules_enforcement: {
1391
+ top_5_critical: rulesEnforcement.TOP_CRITICAL_RULES,
1392
+ pre_implementation_checklist: preChecklist.mandatory_checklist,
1393
+ blocking_patterns: preChecklist.blocking_patterns,
1394
+ active_reminder: rulesReminder,
1395
+ bdd_tdd_warning: '🚨 CREAR TEST PRIMERO. Si implementas sin test = VIOLACIÓN CRÍTICA que bloqueará el commit.',
1396
+ implementation_order: [
1397
+ '1️⃣ PRIMERO: Crear/localizar el archivo .spec.js o .test.ts',
1398
+ '2️⃣ SEGUNDO: Escribir el test que falla (RED)',
1399
+ '3️⃣ TERCERO: Implementar el código mínimo para pasar (GREEN)',
1400
+ '4️⃣ CUARTO: Refactorizar si es necesario (REFACTOR)',
1401
+ '5️⃣ QUINTO: Verificar que compila antes de sugerir'
1402
+ ]
1403
+ }
1404
+ };
1059
1405
  }
1060
1406
 
1061
1407
  const currentBranch = getCurrentGitBranch(REPO_ROOT);
1062
- return {
1408
+ const timeoutResult = {
1063
1409
  status: 'BLOCKED',
1064
1410
  timestamp: new Date().toISOString(),
1065
1411
  branch: currentBranch,
@@ -1076,6 +1422,8 @@ async function aiGateCheck() {
1076
1422
  summary: '🚫 BLOCKED: Gate check timed out.',
1077
1423
  instructions: 'DO NOT proceed with user task. Retry the gate check.'
1078
1424
  };
1425
+ gateSession.recordCheck(timeoutResult);
1426
+ return timeoutResult;
1079
1427
  }
1080
1428
 
1081
1429
  /**
@@ -1119,6 +1467,267 @@ async function readPlatformRulesHandler(params) {
1119
1467
  }
1120
1468
  }
1121
1469
 
1470
+ /**
1471
+ * Set human intent in .AI_EVIDENCE.json
1472
+ */
1473
+ function setHumanIntent(params) {
1474
+ const { goal, secondary_goals, non_goals, constraints, confidence, expires_hours } = params;
1475
+
1476
+ if (!goal) {
1477
+ return { success: false, error: 'Goal is required' };
1478
+ }
1479
+
1480
+ try {
1481
+ if (!fs.existsSync(EVIDENCE_FILE)) {
1482
+ return { success: false, error: '.AI_EVIDENCE.json not found' };
1483
+ }
1484
+
1485
+ const evidence = JSON.parse(fs.readFileSync(EVIDENCE_FILE, 'utf8'));
1486
+ const expiresAt = new Date(Date.now() + (expires_hours || 24) * 60 * 60 * 1000);
1487
+
1488
+ evidence.human_intent = {
1489
+ primary_goal: goal,
1490
+ secondary_goals: secondary_goals || [],
1491
+ non_goals: non_goals || [],
1492
+ constraints: constraints || [],
1493
+ confidence_level: confidence || 'medium',
1494
+ set_by: 'mcp',
1495
+ set_at: new Date().toISOString(),
1496
+ expires_at: expiresAt.toISOString(),
1497
+ preservation_count: 0
1498
+ };
1499
+
1500
+ fs.writeFileSync(EVIDENCE_FILE, JSON.stringify(evidence, null, 2));
1501
+
1502
+ return {
1503
+ success: true,
1504
+ message: `Human intent set: "${goal}"`,
1505
+ expires_at: expiresAt.toISOString(),
1506
+ human_intent: evidence.human_intent
1507
+ };
1508
+ } catch (error) {
1509
+ return { success: false, error: `Failed to set intent: ${error.message}` };
1510
+ }
1511
+ }
1512
+
1513
+ /**
1514
+ * Get current human intent from .AI_EVIDENCE.json
1515
+ */
1516
+ function getHumanIntent() {
1517
+ try {
1518
+ if (!fs.existsSync(EVIDENCE_FILE)) {
1519
+ return { success: false, error: '.AI_EVIDENCE.json not found' };
1520
+ }
1521
+
1522
+ const evidence = JSON.parse(fs.readFileSync(EVIDENCE_FILE, 'utf8'));
1523
+ const intent = evidence.human_intent;
1524
+
1525
+ if (!intent || !intent.primary_goal) {
1526
+ return {
1527
+ success: true,
1528
+ has_intent: false,
1529
+ message: 'No human intent set',
1530
+ human_intent: null
1531
+ };
1532
+ }
1533
+
1534
+ const isExpired = intent.expires_at && new Date(intent.expires_at) < new Date();
1535
+
1536
+ return {
1537
+ success: true,
1538
+ has_intent: !isExpired,
1539
+ is_expired: isExpired,
1540
+ human_intent: intent,
1541
+ cognitive_context: `🎯 USER INTENT: ${intent.primary_goal} (confidence: ${intent.confidence_level || 'unset'})`
1542
+ };
1543
+ } catch (error) {
1544
+ return { success: false, error: `Failed to get intent: ${error.message}` };
1545
+ }
1546
+ }
1547
+
1548
+ /**
1549
+ * Clear human intent in .AI_EVIDENCE.json
1550
+ */
1551
+ function clearHumanIntent() {
1552
+ try {
1553
+ if (!fs.existsSync(EVIDENCE_FILE)) {
1554
+ return { success: false, error: '.AI_EVIDENCE.json not found' };
1555
+ }
1556
+
1557
+ const evidence = JSON.parse(fs.readFileSync(EVIDENCE_FILE, 'utf8'));
1558
+
1559
+ evidence.human_intent = {
1560
+ primary_goal: null,
1561
+ secondary_goals: [],
1562
+ non_goals: [],
1563
+ constraints: [],
1564
+ confidence_level: 'unset',
1565
+ set_by: null,
1566
+ set_at: null,
1567
+ expires_at: null,
1568
+ preservation_count: 0,
1569
+ _hint: 'Set via MCP: mcp1_set_human_intent or CLI: ast-hooks intent set --goal "your goal"'
1570
+ };
1571
+
1572
+ fs.writeFileSync(EVIDENCE_FILE, JSON.stringify(evidence, null, 2));
1573
+
1574
+ return {
1575
+ success: true,
1576
+ message: 'Human intent cleared',
1577
+ human_intent: evidence.human_intent
1578
+ };
1579
+ } catch (error) {
1580
+ return { success: false, error: `Failed to clear intent: ${error.message}` };
1581
+ }
1582
+ }
1583
+
1584
+ /**
1585
+ * 🚀 REVOLUTIONARY: Pre-Flight Check - Validates code BEFORE writing it
1586
+ * Inspired by tdd-guard: https://www.brgr.one/blog/ai-coding-agents-tdd-enforcement
1587
+ *
1588
+ * This tool MUST be called before any edit/write operation to ensure:
1589
+ * 1. TDD cycle is active (test created first)
1590
+ * 2. Proposed code doesn't violate critical rules (using AST Intelligence!)
1591
+ * 3. Code patterns are compliant with platform rules
1592
+ */
1593
+ function preFlightCheck(params) {
1594
+ const { action_type, target_file, proposed_code, bypass_tdd } = params;
1595
+
1596
+ if (!action_type || !target_file) {
1597
+ return {
1598
+ success: false,
1599
+ error: 'action_type and target_file are required',
1600
+ hint: 'Call with: { action_type: "edit"|"create_file", target_file: "/path/to/file.ts", proposed_code: "..." }'
1601
+ };
1602
+ }
1603
+
1604
+ const isTestFile = /\.(spec|test)\.(js|ts|swift|kt)$/.test(target_file);
1605
+
1606
+ if (isTestFile) {
1607
+ rulesEnforcement.recordTestCreated(target_file);
1608
+ return {
1609
+ success: true,
1610
+ allowed: true,
1611
+ message: '✅ TEST FILE DETECTED - TDD cycle activated!',
1612
+ tdd_status: {
1613
+ active: true,
1614
+ phase: 'RED',
1615
+ instruction: 'Write the failing test first, then implement the code to make it pass (GREEN)'
1616
+ },
1617
+ session_state: rulesEnforcement.sessionState
1618
+ };
1619
+ }
1620
+
1621
+ const validation = rulesEnforcement.validateProposedAction(action_type, target_file, proposed_code);
1622
+
1623
+ if (!validation.allowed && !bypass_tdd) {
1624
+ return {
1625
+ success: false,
1626
+ allowed: false,
1627
+ blocked: true,
1628
+ reason: validation.enforcement_message,
1629
+ violations: validation.violations,
1630
+ tdd_status: validation.tddStatus,
1631
+ action_required: 'CREATE_TEST_FIRST',
1632
+ suggestion: `Before editing ${target_file}, create a test file first:
1633
+ 1️⃣ Create ${target_file.replace(/\.(js|ts|swift|kt)$/, '.spec.$1')} or similar
1634
+ 2️⃣ Write the failing test (RED phase)
1635
+ 3️⃣ Then come back and implement the code (GREEN phase)`,
1636
+ reminder: validation.reminder
1637
+ };
1638
+ }
1639
+
1640
+ let astAnalysis = null;
1641
+ if (proposed_code && proposed_code.length > 0) {
1642
+ try {
1643
+ const { analyzeCodeInMemory } = require('../ast/ast-core');
1644
+ astAnalysis = analyzeCodeInMemory(proposed_code, target_file);
1645
+
1646
+ if (astAnalysis.hasCritical) {
1647
+ return {
1648
+ success: false,
1649
+ allowed: false,
1650
+ blocked: true,
1651
+ reason: '🚫 AST INTELLIGENCE BLOCKED: Critical violations detected in proposed code',
1652
+ ast_violations: astAnalysis.violations,
1653
+ ast_summary: astAnalysis.summary,
1654
+ tdd_status: validation.tddStatus,
1655
+ action_required: 'FIX_AST_VIOLATIONS',
1656
+ suggestion: 'Fix the following AST violations before proceeding:\n' +
1657
+ astAnalysis.violations
1658
+ .filter(v => v.severity === 'CRITICAL')
1659
+ .map(v => ` ❌ ${v.ruleId}: ${v.message}`)
1660
+ .join('\n'),
1661
+ reminder: validation.reminder
1662
+ };
1663
+ }
1664
+ } catch (astError) {
1665
+ if (process.env.DEBUG) {
1666
+ process.stderr.write(`[MCP] AST in-memory analysis failed: ${astError.message}\n`);
1667
+ }
1668
+ }
1669
+ }
1670
+
1671
+ if (validation.hasViolations) {
1672
+ return {
1673
+ success: true,
1674
+ allowed: true,
1675
+ has_warnings: true,
1676
+ warnings: validation.violations.filter(v => v.severity === 'WARNING'),
1677
+ ast_analysis: astAnalysis,
1678
+ message: '⚠️ Proceed with caution - review warnings',
1679
+ tdd_status: validation.tddStatus
1680
+ };
1681
+ }
1682
+
1683
+ rulesEnforcement.recordImplementation(target_file);
1684
+
1685
+ return {
1686
+ success: true,
1687
+ allowed: true,
1688
+ message: '✅ Pre-flight check PASSED - proceed with implementation',
1689
+ ast_analysis: astAnalysis,
1690
+ tdd_status: validation.tddStatus,
1691
+ phase: 'GREEN',
1692
+ instruction: 'Implement the minimum code to make the test pass'
1693
+ };
1694
+ }
1695
+
1696
+ /**
1697
+ * Record that a test was created - activates TDD cycle
1698
+ */
1699
+ function recordTestCreated(params) {
1700
+ const { test_file } = params;
1701
+ if (!test_file) {
1702
+ return { success: false, error: 'test_file is required' };
1703
+ }
1704
+
1705
+ rulesEnforcement.recordTestCreated(test_file);
1706
+
1707
+ return {
1708
+ success: true,
1709
+ message: `✅ Test recorded: ${test_file}`,
1710
+ tdd_status: {
1711
+ active: true,
1712
+ phase: 'RED→GREEN',
1713
+ tests_this_session: rulesEnforcement.sessionState.testsCreatedThisSession.length
1714
+ },
1715
+ instruction: 'Now implement the code to make this test pass'
1716
+ };
1717
+ }
1718
+
1719
+ /**
1720
+ * Reset TDD session state (use when starting fresh)
1721
+ */
1722
+ function resetTddSession() {
1723
+ rulesEnforcement.resetSession();
1724
+ return {
1725
+ success: true,
1726
+ message: '🔄 TDD session reset - start fresh with a new test',
1727
+ session_state: rulesEnforcement.sessionState
1728
+ };
1729
+ }
1730
+
1122
1731
  /**
1123
1732
  * Validate and fix common issues
1124
1733
  */
@@ -1410,7 +2019,7 @@ async function handleMcpMessage(message) {
1410
2019
  },
1411
2020
  {
1412
2021
  name: 'ai_gate_check',
1413
- description: '🚦 MANDATORY gate check',
2022
+ description: '🚦 MANDATORY gate check. Returns: status, violations, human_intent (user goals), semantic_snapshot (project state), and mandatory_rules.',
1414
2023
  inputSchema: { type: 'object', properties: {} }
1415
2024
  },
1416
2025
  {
@@ -1427,6 +2036,62 @@ async function handleMcpMessage(message) {
1427
2036
  },
1428
2037
  required: ['platform']
1429
2038
  }
2039
+ },
2040
+ {
2041
+ name: 'set_human_intent',
2042
+ description: '🎯 Set the human intent/goal for the current session. This helps AI understand user objectives.',
2043
+ inputSchema: {
2044
+ type: 'object',
2045
+ properties: {
2046
+ goal: { type: 'string', description: 'Primary goal/intent for the session' },
2047
+ secondary_goals: { type: 'array', items: { type: 'string' }, description: 'Secondary goals' },
2048
+ non_goals: { type: 'array', items: { type: 'string' }, description: 'Explicitly what NOT to do' },
2049
+ constraints: { type: 'array', items: { type: 'string' }, description: 'Constraints to follow' },
2050
+ confidence: { type: 'string', enum: ['high', 'medium', 'low'], description: 'Confidence level' },
2051
+ expires_hours: { type: 'number', description: 'Hours until intent expires (default: 24)' }
2052
+ },
2053
+ required: ['goal']
2054
+ }
2055
+ },
2056
+ {
2057
+ name: 'get_human_intent',
2058
+ description: '🎯 Get the current human intent/goal for the session.',
2059
+ inputSchema: { type: 'object', properties: {} }
2060
+ },
2061
+ {
2062
+ name: 'clear_human_intent',
2063
+ description: '🎯 Clear/reset the human intent to empty state.',
2064
+ inputSchema: { type: 'object', properties: {} }
2065
+ },
2066
+ {
2067
+ name: 'pre_flight_check',
2068
+ description: '🚀 REVOLUTIONARY: Validate code BEFORE writing it. Enforces TDD cycle and checks for rule violations. Call this BEFORE any edit/create_file operation.',
2069
+ inputSchema: {
2070
+ type: 'object',
2071
+ properties: {
2072
+ action_type: { type: 'string', enum: ['edit', 'create_file'], description: 'Type of action to perform' },
2073
+ target_file: { type: 'string', description: 'File path to edit/create' },
2074
+ proposed_code: { type: 'string', description: 'Optional: code to validate for rule violations' },
2075
+ bypass_tdd: { type: 'boolean', description: 'Emergency bypass for TDD check (not recommended)' }
2076
+ },
2077
+ required: ['action_type', 'target_file']
2078
+ }
2079
+ },
2080
+ {
2081
+ name: 'record_test_created',
2082
+ description: '✅ Record that a test file was created - activates TDD cycle allowing implementation',
2083
+ inputSchema: {
2084
+ type: 'object',
2085
+ properties: {
2086
+ test_file: { type: 'string', description: 'Path to the test file created' }
2087
+ },
2088
+ required: ['test_file']
2089
+ }
2090
+ },
2091
+ {
2092
+ name: 'reset_tdd_session',
2093
+ description: '🔄 Reset TDD session state - use when starting fresh on a new feature',
2094
+ inputSchema: { type: 'object', properties: {} }
1430
2095
  }
1431
2096
  ]
1432
2097
  }
@@ -1437,6 +2102,38 @@ async function handleMcpMessage(message) {
1437
2102
  const toolName = request.params?.name;
1438
2103
  const toolParams = request.params?.arguments || {};
1439
2104
 
2105
+ // Tools that require gate enforcement (destructive operations)
2106
+ const GATE_ENFORCED_TOOLS = [
2107
+ 'auto_complete_gitflow',
2108
+ 'sync_branches',
2109
+ 'cleanup_stale_branches',
2110
+ 'validate_and_fix'
2111
+ ];
2112
+
2113
+ // Check gate enforcement for critical tools
2114
+ if (GATE_ENFORCED_TOOLS.includes(toolName)) {
2115
+ const enforcement = gateSession.getEnforcementStatus();
2116
+ if (enforcement.blocked) {
2117
+ return {
2118
+ jsonrpc: '2.0',
2119
+ id: request.id,
2120
+ result: {
2121
+ content: [{
2122
+ type: 'text',
2123
+ text: JSON.stringify({
2124
+ success: false,
2125
+ error: 'GATE_ENFORCEMENT_BLOCKED',
2126
+ reason: enforcement.reason,
2127
+ action: enforcement.action,
2128
+ violations: enforcement.violations || [],
2129
+ hint: '🚦 Call ai_gate_check first to unlock this tool'
2130
+ }, null, 2)
2131
+ }]
2132
+ }
2133
+ };
2134
+ }
2135
+ }
2136
+
1440
2137
  let result;
1441
2138
  switch (toolName) {
1442
2139
  case 'check_evidence_status':
@@ -1463,6 +2160,24 @@ async function handleMcpMessage(message) {
1463
2160
  case 'read_platform_rules':
1464
2161
  result = await readPlatformRulesHandler(toolParams);
1465
2162
  break;
2163
+ case 'set_human_intent':
2164
+ result = setHumanIntent(toolParams);
2165
+ break;
2166
+ case 'get_human_intent':
2167
+ result = getHumanIntent();
2168
+ break;
2169
+ case 'clear_human_intent':
2170
+ result = clearHumanIntent();
2171
+ break;
2172
+ case 'pre_flight_check':
2173
+ result = preFlightCheck(toolParams);
2174
+ break;
2175
+ case 'record_test_created':
2176
+ result = recordTestCreated(toolParams);
2177
+ break;
2178
+ case 'reset_tdd_session':
2179
+ result = resetTddSession();
2180
+ break;
1466
2181
  default:
1467
2182
  return {
1468
2183
  jsonrpc: '2.0',