pumuki-ast-hooks 5.5.50 → 5.5.52
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/docs/MCP_SERVERS.md +16 -2
- package/docs/RELEASE_NOTES.md +34 -0
- package/hooks/git-status-monitor.ts +0 -5
- package/hooks/notify-macos.ts +0 -1
- package/hooks/pre-tool-use-evidence-validator.ts +0 -1
- package/package.json +2 -2
- package/scripts/hooks-system/.audit_tmp/hook-metrics.jsonl +96 -0
- package/scripts/hooks-system/application/services/guard/GuardConfig.js +2 -4
- package/scripts/hooks-system/application/services/installation/GitEnvironmentService.js +2 -20
- package/scripts/hooks-system/application/services/installation/McpConfigurator.js +9 -139
- package/scripts/hooks-system/application/services/installation/mcp/McpGlobalConfigCleaner.js +49 -0
- package/scripts/hooks-system/application/services/installation/mcp/McpProjectConfigWriter.js +59 -0
- package/scripts/hooks-system/application/services/installation/mcp/McpServerConfigBuilder.js +103 -0
- package/scripts/hooks-system/infrastructure/ast/ast-core.js +1 -13
- package/scripts/hooks-system/infrastructure/ast/ast-intelligence.js +3 -2
- package/scripts/hooks-system/infrastructure/ast/backend/ast-backend.js +17 -9
- package/scripts/hooks-system/infrastructure/ast/backend/detectors/god-class-detector.js +2 -1
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSCICDChecks.js +385 -0
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSCICDRules.js +38 -408
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSEnterpriseAnalyzer.js +397 -34
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSSPMChecks.js +408 -0
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSSPMRules.js +36 -442
- package/scripts/hooks-system/infrastructure/ast/ios/parsers/SourceKittenExtractor.js +146 -0
- package/scripts/hooks-system/infrastructure/ast/ios/parsers/SourceKittenParser.js +22 -190
- package/scripts/hooks-system/infrastructure/ast/ios/parsers/SourceKittenRunner.js +62 -0
- package/scripts/hooks-system/infrastructure/mcp/ast-intelligence-automation.js +398 -1
- package/scripts/hooks-system/infrastructure/orchestration/__tests__/intelligent-audit.spec.js +0 -16
- package/scripts/hooks-system/infrastructure/orchestration/intelligent-audit.js +14 -25
- package/scripts/hooks-system/infrastructure/shell/gitflow/gitflow-enforcer.sh +20 -76
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSEnterpriseChecks.js +0 -350
|
@@ -576,9 +576,293 @@ function checkBranchChangesCoherence(branchName, uncommittedChanges) {
|
|
|
576
576
|
return { isCoherent: true, expectedScope, detectedScope: dominantScope, reason: 'Changes match branch scope' };
|
|
577
577
|
}
|
|
578
578
|
|
|
579
|
+
/**
|
|
580
|
+
* Load platform rules from .cursor/rules or .windsurf/rules
|
|
581
|
+
* Returns the content of the rules file for the detected platform
|
|
582
|
+
*/
|
|
583
|
+
async function loadPlatformRules(platforms) {
|
|
584
|
+
const DynamicRulesLoader = require('../../application/services/DynamicRulesLoader');
|
|
585
|
+
const loader = new DynamicRulesLoader();
|
|
586
|
+
const rules = {};
|
|
587
|
+
const criticalRules = [];
|
|
588
|
+
|
|
589
|
+
for (const platform of platforms) {
|
|
590
|
+
try {
|
|
591
|
+
const content = await loader.loadRule(`rules${platform}.mdc`);
|
|
592
|
+
if (content) {
|
|
593
|
+
rules[platform] = content;
|
|
594
|
+
const criticalPatterns = extractCriticalPatterns(content, platform);
|
|
595
|
+
criticalRules.push(...criticalPatterns);
|
|
596
|
+
}
|
|
597
|
+
} catch (error) {
|
|
598
|
+
if (process.env.DEBUG) {
|
|
599
|
+
process.stderr.write(`[MCP] Failed to load rules for ${platform}: ${error.message}\n`);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
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
|
+
}
|
|
616
|
+
|
|
617
|
+
return { rules, criticalRules };
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Extract critical patterns from rules content that AI MUST follow
|
|
622
|
+
*/
|
|
623
|
+
function extractCriticalPatterns(content, platform) {
|
|
624
|
+
const patterns = [];
|
|
625
|
+
if (!content) return patterns;
|
|
626
|
+
|
|
627
|
+
const lines = content.split('\n');
|
|
628
|
+
for (const line of lines) {
|
|
629
|
+
if (line.includes('❌') || line.includes('NUNCA') || line.includes('PROHIBIDO') || line.includes('NO ')) {
|
|
630
|
+
patterns.push({ platform, rule: line.trim(), severity: 'CRITICAL' });
|
|
631
|
+
}
|
|
632
|
+
if (line.includes('✅') && (line.includes('OBLIGATORIO') || line.includes('SIEMPRE'))) {
|
|
633
|
+
patterns.push({ platform, rule: line.trim(), severity: 'MANDATORY' });
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (platform === 'ios') {
|
|
638
|
+
// PROHIBICIONES CRÍTICAS iOS
|
|
639
|
+
patterns.push({ platform: 'ios', rule: '❌ NUNCA usar GCD (DispatchQueue.main.async, DispatchQueue.global) - usar Swift Concurrency (async/await, Task, actor, @MainActor)', severity: 'CRITICAL' });
|
|
640
|
+
patterns.push({ platform: 'ios', rule: '❌ NUNCA usar completion handlers/@escaping callbacks - usar async/await', severity: 'CRITICAL' });
|
|
641
|
+
patterns.push({ platform: 'ios', rule: '❌ NUNCA dejar catch vacíos - siempre gestionar errores (log/rethrow/wrap)', severity: 'CRITICAL' });
|
|
642
|
+
patterns.push({ platform: 'ios', rule: '❌ NUNCA mezclar operaciones de escritura (commands) con lecturas (queries) en el mismo flujo/servicio - aplicar CQRS', severity: 'CRITICAL' });
|
|
643
|
+
patterns.push({ platform: 'ios', rule: '❌ NUNCA usar Singleton - usar Inyección de Dependencias (DI por init/protocols)', severity: 'CRITICAL' });
|
|
644
|
+
patterns.push({ platform: 'ios', rule: '❌ NUNCA usar force unwrap (!) - usar guard let/if let/?? (excepción: IBOutlets)', severity: 'CRITICAL' });
|
|
645
|
+
patterns.push({ platform: 'ios', rule: '❌ NUNCA usar librerías de terceros (Alamofire, Swinject, Quick/Nimble, etc.) - usar APIs nativas (URLSession, DI manual, Swift Testing)', severity: 'CRITICAL' });
|
|
646
|
+
patterns.push({ platform: 'ios', rule: '❌ NUNCA añadir comentarios en el código - nombres autodescriptivos', severity: 'CRITICAL' });
|
|
647
|
+
patterns.push({ platform: 'ios', rule: '❌ NUNCA usar prints/logs ad-hoc - usar Logger del framework os', severity: 'CRITICAL' });
|
|
648
|
+
patterns.push({ platform: 'ios', rule: '❌ NUNCA usar AnyView - afecta performance, usar @ViewBuilder o generics', severity: 'CRITICAL' });
|
|
649
|
+
patterns.push({ platform: 'ios', rule: '❌ NUNCA usar ObservableObject - usar @Observable (iOS 17+)', severity: 'CRITICAL' });
|
|
650
|
+
patterns.push({ platform: 'ios', rule: '❌ NUNCA usar Storyboards/XIBs - usar SwiftUI o Programmatic UI', severity: 'CRITICAL' });
|
|
651
|
+
patterns.push({ platform: 'ios', rule: '❌ NUNCA usar CocoaPods/Carthage - usar Swift Package Manager', severity: 'CRITICAL' });
|
|
652
|
+
patterns.push({ platform: 'ios', rule: '❌ NUNCA usar JSONSerialization - usar Codable', severity: 'CRITICAL' });
|
|
653
|
+
patterns.push({ platform: 'ios', rule: '❌ NUNCA usar OperationQueue para async - usar Task/TaskGroup', severity: 'CRITICAL' });
|
|
654
|
+
patterns.push({ platform: 'ios', rule: '❌ NUNCA usar any (type erasure) - usar generics con protocolos de frontera', severity: 'CRITICAL' });
|
|
655
|
+
patterns.push({ platform: 'ios', rule: '❌ NUNCA usar Localizable.strings - usar String Catalogs (.xcstrings)', severity: 'CRITICAL' });
|
|
656
|
+
patterns.push({ platform: 'ios', rule: '❌ NUNCA ignorar warnings - warnings = errores futuros', severity: 'CRITICAL' });
|
|
657
|
+
patterns.push({ platform: 'ios', rule: '❌ NUNCA crear Massive View Controllers (>300 líneas) - separar lógica en ViewModels', severity: 'CRITICAL' });
|
|
658
|
+
patterns.push({ platform: 'ios', rule: '❌ NUNCA crear retain cycles - usar [weak self] en closures', severity: 'CRITICAL' });
|
|
659
|
+
|
|
660
|
+
// OBLIGATORIOS iOS
|
|
661
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar Swift 6.2 con Strict Concurrency Checking en Complete', severity: 'MANDATORY' });
|
|
662
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar async/await para TODAS las operaciones asíncronas', severity: 'MANDATORY' });
|
|
663
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar SwiftUI (UIKit solo si estrictamente necesario)', severity: 'MANDATORY' });
|
|
664
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar @Observable (iOS 17+) con @Bindable para bindings', severity: 'MANDATORY' });
|
|
665
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar NavigationStack + NavigationPath para navegación', severity: 'MANDATORY' });
|
|
666
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar guard/early returns - evitar pyramid of doom', severity: 'MANDATORY' });
|
|
667
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO seguir Clean Architecture (Domain → Application → Infrastructure → Presentation)', severity: 'MANDATORY' });
|
|
668
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO aplicar CQRS (Command Query Responsibility Segregation): separar comandos (write) de consultas (read)', severity: 'MANDATORY' });
|
|
669
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO verificar SOLID (SRP, OCP, LSP, ISP, DIP)', severity: 'MANDATORY' });
|
|
670
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO nombres autodescriptivos en inglés', severity: 'MANDATORY' });
|
|
671
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO comprobar que compila (Xcode build) ANTES de sugerir', severity: 'MANDATORY' });
|
|
672
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar Sendable conformance para tipos thread-safe', severity: 'MANDATORY' });
|
|
673
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar actor para estado compartido thread-safe', severity: 'MANDATORY' });
|
|
674
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar @MainActor para código UI', severity: 'MANDATORY' });
|
|
675
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar Task {} para lanzar contextos asíncronos desde código síncrono', severity: 'MANDATORY' });
|
|
676
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar Keychain nativo para datos sensibles (NO UserDefaults)', severity: 'MANDATORY' });
|
|
677
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar SwiftData (iOS 17+) para persistencia (CoreData solo legacy)', severity: 'MANDATORY' });
|
|
678
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar Swift Testing framework para tests (XCTest solo legacy)', severity: 'MANDATORY' });
|
|
679
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar makeSUT pattern en tests', severity: 'MANDATORY' });
|
|
680
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar trackForMemoryLeaks helper en tests', severity: 'MANDATORY' });
|
|
681
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar spies en vez de mocks/stubs', severity: 'MANDATORY' });
|
|
682
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar protocols para testability (inyectar protocols, no tipos concretos)', severity: 'MANDATORY' });
|
|
683
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar struct por defecto, class solo cuando necesites identity/herencia', severity: 'MANDATORY' });
|
|
684
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar let > var (inmutabilidad por defecto)', severity: 'MANDATORY' });
|
|
685
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO cero warnings en el proyecto', severity: 'MANDATORY' });
|
|
686
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO seguir flujo BDD → TDD (Feature files → Specs → Implementación)', severity: 'MANDATORY' });
|
|
687
|
+
patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO en producción todo real contra APIs/persistencia (cero mocks/spies)', severity: 'MANDATORY' });
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
if (platform === 'android') {
|
|
691
|
+
// PROHIBICIONES CRÍTICAS Android
|
|
692
|
+
patterns.push({ platform: 'android', rule: '❌ NUNCA usar Singleton - usar Hilt para Inyección de Dependencias', severity: 'CRITICAL' });
|
|
693
|
+
patterns.push({ platform: 'android', rule: '❌ NUNCA usar XML layouts - usar Jetpack Compose', severity: 'CRITICAL' });
|
|
694
|
+
patterns.push({ platform: 'android', rule: '❌ NUNCA usar Java en código nuevo - usar Kotlin 100%', severity: 'CRITICAL' });
|
|
695
|
+
patterns.push({ platform: 'android', rule: '❌ NUNCA usar LiveData - usar StateFlow/SharedFlow', severity: 'CRITICAL' });
|
|
696
|
+
patterns.push({ platform: 'android', rule: '❌ NUNCA dejar catch vacíos - siempre gestionar errores', severity: 'CRITICAL' });
|
|
697
|
+
patterns.push({ platform: 'android', rule: '❌ NUNCA mezclar operaciones de escritura (commands) con lecturas (queries) en el mismo caso de uso/repository - aplicar CQRS', severity: 'CRITICAL' });
|
|
698
|
+
patterns.push({ platform: 'android', rule: '❌ NUNCA añadir comentarios en el código', severity: 'CRITICAL' });
|
|
699
|
+
patterns.push({ platform: 'android', rule: '❌ NUNCA usar force unwrap (!!) - usar ?, ?:, let, requireNotNull', severity: 'CRITICAL' });
|
|
700
|
+
patterns.push({ platform: 'android', rule: '❌ NUNCA usar AsyncTask (deprecated) - usar Coroutines', severity: 'CRITICAL' });
|
|
701
|
+
patterns.push({ platform: 'android', rule: '❌ NUNCA usar RxJava en código nuevo - usar Flow', severity: 'CRITICAL' });
|
|
702
|
+
patterns.push({ platform: 'android', rule: '❌ NUNCA usar findViewById - usar Compose o View Binding', severity: 'CRITICAL' });
|
|
703
|
+
patterns.push({ platform: 'android', rule: '❌ NUNCA usar Context leaks - no referencias a Activity en objetos long-lived', severity: 'CRITICAL' });
|
|
704
|
+
patterns.push({ platform: 'android', rule: '❌ NUNCA usar God Activities - Single Activity + Composables', severity: 'CRITICAL' });
|
|
705
|
+
patterns.push({ platform: 'android', rule: '❌ NUNCA hardcodear strings - usar strings.xml', severity: 'CRITICAL' });
|
|
706
|
+
patterns.push({ platform: 'android', rule: '❌ NUNCA usar callbacks para async - usar suspend functions', severity: 'CRITICAL' });
|
|
707
|
+
|
|
708
|
+
// OBLIGATORIOS Android
|
|
709
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar Jetpack Compose + Material 3', severity: 'MANDATORY' });
|
|
710
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar Hilt para DI (@HiltAndroidApp, @AndroidEntryPoint, @Inject)', severity: 'MANDATORY' });
|
|
711
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar Kotlin Coroutines + Flow para async', severity: 'MANDATORY' });
|
|
712
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar StateFlow para exponer estado del ViewModel', severity: 'MANDATORY' });
|
|
713
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar sealed classes para estados (Success, Error, Loading)', severity: 'MANDATORY' });
|
|
714
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO seguir Clean Architecture (Domain → Data → Presentation)', severity: 'MANDATORY' });
|
|
715
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO aplicar CQRS (Command Query Responsibility Segregation): separar comandos (write) de consultas (read)', severity: 'MANDATORY' });
|
|
716
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO verificar SOLID', severity: 'MANDATORY' });
|
|
717
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar MVVM + Single Activity', severity: 'MANDATORY' });
|
|
718
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar Navigation Compose para navegación', severity: 'MANDATORY' });
|
|
719
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar Room para persistencia local', severity: 'MANDATORY' });
|
|
720
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar Retrofit + Moshi para networking', severity: 'MANDATORY' });
|
|
721
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar Coil para carga de imágenes', severity: 'MANDATORY' });
|
|
722
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar Kotlin DSL (build.gradle.kts)', severity: 'MANDATORY' });
|
|
723
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar Version Catalogs (libs.versions.toml)', severity: 'MANDATORY' });
|
|
724
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar JUnit5 + MockK para testing', severity: 'MANDATORY' });
|
|
725
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar Timber para logging (solo en DEBUG)', severity: 'MANDATORY' });
|
|
726
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar EncryptedSharedPreferences para datos sensibles', severity: 'MANDATORY' });
|
|
727
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO nombres autodescriptivos en inglés', severity: 'MANDATORY' });
|
|
728
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO comprobar que compila (Gradle build) ANTES de sugerir', severity: 'MANDATORY' });
|
|
729
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO seguir flujo BDD → TDD', severity: 'MANDATORY' });
|
|
730
|
+
patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO en producción todo real contra APIs/persistencia', severity: 'MANDATORY' });
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
if (platform === 'backend') {
|
|
734
|
+
// PROHIBICIONES CRÍTICAS Backend
|
|
735
|
+
patterns.push({ platform: 'backend', rule: '❌ NUNCA usar Singleton - usar NestJS DI (@Injectable)', severity: 'CRITICAL' });
|
|
736
|
+
patterns.push({ platform: 'backend', rule: '❌ NUNCA usar console.log/console.error - usar Logger de NestJS', severity: 'CRITICAL' });
|
|
737
|
+
patterns.push({ platform: 'backend', rule: '❌ NUNCA usar any/unknown en TypeScript - usar tipos explícitos o generics', severity: 'CRITICAL' });
|
|
738
|
+
patterns.push({ platform: 'backend', rule: '❌ NUNCA dejar catch vacíos - siempre loggear o propagar (common.error.empty_catch)', severity: 'CRITICAL' });
|
|
739
|
+
patterns.push({ platform: 'backend', rule: '❌ NUNCA mezclar Command y Query en el mismo handler/service - aplicar CQRS', severity: 'CRITICAL' });
|
|
740
|
+
patterns.push({ platform: 'backend', rule: '❌ NUNCA hardcodear secretos - usar variables de entorno (AWS Secrets Manager, Vault)', severity: 'CRITICAL' });
|
|
741
|
+
patterns.push({ platform: 'backend', rule: '❌ NUNCA añadir comentarios en el código', severity: 'CRITICAL' });
|
|
742
|
+
patterns.push({ platform: 'backend', rule: '❌ NUNCA exponer datos sin validar - usar class-validator + DTOs', severity: 'CRITICAL' });
|
|
743
|
+
patterns.push({ platform: 'backend', rule: '❌ NUNCA exponer stack traces en producción', severity: 'CRITICAL' });
|
|
744
|
+
patterns.push({ platform: 'backend', rule: '❌ NUNCA usar mocks en producción - solo datos reales', severity: 'CRITICAL' });
|
|
745
|
+
patterns.push({ platform: 'backend', rule: '❌ NUNCA loggear datos sensibles (passwords, tokens, PII)', severity: 'CRITICAL' });
|
|
746
|
+
patterns.push({ platform: 'backend', rule: '❌ NUNCA usar God classes (servicios >500 líneas)', severity: 'CRITICAL' });
|
|
747
|
+
patterns.push({ platform: 'backend', rule: '❌ NUNCA usar callback hell - usar async/await', severity: 'CRITICAL' });
|
|
748
|
+
patterns.push({ platform: 'backend', rule: '❌ NUNCA poner lógica en controllers - mover a servicios/use cases', severity: 'CRITICAL' });
|
|
749
|
+
|
|
750
|
+
// OBLIGATORIOS Backend
|
|
751
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar Repository Pattern (interfaces en domain, impl en infrastructure)', severity: 'MANDATORY' });
|
|
752
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar DTOs + class-validator para validación de entrada/salida', severity: 'MANDATORY' });
|
|
753
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar Guards para autenticación/autorización (@UseGuards)', severity: 'MANDATORY' });
|
|
754
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO paginación en TODOS los endpoints de listado', severity: 'MANDATORY' });
|
|
755
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO seguir Clean Architecture (Domain → Application → Infrastructure → Presentation)', severity: 'MANDATORY' });
|
|
756
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO aplicar CQRS (Command Query Responsibility Segregation): Commands (write) separados de Queries (read)', severity: 'MANDATORY' });
|
|
757
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO verificar SOLID', severity: 'MANDATORY' });
|
|
758
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO TypeScript strict mode', severity: 'MANDATORY' });
|
|
759
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar Use Cases explícitos (CreateOrderUseCase, etc.)', severity: 'MANDATORY' });
|
|
760
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar Exception Filters para manejo global de errores', severity: 'MANDATORY' });
|
|
761
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar Swagger/OpenAPI para documentación (@nestjs/swagger)', severity: 'MANDATORY' });
|
|
762
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar queries parametrizadas (prevenir SQL injection)', severity: 'MANDATORY' });
|
|
763
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar índices en columnas frecuentes en WHERE/JOIN', severity: 'MANDATORY' });
|
|
764
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar transacciones para operaciones críticas', severity: 'MANDATORY' });
|
|
765
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar soft deletes (deleted_at) por defecto', severity: 'MANDATORY' });
|
|
766
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar JWT + refresh tokens para autenticación', severity: 'MANDATORY' });
|
|
767
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar rate limiting (@nestjs/throttler)', severity: 'MANDATORY' });
|
|
768
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar Helmet para security headers', severity: 'MANDATORY' });
|
|
769
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar health checks (/health endpoint)', severity: 'MANDATORY' });
|
|
770
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar correlation IDs para tracing distribuido', severity: 'MANDATORY' });
|
|
771
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO nombres autodescriptivos en inglés', severity: 'MANDATORY' });
|
|
772
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO comprobar que compila (npm run build) ANTES de sugerir', severity: 'MANDATORY' });
|
|
773
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO seguir flujo BDD → TDD', severity: 'MANDATORY' });
|
|
774
|
+
patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO coverage >95% en lógica crítica', severity: 'MANDATORY' });
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
if (platform === 'frontend') {
|
|
778
|
+
// PROHIBICIONES CRÍTICAS Frontend
|
|
779
|
+
patterns.push({ platform: 'frontend', rule: '❌ NUNCA usar any/unknown en TypeScript - usar tipos explícitos o generics', severity: 'CRITICAL' });
|
|
780
|
+
patterns.push({ platform: 'frontend', rule: '❌ NUNCA usar class components - usar functional components + hooks', severity: 'CRITICAL' });
|
|
781
|
+
patterns.push({ platform: 'frontend', rule: '❌ NUNCA hardcodear strings - usar i18n (useTranslation)', severity: 'CRITICAL' });
|
|
782
|
+
patterns.push({ platform: 'frontend', rule: '❌ NUNCA dejar catch vacíos - siempre gestionar errores (common.error.empty_catch)', severity: 'CRITICAL' });
|
|
783
|
+
patterns.push({ platform: 'frontend', rule: '❌ NUNCA mezclar operaciones de escritura (commands/mutations) con lecturas (queries) en el mismo hook/service - aplicar CQRS', severity: 'CRITICAL' });
|
|
784
|
+
patterns.push({ platform: 'frontend', rule: '❌ NUNCA añadir comentarios en el código', severity: 'CRITICAL' });
|
|
785
|
+
patterns.push({ platform: 'frontend', rule: '❌ NUNCA usar console.log en producción', severity: 'CRITICAL' });
|
|
786
|
+
patterns.push({ platform: 'frontend', rule: '❌ NUNCA usar Singleton - usar providers/context para DI', severity: 'CRITICAL' });
|
|
787
|
+
patterns.push({ platform: 'frontend', rule: '❌ NUNCA usar índice como key en listas si el orden puede cambiar', severity: 'CRITICAL' });
|
|
788
|
+
patterns.push({ platform: 'frontend', rule: '❌ NUNCA usar prop drilling excesivo - usar Context/Zustand', severity: 'CRITICAL' });
|
|
789
|
+
patterns.push({ platform: 'frontend', rule: '❌ NUNCA renderizar HTML de usuario sin sanitizar (DOMPurify)', severity: 'CRITICAL' });
|
|
790
|
+
patterns.push({ platform: 'frontend', rule: '❌ NUNCA poner tokens en URLs - usar Authorization headers', severity: 'CRITICAL' });
|
|
791
|
+
|
|
792
|
+
// OBLIGATORIOS Frontend
|
|
793
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar TypeScript strict mode', severity: 'MANDATORY' });
|
|
794
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar React functional components + hooks', severity: 'MANDATORY' });
|
|
795
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar TailwindCSS para estilos', severity: 'MANDATORY' });
|
|
796
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar Next.js 15 App Router (app/ directory)', severity: 'MANDATORY' });
|
|
797
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar Server Components por defecto ("use client" solo cuando necesario)', severity: 'MANDATORY' });
|
|
798
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar React Query para server state', severity: 'MANDATORY' });
|
|
799
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar Zustand para estado global', severity: 'MANDATORY' });
|
|
800
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar React Hook Form + Zod para forms', severity: 'MANDATORY' });
|
|
801
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO API client en capa infrastructure (abstraer axios/fetch)', severity: 'MANDATORY' });
|
|
802
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO tests con React Testing Library + Playwright', severity: 'MANDATORY' });
|
|
803
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO seguir Clean Architecture', severity: 'MANDATORY' });
|
|
804
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO aplicar CQRS (Command Query Responsibility Segregation): separar writes (mutations) de reads (queries)', severity: 'MANDATORY' });
|
|
805
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO verificar SOLID', severity: 'MANDATORY' });
|
|
806
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar Next/Image para imágenes', severity: 'MANDATORY' });
|
|
807
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar loading.tsx/error.tsx en cada ruta', severity: 'MANDATORY' });
|
|
808
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar semantic HTML + ARIA labels', severity: 'MANDATORY' });
|
|
809
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar keyboard navigation en todos los interactivos', severity: 'MANDATORY' });
|
|
810
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO WCAG AA mínimo (contraste 4.5:1)', severity: 'MANDATORY' });
|
|
811
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO nombres autodescriptivos en inglés', severity: 'MANDATORY' });
|
|
812
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO comprobar que compila y pasa tests ANTES de sugerir', severity: 'MANDATORY' });
|
|
813
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO seguir flujo BDD → TDD', severity: 'MANDATORY' });
|
|
814
|
+
patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO en producción todo real contra APIs', severity: 'MANDATORY' });
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
if (platform === 'gold') {
|
|
818
|
+
// PROHIBICIONES CRÍTICAS Gold (comunes a todas las plataformas)
|
|
819
|
+
patterns.push({ platform: 'gold', rule: '❌ NUNCA usar Singleton - usar Inyección de Dependencias', severity: 'CRITICAL' });
|
|
820
|
+
patterns.push({ platform: 'gold', rule: '❌ NUNCA dejar catch vacíos - siempre loggear o propagar (AST: common.error.empty_catch)', severity: 'CRITICAL' });
|
|
821
|
+
patterns.push({ platform: 'gold', rule: '❌ NUNCA añadir comentarios en el código - nombres autodescriptivos', severity: 'CRITICAL' });
|
|
822
|
+
patterns.push({ platform: 'gold', rule: '❌ NUNCA usar mocks/spies en producción - todo real contra BBDD/servicios', severity: 'CRITICAL' });
|
|
823
|
+
patterns.push({ platform: 'gold', rule: '❌ NUNCA usar --no-verify o GIT_BYPASS_HOOK sin autorización explícita verbal', severity: 'CRITICAL' });
|
|
824
|
+
patterns.push({ platform: 'gold', rule: '❌ NUNCA hardcodear secretos en código - usar variables de entorno', severity: 'CRITICAL' });
|
|
825
|
+
patterns.push({ platform: 'gold', rule: '❌ NUNCA silenciar errores - siempre loggear o propagar', severity: 'CRITICAL' });
|
|
826
|
+
patterns.push({ platform: 'gold', rule: '❌ NUNCA modificar librerías locales (node_modules/@pumuki/...) - reportar bugs', severity: 'CRITICAL' });
|
|
827
|
+
|
|
828
|
+
// OBLIGATORIOS Gold (comunes a todas las plataformas)
|
|
829
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO responder siempre en español', severity: 'MANDATORY' });
|
|
830
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO actuar como Arquitecto de Soluciones y Software Designer', severity: 'MANDATORY' });
|
|
831
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO seguir flujo BDD → TDD (Feature files → Specs → Implementación)', severity: 'MANDATORY' });
|
|
832
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO seguir Clean Architecture y Clean Code (capas, dependencias hacia adentro)', severity: 'MANDATORY' });
|
|
833
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO verificar SOLID (SRP, OCP, LSP, ISP, DIP) en cada cambio', severity: 'MANDATORY' });
|
|
834
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO preferir guard/early returns - evitar pyramid of doom', severity: 'MANDATORY' });
|
|
835
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO nombres autodescriptivos en inglés (todo el código)', severity: 'MANDATORY' });
|
|
836
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO comprobar que compila ANTES de sugerir cualquier cambio', severity: 'MANDATORY' });
|
|
837
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO analizar estructura existente antes de implementar', severity: 'MANDATORY' });
|
|
838
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO usar makeSUT pattern en tests', severity: 'MANDATORY' });
|
|
839
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO usar trackForMemoryLeaks en tests', severity: 'MANDATORY' });
|
|
840
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO preferir spies frente a stubs/mocks en tests', severity: 'MANDATORY' });
|
|
841
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO validar SIEMPRE entradas de usuario', severity: 'MANDATORY' });
|
|
842
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO sanitizar datos (prevenir XSS, SQL Injection)', severity: 'MANDATORY' });
|
|
843
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO principio de menor privilegio (permisos mínimos)', severity: 'MANDATORY' });
|
|
844
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO paginación en todas las listas', severity: 'MANDATORY' });
|
|
845
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO tests como documentación viva (AAA/Given-When-Then)', severity: 'MANDATORY' });
|
|
846
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO cobertura mínima 80%, objetivo 95%+ en lógica crítica', severity: 'MANDATORY' });
|
|
847
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO commits atómicos con Conventional Commits (feat:, fix:, etc.)', severity: 'MANDATORY' });
|
|
848
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO branch naming (feature/, bugfix/, hotfix/, refactor/)', severity: 'MANDATORY' });
|
|
849
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO i18n desde día 1 - no hardcodear strings', severity: 'MANDATORY' });
|
|
850
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO WCAG 2.1 AA mínimo para accesibilidad', severity: 'MANDATORY' });
|
|
851
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO "Measure twice, cut once" - planificar bien antes de implementar', severity: 'MANDATORY' });
|
|
852
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO YAGNI - You Ain\'t Gonna Need It (no sobre-ingeniería)', severity: 'MANDATORY' });
|
|
853
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO KISS - Keep It Simple, Stupid (solución más simple que funcione)', severity: 'MANDATORY' });
|
|
854
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO Fail Fast - validar precondiciones al inicio', severity: 'MANDATORY' });
|
|
855
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO inmutabilidad por defecto (const, readonly, let > var)', severity: 'MANDATORY' });
|
|
856
|
+
patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO composición > herencia', severity: 'MANDATORY' });
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
return patterns;
|
|
860
|
+
}
|
|
861
|
+
|
|
579
862
|
/**
|
|
580
863
|
* AI Gate Check - MANDATORY at start of every AI response
|
|
581
864
|
* Returns BLOCKED or ALLOWED status with auto-fixes applied
|
|
865
|
+
* NOW INCLUDES: mandatory_rules that AI MUST read and follow
|
|
582
866
|
*/
|
|
583
867
|
async function aiGateCheck() {
|
|
584
868
|
const startedAt = Date.now();
|
|
@@ -683,6 +967,57 @@ async function aiGateCheck() {
|
|
|
683
967
|
}
|
|
684
968
|
}
|
|
685
969
|
|
|
970
|
+
let detectedPlatforms = [];
|
|
971
|
+
try {
|
|
972
|
+
const orchestrator = getCompositionRoot().getOrchestrator();
|
|
973
|
+
const contextDecision = await orchestrator.analyzeContext();
|
|
974
|
+
if (contextDecision && contextDecision.platforms) {
|
|
975
|
+
detectedPlatforms = contextDecision.platforms.map(p => p.platform || p);
|
|
976
|
+
}
|
|
977
|
+
} catch (err) {
|
|
978
|
+
if (process.env.DEBUG) {
|
|
979
|
+
process.stderr.write(`[MCP] analyzeContext failed, using fallback: ${err.message}\n`);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
if (detectedPlatforms.length === 0) {
|
|
984
|
+
try {
|
|
985
|
+
const PlatformDetectionService = require('../../application/services/PlatformDetectionService');
|
|
986
|
+
const detector = new PlatformDetectionService();
|
|
987
|
+
detectedPlatforms = await detector.detectPlatforms(REPO_ROOT);
|
|
988
|
+
} catch (err) {
|
|
989
|
+
if (process.env.DEBUG) {
|
|
990
|
+
process.stderr.write(`[MCP] PlatformDetectionService failed: ${err.message}\n`);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
const platformsToLoad = detectedPlatforms.length > 0
|
|
996
|
+
? detectedPlatforms.map(p => String(p).toLowerCase())
|
|
997
|
+
: ['backend', 'frontend', 'ios', 'android'];
|
|
998
|
+
|
|
999
|
+
let mandatoryRules = null;
|
|
1000
|
+
try {
|
|
1001
|
+
const rulesData = await loadPlatformRules(platformsToLoad);
|
|
1002
|
+
mandatoryRules = {
|
|
1003
|
+
platforms: platformsToLoad,
|
|
1004
|
+
criticalRules: rulesData.criticalRules || [],
|
|
1005
|
+
rulesLoaded: Object.keys(rulesData.rules || {}),
|
|
1006
|
+
warning: '⚠️ AI MUST read and follow these rules before ANY code generation or modification'
|
|
1007
|
+
};
|
|
1008
|
+
} catch (err) {
|
|
1009
|
+
if (process.env.DEBUG) {
|
|
1010
|
+
process.stderr.write(`[MCP] loadPlatformRules failed: ${err.message}\n`);
|
|
1011
|
+
}
|
|
1012
|
+
mandatoryRules = {
|
|
1013
|
+
platforms: platformsToLoad,
|
|
1014
|
+
criticalRules: [],
|
|
1015
|
+
rulesLoaded: [],
|
|
1016
|
+
warning: '⚠️ Rules loading failed but AI MUST still follow project conventions',
|
|
1017
|
+
error: err.message
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
|
|
686
1021
|
return {
|
|
687
1022
|
status: isBlocked ? 'BLOCKED' : 'ALLOWED',
|
|
688
1023
|
timestamp: new Date().toISOString(),
|
|
@@ -690,12 +1025,15 @@ async function aiGateCheck() {
|
|
|
690
1025
|
violations,
|
|
691
1026
|
warnings,
|
|
692
1027
|
autoFixes,
|
|
1028
|
+
mandatory_rules: mandatoryRules,
|
|
693
1029
|
summary: isBlocked
|
|
694
1030
|
? `🚫 BLOCKED: ${violations.length} violation(s). Fix before proceeding.`
|
|
695
1031
|
: `🚦 ALLOWED: Gate passed.${warnings.length > 0 ? ` ${warnings.length} warning(s).` : ''}`,
|
|
696
1032
|
instructions: isBlocked
|
|
697
1033
|
? 'DO NOT proceed with user task. Announce violations and fix them first.'
|
|
698
|
-
:
|
|
1034
|
+
: mandatoryRules
|
|
1035
|
+
? `You may proceed with user task. CRITICAL: Review mandatory_rules.criticalRules BEFORE generating ANY code.`
|
|
1036
|
+
: 'You may proceed with user task.'
|
|
699
1037
|
};
|
|
700
1038
|
};
|
|
701
1039
|
|
|
@@ -717,6 +1055,47 @@ async function aiGateCheck() {
|
|
|
717
1055
|
};
|
|
718
1056
|
}
|
|
719
1057
|
|
|
1058
|
+
/**
|
|
1059
|
+
* Read platform rules handler - returns critical rules for a specific platform
|
|
1060
|
+
*/
|
|
1061
|
+
async function readPlatformRulesHandler(params) {
|
|
1062
|
+
const platform = params.platform;
|
|
1063
|
+
if (!platform) {
|
|
1064
|
+
return {
|
|
1065
|
+
success: false,
|
|
1066
|
+
error: 'Platform is required. Use: ios, android, backend, or frontend'
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
try {
|
|
1071
|
+
const rulesData = await loadPlatformRules([platform]);
|
|
1072
|
+
const DynamicRulesLoader = require('../../application/services/DynamicRulesLoader');
|
|
1073
|
+
const loader = new DynamicRulesLoader();
|
|
1074
|
+
const fullContent = await loader.loadRule(`rules${platform}.mdc`);
|
|
1075
|
+
|
|
1076
|
+
return {
|
|
1077
|
+
success: true,
|
|
1078
|
+
platform,
|
|
1079
|
+
rulesLoaded: true,
|
|
1080
|
+
criticalRules: rulesData.criticalRules,
|
|
1081
|
+
fullRulesContent: fullContent,
|
|
1082
|
+
warning: `⚠️ YOU MUST FOLLOW ALL THESE RULES. Violations will block commits.`,
|
|
1083
|
+
instructions: [
|
|
1084
|
+
`❌ NEVER violate any rule marked with ❌ or NUNCA/PROHIBIDO`,
|
|
1085
|
+
`✅ ALWAYS follow rules marked with ✅ or OBLIGATORIO/SIEMPRE`,
|
|
1086
|
+
`🚨 If you violate these rules, the commit will be BLOCKED`,
|
|
1087
|
+
`📝 Read the fullRulesContent carefully before generating ANY code`
|
|
1088
|
+
]
|
|
1089
|
+
};
|
|
1090
|
+
} catch (error) {
|
|
1091
|
+
return {
|
|
1092
|
+
success: false,
|
|
1093
|
+
platform,
|
|
1094
|
+
error: `Failed to load rules: ${error.message}`
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
|
|
720
1099
|
/**
|
|
721
1100
|
* Validate and fix common issues
|
|
722
1101
|
*/
|
|
@@ -1010,6 +1389,21 @@ async function handleMcpMessage(message) {
|
|
|
1010
1389
|
name: 'ai_gate_check',
|
|
1011
1390
|
description: '🚦 MANDATORY gate check',
|
|
1012
1391
|
inputSchema: { type: 'object', properties: {} }
|
|
1392
|
+
},
|
|
1393
|
+
{
|
|
1394
|
+
name: 'read_platform_rules',
|
|
1395
|
+
description: '📚 MANDATORY: Read platform-specific rules BEFORE any code generation. Returns critical rules that AI MUST follow.',
|
|
1396
|
+
inputSchema: {
|
|
1397
|
+
type: 'object',
|
|
1398
|
+
properties: {
|
|
1399
|
+
platform: {
|
|
1400
|
+
type: 'string',
|
|
1401
|
+
enum: ['ios', 'android', 'backend', 'frontend'],
|
|
1402
|
+
description: 'Platform to load rules for'
|
|
1403
|
+
}
|
|
1404
|
+
},
|
|
1405
|
+
required: ['platform']
|
|
1406
|
+
}
|
|
1013
1407
|
}
|
|
1014
1408
|
]
|
|
1015
1409
|
}
|
|
@@ -1043,6 +1437,9 @@ async function handleMcpMessage(message) {
|
|
|
1043
1437
|
case 'ai_gate_check':
|
|
1044
1438
|
result = await aiGateCheck();
|
|
1045
1439
|
break;
|
|
1440
|
+
case 'read_platform_rules':
|
|
1441
|
+
result = await readPlatformRulesHandler(toolParams);
|
|
1442
|
+
break;
|
|
1046
1443
|
default:
|
|
1047
1444
|
return {
|
|
1048
1445
|
jsonrpc: '2.0',
|
package/scripts/hooks-system/infrastructure/orchestration/__tests__/intelligent-audit.spec.js
CHANGED
|
@@ -11,22 +11,6 @@ describe('intelligent-audit', () => {
|
|
|
11
11
|
const mod = require('../intelligent-audit');
|
|
12
12
|
expect(typeof mod.runIntelligentAudit).toBe('function');
|
|
13
13
|
});
|
|
14
|
-
|
|
15
|
-
it('should filter staged violations strictly (no substring matches, no .audit_tmp)', () => {
|
|
16
|
-
const mod = require('../intelligent-audit');
|
|
17
|
-
|
|
18
|
-
expect(typeof mod.isViolationInStagedFiles).toBe('function');
|
|
19
|
-
|
|
20
|
-
const stagedSet = new Set([
|
|
21
|
-
'apps/ios/Application/AppCoordinator.swift'
|
|
22
|
-
]);
|
|
23
|
-
|
|
24
|
-
expect(mod.isViolationInStagedFiles('apps/ios/Application/AppCoordinator.swift', stagedSet)).toBe(true);
|
|
25
|
-
expect(mod.isViolationInStagedFiles('apps/ios/Application/AppCoordinator.swift.backup', stagedSet)).toBe(false);
|
|
26
|
-
expect(mod.isViolationInStagedFiles('.audit_tmp/AppCoordinator.123.staged.swift', stagedSet)).toBe(false);
|
|
27
|
-
expect(mod.isViolationInStagedFiles('some/dir/.audit_tmp/AppCoordinator.123.staged.swift', stagedSet)).toBe(false);
|
|
28
|
-
expect(mod.isViolationInStagedFiles('apps/ios/Application/AppCoordinator', stagedSet)).toBe(false);
|
|
29
|
-
});
|
|
30
14
|
});
|
|
31
15
|
|
|
32
16
|
describe('AI_EVIDENCE.json structure validation', () => {
|
|
@@ -235,28 +235,6 @@ function toRepoRelativePath(filePath) {
|
|
|
235
235
|
return normalized;
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
-
function isAuditTmpPath(repoRelativePath) {
|
|
239
|
-
const normalized = normalizePathForMatch(repoRelativePath);
|
|
240
|
-
return normalized.startsWith('.audit_tmp/') || normalized.includes('/.audit_tmp/');
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function isViolationInStagedFiles(violationPath, stagedSet) {
|
|
244
|
-
if (!violationPath) {
|
|
245
|
-
return false;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const repoRelative = toRepoRelativePath(violationPath);
|
|
249
|
-
if (!repoRelative) {
|
|
250
|
-
return false;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (isAuditTmpPath(repoRelative)) {
|
|
254
|
-
return false;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
return stagedSet.has(repoRelative);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
238
|
function resolveAuditTmpDir() {
|
|
261
239
|
const configured = (env.get('AUDIT_TMP', '') || '').trim();
|
|
262
240
|
if (configured.length > 0) {
|
|
@@ -295,7 +273,18 @@ async function runIntelligentAudit() {
|
|
|
295
273
|
|
|
296
274
|
const stagedViolations = rawViolations.filter(v => {
|
|
297
275
|
const violationPath = toRepoRelativePath(v.filePath || v.file || '');
|
|
298
|
-
|
|
276
|
+
if (!violationPath) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
if (stagedSet.has(violationPath)) {
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
for (const sf of stagedSet) {
|
|
283
|
+
if (sf && (violationPath === sf || violationPath.endsWith('/' + sf) || violationPath.includes('/' + sf))) {
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return false;
|
|
299
288
|
});
|
|
300
289
|
|
|
301
290
|
console.log(`[Intelligent Audit] Gate scope: STAGING (${stagedFiles.length} files)`);
|
|
@@ -561,7 +550,7 @@ async function updateAIEvidence(violations, gateResult, tokenUsage) {
|
|
|
561
550
|
file: v.filePath || v.file || 'unknown',
|
|
562
551
|
line: v.line || null,
|
|
563
552
|
severity: v.severity,
|
|
564
|
-
|
|
553
|
+
rule: ruleId,
|
|
565
554
|
message: v.message || v.description || '',
|
|
566
555
|
category: v.category || deriveCategoryFromRuleId(ruleId),
|
|
567
556
|
intelligent_evaluation: v.intelligentEvaluation || false,
|
|
@@ -694,4 +683,4 @@ if (require.main === module) {
|
|
|
694
683
|
});
|
|
695
684
|
}
|
|
696
685
|
|
|
697
|
-
module.exports = { runIntelligentAudit
|
|
686
|
+
module.exports = { runIntelligentAudit };
|