pumuki 6.3.153 → 6.3.154

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.
@@ -13,6 +13,7 @@ import {
13
13
  hasSwiftDispatchGroupUsage,
14
14
  hasSwiftDispatchQueueUsage,
15
15
  hasSwiftDispatchSemaphoreUsage,
16
+ hasSwiftExplicitColorStaticMemberUsage,
16
17
  hasSwiftForEachIndicesUsage,
17
18
  hasSwiftForceCastUsage,
18
19
  hasSwiftFontWeightBoldUsage,
@@ -20,6 +21,7 @@ import {
20
21
  hasSwiftForceTryUsage,
21
22
  hasSwiftForceUnwrap,
22
23
  hasSwiftGeometryReaderUsage,
24
+ hasSwiftInlineFilteringInForEachUsage,
23
25
  hasSwiftLegacyOnChangeUsage,
24
26
  hasSwiftLegacyExpectationDescriptionUsage,
25
27
  hasSwiftLegacySwiftUiObservableWrapperUsage,
@@ -243,7 +245,9 @@ MainActor.assumeIsolated { reload() }
243
245
  assert.equal(hasSwiftNonisolatedUnsafeUsage(source), true);
244
246
  assert.equal(hasSwiftAssumeIsolatedUsage(source), true);
245
247
  assert.equal(hasSwiftForEachIndicesUsage(source), true);
248
+ assert.equal(hasSwiftInlineFilteringInForEachUsage('ForEach(items.filter { $0.isActive }) { item in Text(item.title) }'), true);
246
249
  assert.equal(hasSwiftContainsUserFilterUsage(source), true);
250
+ assert.equal(hasSwiftExplicitColorStaticMemberUsage('Text("A").foregroundStyle(Color.blue)'), true);
247
251
  assert.equal(hasSwiftGeometryReaderUsage(source), true);
248
252
  assert.equal(hasSwiftFontWeightBoldUsage(source), true);
249
253
  assert.equal(hasSwiftObservableObjectUsage(source), true);
@@ -289,7 +293,9 @@ let t = "MainActor.assumeIsolated { reload() }"
289
293
  assert.equal(hasSwiftNonisolatedUnsafeUsage(source), false);
290
294
  assert.equal(hasSwiftAssumeIsolatedUsage(source), false);
291
295
  assert.equal(hasSwiftForEachIndicesUsage(source), false);
296
+ assert.equal(hasSwiftInlineFilteringInForEachUsage(source), false);
292
297
  assert.equal(hasSwiftContainsUserFilterUsage(source), false);
298
+ assert.equal(hasSwiftExplicitColorStaticMemberUsage('Text("A").foregroundStyle(.blue)'), false);
293
299
  assert.equal(hasSwiftGeometryReaderUsage(source), false);
294
300
  assert.equal(hasSwiftFontWeightBoldUsage(source), false);
295
301
  assert.equal(hasSwiftTaskDetachedUsage(source), false);
@@ -400,7 +406,7 @@ final class LoginModelTests: XCTestCase {
400
406
  }
401
407
  `;
402
408
 
403
- assert.equal(hasSwiftLegacyXCTestImportUsage(unitTest), true);
409
+ assert.equal(hasSwiftLegacyXCTestImportUsage(unitTest), false);
404
410
  assert.equal(hasSwiftLegacyXCTestImportUsage(uiTest), false);
405
411
  assert.equal(hasSwiftLegacyXCTestImportUsage(performanceTest), false);
406
412
  assert.equal(hasSwiftLegacyXCTestImportUsage(brownfieldCompatibleUnitTest), false);
@@ -524,7 +530,7 @@ final class LoginModelTests: XCTestCase {
524
530
  }
525
531
  `;
526
532
 
527
- assert.equal(hasSwiftModernizableXCTestSuiteUsage(legacySuite), true);
533
+ assert.equal(hasSwiftModernizableXCTestSuiteUsage(legacySuite), false);
528
534
  assert.equal(hasSwiftModernizableXCTestSuiteUsage(mixedSuite), false);
529
535
  assert.equal(hasSwiftModernizableXCTestSuiteUsage(uiSuite), false);
530
536
  assert.equal(hasSwiftModernizableXCTestSuiteUsage(brownfieldCompatibleSuite), false);
@@ -596,7 +602,7 @@ final class BuyerCommerceUISmokeTests: XCTestCase {
596
602
  assert.equal(hasSwiftXCTUnwrapUsage(`${uiSource}\nlet value = try XCTUnwrap(optional)`), false);
597
603
  });
598
604
 
599
- test('hasSwiftXCTestAssertionUsage excluye XCTest brownfield compatible y bloquea suites sin contrato de calidad', () => {
605
+ test('detectores Swift Testing excluyen XCTest brownfield y dejan la calidad a su guard dedicado', () => {
600
606
  const compatibleSource = `
601
607
  import XCTest
602
608
 
@@ -633,7 +639,11 @@ final class LoginModelTests: XCTestCase {
633
639
 
634
640
  assert.equal(hasSwiftXCTestAssertionUsage(compatibleSource), false);
635
641
  assert.equal(hasSwiftXCTUnwrapUsage(compatibleSource), false);
636
- assert.equal(hasSwiftXCTestAssertionUsage(missingQualityContract), true);
642
+ assert.equal(hasSwiftLegacyXCTestImportUsage(compatibleSource), false);
643
+ assert.equal(hasSwiftModernizableXCTestSuiteUsage(compatibleSource), false);
644
+ assert.equal(hasSwiftXCTestAssertionUsage(missingQualityContract), false);
645
+ assert.equal(hasSwiftLegacyXCTestImportUsage(missingQualityContract), false);
646
+ assert.equal(hasSwiftModernizableXCTestSuiteUsage(missingQualityContract), false);
637
647
  });
638
648
 
639
649
  test('hasSwiftXCTUnwrapUsage detecta XCTUnwrap real y evita strings', () => {
@@ -479,6 +479,17 @@ export const hasSwiftForEachIndicesUsage = (source: string): boolean => {
479
479
  );
480
480
  };
481
481
 
482
+ export const hasSwiftInlineFilteringInForEachUsage = (source: string): boolean => {
483
+ return hasSwiftSanitizedRegexMatch(
484
+ source,
485
+ /\bForEach\s*\(\s*[A-Za-z_][A-Za-z0-9_.]*\s*\.\s*filter\s*\{/g
486
+ );
487
+ };
488
+
489
+ export const hasSwiftExplicitColorStaticMemberUsage = (source: string): boolean => {
490
+ return hasSwiftSanitizedRegexMatch(source, /\bColor\s*\.\s*[a-z][A-Za-z0-9_]*\b/g);
491
+ };
492
+
482
493
  const isUserSearchIdentifier = (value: string): boolean => {
483
494
  return /^(?:query|search(?:Text|Term|Query|Value)?|filter(?:Text|Value)?|text|term|input)$/i.test(
484
495
  value
@@ -756,6 +767,18 @@ const hasSwiftBrownfieldCompatibleXCTestUsage = (source: string): boolean => {
756
767
  return hasSwiftMakeSutUsage(source) && hasSwiftMemoryLeakTrackingUsage(source);
757
768
  };
758
769
 
770
+ const hasSwiftXCTestOnlyBrownfieldSuiteUsage = (source: string): boolean => {
771
+ if (!hasSwiftXCTestImportUsage(source) || !hasSwiftXCTestCaseSubclassUsage(source)) {
772
+ return false;
773
+ }
774
+
775
+ if (hasSwiftLegacyXCTestUiOrPerformanceUsage(source)) {
776
+ return false;
777
+ }
778
+
779
+ return !hasSwiftTestingImportUsage(source) && !hasSwiftTestingSuiteAttributeUsage(source);
780
+ };
781
+
759
782
  export const hasSwiftLegacyXCTestImportUsage = (source: string): boolean => {
760
783
  if (!hasSwiftXCTestImportUsage(source)) {
761
784
  return false;
@@ -765,7 +788,7 @@ export const hasSwiftLegacyXCTestImportUsage = (source: string): boolean => {
765
788
  return false;
766
789
  }
767
790
 
768
- if (hasSwiftBrownfieldCompatibleXCTestUsage(source)) {
791
+ if (hasSwiftXCTestOnlyBrownfieldSuiteUsage(source)) {
769
792
  return false;
770
793
  }
771
794
 
@@ -801,7 +824,7 @@ export const hasSwiftXCTestAssertionUsage = (source: string): boolean => {
801
824
  return false;
802
825
  }
803
826
 
804
- if (hasSwiftBrownfieldCompatibleXCTestUsage(source)) {
827
+ if (hasSwiftXCTestOnlyBrownfieldSuiteUsage(source)) {
805
828
  return false;
806
829
  }
807
830
 
@@ -816,7 +839,7 @@ export const hasSwiftXCTUnwrapUsage = (source: string): boolean => {
816
839
  return false;
817
840
  }
818
841
 
819
- if (hasSwiftBrownfieldCompatibleXCTestUsage(source)) {
842
+ if (hasSwiftXCTestOnlyBrownfieldSuiteUsage(source)) {
820
843
  return false;
821
844
  }
822
845
 
@@ -3,7 +3,7 @@ import test from 'node:test';
3
3
  import { androidRules } from './android';
4
4
 
5
5
  test('androidRules define reglas heurísticas locked para plataforma android', () => {
6
- assert.equal(androidRules.length, 76);
6
+ assert.equal(androidRules.length, 82);
7
7
 
8
8
  const ids = androidRules.map((rule) => rule.id);
9
9
  assert.deepEqual(ids, [
@@ -39,9 +39,15 @@ test('androidRules define reglas heurísticas locked para plataforma android', (
39
39
  'heuristics.android.single-source-of-truth-viewmodel-es-la-fuente.ast',
40
40
  'heuristics.android.skip-recomposition-para-metros-inmutables-o-estables.ast',
41
41
  'heuristics.android.stability-composables-estables-recomponen-menos.ast',
42
- 'heuristics.android.string-formatting-1-s-2-d-para-argumentos.ast',
42
+ 'heuristics.android.string-formatting-1-s-2-d-para-argumentos.ast',
43
43
  'heuristics.android.binds-para-implementaciones-de-interfaces-ma-s-eficiente.ast',
44
44
  'heuristics.android.provides-para-interfaces-o-third-party.ast',
45
+ 'heuristics.android.hilt-di-framework-no-manual-factories.ast',
46
+ 'heuristics.android.hiltandroidapp-application-class.ast',
47
+ 'heuristics.android.androidentrypoint-activity-fragment-viewmodel.ast',
48
+ 'heuristics.android.inject-constructor-constructor-injection.ast',
49
+ 'heuristics.android.module-installin-provide-dependencies.ast',
50
+ 'heuristics.android.viewmodelscoped-para-dependencias-de-viewmodel.ast',
45
51
  'heuristics.android.workmanager-androidx-work-work-runtime-ktx.ast',
46
52
  'heuristics.android.version-catalogs-libs-versions-toml-para-dependencias.ast',
47
53
  'heuristics.android.workmanager-background-tasks.ast',
@@ -179,6 +185,30 @@ test('androidRules define reglas heurísticas locked para plataforma android', (
179
185
  byId.get('heuristics.android.provides-para-interfaces-o-third-party.ast')?.then.code,
180
186
  'HEURISTICS_ANDROID_PROVIDES_PARA_INTERFACES_O_THIRD_PARTY_AST'
181
187
  );
188
+ assert.equal(
189
+ byId.get('heuristics.android.hilt-di-framework-no-manual-factories.ast')?.then.code,
190
+ 'HEURISTICS_ANDROID_HILT_DI_FRAMEWORK_NO_MANUAL_FACTORIES_AST'
191
+ );
192
+ assert.equal(
193
+ byId.get('heuristics.android.hiltandroidapp-application-class.ast')?.then.code,
194
+ 'HEURISTICS_ANDROID_HILTANDROIDAPP_APPLICATION_CLASS_AST'
195
+ );
196
+ assert.equal(
197
+ byId.get('heuristics.android.androidentrypoint-activity-fragment-viewmodel.ast')?.then.code,
198
+ 'HEURISTICS_ANDROID_ANDROIDENTRYPOINT_ACTIVITY_FRAGMENT_VIEWMODEL_AST'
199
+ );
200
+ assert.equal(
201
+ byId.get('heuristics.android.inject-constructor-constructor-injection.ast')?.then.code,
202
+ 'HEURISTICS_ANDROID_INJECT_CONSTRUCTOR_CONSTRUCTOR_INJECTION_AST'
203
+ );
204
+ assert.equal(
205
+ byId.get('heuristics.android.module-installin-provide-dependencies.ast')?.then.code,
206
+ 'HEURISTICS_ANDROID_MODULE_INSTALLIN_PROVIDE_DEPENDENCIES_AST'
207
+ );
208
+ assert.equal(
209
+ byId.get('heuristics.android.viewmodelscoped-para-dependencias-de-viewmodel.ast')?.then.code,
210
+ 'HEURISTICS_ANDROID_VIEWMODELSCOPED_PARA_DEPENDENCIAS_DE_VIEWMODEL_AST'
211
+ );
182
212
  assert.equal(
183
213
  byId.get('heuristics.android.workmanager-androidx-work-work-runtime-ktx.ast')?.then.code,
184
214
  'HEURISTICS_ANDROID_WORKMANAGER_ANDROIDX_WORK_WORK_RUNTIME_KTX_AST'
@@ -663,6 +663,116 @@ export const androidRules: RuleSet = [
663
663
  code: 'HEURISTICS_ANDROID_PROVIDES_PARA_INTERFACES_O_THIRD_PARTY_AST',
664
664
  },
665
665
  },
666
+ {
667
+ id: 'heuristics.android.hilt-di-framework-no-manual-factories.ast',
668
+ description: 'Detects Hilt DI framework usage in Android production Kotlin files.',
669
+ severity: 'WARN',
670
+ platform: 'android',
671
+ locked: true,
672
+ when: {
673
+ kind: 'Heuristic',
674
+ where: {
675
+ ruleId: 'heuristics.android.hilt-di-framework-no-manual-factories.ast',
676
+ },
677
+ },
678
+ then: {
679
+ kind: 'Finding',
680
+ message:
681
+ 'AST heuristic detected Hilt DI framework usage instead of manual factories in Android production code.',
682
+ code: 'HEURISTICS_ANDROID_HILT_DI_FRAMEWORK_NO_MANUAL_FACTORIES_AST',
683
+ },
684
+ },
685
+ {
686
+ id: 'heuristics.android.hiltandroidapp-application-class.ast',
687
+ description: 'Detects @HiltAndroidApp usage in Android production Kotlin files.',
688
+ severity: 'WARN',
689
+ platform: 'android',
690
+ locked: true,
691
+ when: {
692
+ kind: 'Heuristic',
693
+ where: {
694
+ ruleId: 'heuristics.android.hiltandroidapp-application-class.ast',
695
+ },
696
+ },
697
+ then: {
698
+ kind: 'Finding',
699
+ message: 'AST heuristic detected @HiltAndroidApp usage in Android production code.',
700
+ code: 'HEURISTICS_ANDROID_HILTANDROIDAPP_APPLICATION_CLASS_AST',
701
+ },
702
+ },
703
+ {
704
+ id: 'heuristics.android.androidentrypoint-activity-fragment-viewmodel.ast',
705
+ description: 'Detects @AndroidEntryPoint usage in Android production Kotlin files.',
706
+ severity: 'WARN',
707
+ platform: 'android',
708
+ locked: true,
709
+ when: {
710
+ kind: 'Heuristic',
711
+ where: {
712
+ ruleId: 'heuristics.android.androidentrypoint-activity-fragment-viewmodel.ast',
713
+ },
714
+ },
715
+ then: {
716
+ kind: 'Finding',
717
+ message:
718
+ 'AST heuristic detected @AndroidEntryPoint usage in Android production code where Activity, Fragment, and ViewModel injection boundaries should remain explicit.',
719
+ code: 'HEURISTICS_ANDROID_ANDROIDENTRYPOINT_ACTIVITY_FRAGMENT_VIEWMODEL_AST',
720
+ },
721
+ },
722
+ {
723
+ id: 'heuristics.android.inject-constructor-constructor-injection.ast',
724
+ description: 'Detects @Inject constructor usage in Android production Kotlin files.',
725
+ severity: 'WARN',
726
+ platform: 'android',
727
+ locked: true,
728
+ when: {
729
+ kind: 'Heuristic',
730
+ where: {
731
+ ruleId: 'heuristics.android.inject-constructor-constructor-injection.ast',
732
+ },
733
+ },
734
+ then: {
735
+ kind: 'Finding',
736
+ message: 'AST heuristic detected @Inject constructor usage in Android production code.',
737
+ code: 'HEURISTICS_ANDROID_INJECT_CONSTRUCTOR_CONSTRUCTOR_INJECTION_AST',
738
+ },
739
+ },
740
+ {
741
+ id: 'heuristics.android.module-installin-provide-dependencies.ast',
742
+ description: 'Detects @Module + @InstallIn usage in Android production Kotlin files.',
743
+ severity: 'WARN',
744
+ platform: 'android',
745
+ locked: true,
746
+ when: {
747
+ kind: 'Heuristic',
748
+ where: {
749
+ ruleId: 'heuristics.android.module-installin-provide-dependencies.ast',
750
+ },
751
+ },
752
+ then: {
753
+ kind: 'Finding',
754
+ message: 'AST heuristic detected @Module + @InstallIn usage in Android production code.',
755
+ code: 'HEURISTICS_ANDROID_MODULE_INSTALLIN_PROVIDE_DEPENDENCIES_AST',
756
+ },
757
+ },
758
+ {
759
+ id: 'heuristics.android.viewmodelscoped-para-dependencias-de-viewmodel.ast',
760
+ description: 'Detects @ViewModelScoped usage in Android production Kotlin files.',
761
+ severity: 'WARN',
762
+ platform: 'android',
763
+ locked: true,
764
+ when: {
765
+ kind: 'Heuristic',
766
+ where: {
767
+ ruleId: 'heuristics.android.viewmodelscoped-para-dependencias-de-viewmodel.ast',
768
+ },
769
+ },
770
+ then: {
771
+ kind: 'Finding',
772
+ message: 'AST heuristic detected @ViewModelScoped usage in Android production code.',
773
+ code: 'HEURISTICS_ANDROID_VIEWMODELSCOPED_PARA_DEPENDENCIAS_DE_VIEWMODEL_AST',
774
+ },
775
+ },
666
776
  {
667
777
  id: 'heuristics.android.workmanager-androidx-work-work-runtime-ktx.ast',
668
778
  description: 'Detects WorkManager dependency usage in Android build files.',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.153",
3
+ "version": "6.3.154",
4
4
  "description": "Enterprise-grade AST Intelligence System with multi-platform support (iOS, Android, Backend, Frontend) and Feature-First + DDD + Clean Architecture enforcement. Includes dynamic violations API for intelligent querying.",
5
5
  "main": "index.js",
6
6
  "bin": {
package/skills.lock.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": "1.0",
3
3
  "compilerVersion": "1.0.0",
4
- "generatedAt": "2026-05-03T20:19:41.600Z",
4
+ "generatedAt": "2026-05-05T20:36:04.017Z",
5
5
  "bundles": [
6
6
  {
7
7
  "name": "android-guidelines",
@@ -5298,7 +5298,7 @@
5298
5298
  "name": "ios-guidelines",
5299
5299
  "version": "1.0.0",
5300
5300
  "source": "file:vendor/skills/ios-enterprise-rules/SKILL.md",
5301
- "hash": "630af341b60b0303348b072f594200e52dc29ca1df3687fc1ed077ec9635b8d4",
5301
+ "hash": "3c15fbec440154ff12f4ca998166a3faed9d943831bf34e8c80969bbcd0f94e5",
5302
5302
  "rules": [
5303
5303
  {
5304
5304
  "id": "skills.ios.guideline.ios.accessibility-identifiers-para-localizar-elementos",
@@ -5384,18 +5384,6 @@
5384
5384
  "evaluationMode": "DECLARATIVE",
5385
5385
  "origin": "core"
5386
5386
  },
5387
- {
5388
- "id": "skills.ios.guideline.ios.apiendpoint-como-struct-data-driven-ocp-endpoints-en-features-no-enum-",
5389
- "description": "APIEndpoint como struct data-driven - OCP: endpoints en features, no enum central",
5390
- "severity": "WARN",
5391
- "platform": "ios",
5392
- "sourceSkill": "ios-guidelines",
5393
- "sourcePath": "vendor/skills/ios-enterprise-rules/SKILL.md",
5394
- "confidence": "MEDIUM",
5395
- "locked": true,
5396
- "evaluationMode": "DECLARATIVE",
5397
- "origin": "core"
5398
- },
5399
5387
  {
5400
5388
  "id": "skills.ios.guideline.ios.app-transport-security-ats-https-por-defecto",
5401
5389
  "description": "App Transport Security (ATS) - HTTPS por defecto",
@@ -7761,18 +7749,6 @@
7761
7749
  "evaluationMode": "DECLARATIVE",
7762
7750
  "origin": "core"
7763
7751
  },
7764
- {
7765
- "id": "skills.ios.guideline.ios.verificar-que-no-viole-solid-srp-ocp-lsp-isp-dip",
7766
- "description": "Verificar que NO viole SOLID (SRP, OCP, LSP, ISP, DIP)",
7767
- "severity": "WARN",
7768
- "platform": "ios",
7769
- "sourceSkill": "ios-guidelines",
7770
- "sourcePath": "vendor/skills/ios-enterprise-rules/SKILL.md",
7771
- "confidence": "MEDIUM",
7772
- "locked": true,
7773
- "evaluationMode": "DECLARATIVE",
7774
- "origin": "core"
7775
- },
7776
7752
  {
7777
7753
  "id": "skills.ios.guideline.ios.viewmodels-por-pantalla-orderslistviewmodel-orderdetailviewmodel",
7778
7754
  "description": "ViewModels por pantalla - OrdersListViewModel, OrderDetailViewModel",
@@ -8098,6 +8074,19 @@
8098
8074
  "evaluationMode": "AUTO",
8099
8075
  "origin": "core"
8100
8076
  },
8077
+ {
8078
+ "id": "skills.ios.no-solid-violations",
8079
+ "description": "Verificar que NO viole SOLID (SRP, OCP, LSP, ISP, DIP)",
8080
+ "severity": "WARN",
8081
+ "platform": "ios",
8082
+ "sourceSkill": "ios-guidelines",
8083
+ "sourcePath": "vendor/skills/ios-enterprise-rules/SKILL.md",
8084
+ "stage": "PRE_WRITE",
8085
+ "confidence": "MEDIUM",
8086
+ "locked": true,
8087
+ "evaluationMode": "AUTO",
8088
+ "origin": "core"
8089
+ },
8101
8090
  {
8102
8091
  "id": "skills.ios.no-string-format",
8103
8092
  "description": "String(localized:) + Text format en lugar de String(format:)",