pumuki-ast-hooks 5.5.60 β 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.
- package/README.md +361 -1101
- package/bin/__tests__/check-version.spec.js +32 -57
- package/docs/ARCHITECTURE.md +66 -1
- package/docs/TODO.md +41 -0
- package/docs/images/ast_intelligence_01.svg +40 -0
- package/docs/images/ast_intelligence_02.svg +39 -0
- package/docs/images/ast_intelligence_03.svg +55 -0
- package/docs/images/ast_intelligence_04.svg +39 -0
- package/docs/images/ast_intelligence_05.svg +45 -0
- package/docs/images/logo.png +0 -0
- package/package.json +1 -1
- package/scripts/hooks-system/.audit_tmp/hook-metrics.jsonl +20 -0
- package/scripts/hooks-system/application/DIValidationService.js +43 -0
- package/scripts/hooks-system/application/__tests__/DIValidationService.spec.js +81 -0
- package/scripts/hooks-system/bin/__tests__/check-version.spec.js +37 -57
- package/scripts/hooks-system/bin/cli.js +109 -0
- package/scripts/hooks-system/config/di-rules.json +42 -0
- package/scripts/hooks-system/domain/ports/FileSystemPort.js +19 -0
- package/scripts/hooks-system/domain/strategies/ConcreteDependencyStrategy.js +78 -0
- package/scripts/hooks-system/domain/strategies/DIStrategy.js +31 -0
- package/scripts/hooks-system/infrastructure/adapters/NodeFileSystemAdapter.js +28 -0
- package/scripts/hooks-system/infrastructure/ast/ast-core.js +124 -0
- package/scripts/hooks-system/infrastructure/ast/backend/ast-backend.js +19 -1
- package/scripts/hooks-system/infrastructure/ast/backend/detectors/god-class-detector.js +28 -8
- package/scripts/hooks-system/infrastructure/ast/common/ast-common.js +133 -0
- package/scripts/hooks-system/infrastructure/ast/frontend/analyzers/__tests__/FrontendArchitectureDetector.spec.js +4 -1
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/__tests__/iOSASTIntelligentAnalyzer.spec.js +3 -1
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSASTIntelligentAnalyzer.js +3 -2
- package/scripts/hooks-system/infrastructure/ast/ios/ast-ios.js +1 -1
- package/scripts/hooks-system/infrastructure/ast/ios/detectors/ios-ast-intelligent-strategies.js +40 -46
- package/scripts/hooks-system/infrastructure/cascade-hooks/README.md +114 -0
- package/scripts/hooks-system/infrastructure/cascade-hooks/cascade-hooks-config.json +20 -0
- package/scripts/hooks-system/infrastructure/cascade-hooks/claude-code-hook.sh +127 -0
- package/scripts/hooks-system/infrastructure/cascade-hooks/post-write-code-hook.js +72 -0
- package/scripts/hooks-system/infrastructure/cascade-hooks/pre-write-code-hook.js +167 -0
- package/scripts/hooks-system/infrastructure/cascade-hooks/universal-hook-adapter.js +186 -0
- package/scripts/hooks-system/infrastructure/mcp/ast-intelligence-automation.js +739 -24
- package/scripts/hooks-system/infrastructure/observability/MetricsCollector.js +221 -0
- package/scripts/hooks-system/infrastructure/observability/index.js +23 -0
- package/scripts/hooks-system/infrastructure/orchestration/__tests__/intelligent-audit.spec.js +177 -0
- package/scripts/hooks-system/infrastructure/orchestration/intelligent-audit.js +87 -1
- package/scripts/hooks-system/infrastructure/registry/StrategyRegistry.js +63 -0
- package/scripts/hooks-system/infrastructure/resilience/CircuitBreaker.js +229 -0
- package/scripts/hooks-system/infrastructure/resilience/RetryPolicy.js +141 -0
- 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
|
-
*
|
|
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
|
|
868
|
+
for (const ruleFile of ALL_RULE_FILES) {
|
|
590
869
|
try {
|
|
591
|
-
const content = await loader.loadRule(
|
|
870
|
+
const content = await loader.loadRule(ruleFile.file);
|
|
592
871
|
if (content) {
|
|
593
|
-
rules[
|
|
594
|
-
|
|
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
|
|
879
|
+
process.stderr.write(`[MCP] Failed to load ${ruleFile.file}: ${error.message}\n`);
|
|
600
880
|
}
|
|
601
881
|
}
|
|
602
882
|
}
|
|
603
883
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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',
|