pumuki 6.3.209 → 6.3.211

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.
@@ -29,6 +29,7 @@ import {
29
29
  hasSwiftLegacyExpectationDescriptionUsage,
30
30
  hasSwiftLegacySwiftUiObservableWrapperUsage,
31
31
  hasSwiftMainThreadBlockingSleepUsage,
32
+ hasSwiftMassiveViewControllerResponsibilityUsage,
32
33
  hasSwiftMixedTestingFrameworksUsage,
33
34
  hasSwiftLegacyXCTestImportUsage,
34
35
  hasSwiftModernizableXCTestSuiteUsage,
@@ -40,6 +41,7 @@ import {
40
41
  hasSwiftNSManagedObjectBoundaryUsage,
41
42
  hasSwiftNSManagedObjectStateLeakUsage,
42
43
  hasSwiftNavigationViewUsage,
44
+ hasSwiftNonIBOutletImplicitlyUnwrappedOptionalUsage,
43
45
  hasSwiftObservableObjectUsage,
44
46
  hasSwiftOnTapGestureUsage,
45
47
  hasSwiftOperationQueueUsage,
@@ -283,6 +285,60 @@ final class APIClient {
283
285
  assert.equal(hasSwiftCustomSingletonUsage(ignored), false);
284
286
  });
285
287
 
288
+ test('hasSwiftMassiveViewControllerResponsibilityUsage detecta ViewControllers con acceso directo a infraestructura', () => {
289
+ const source = `
290
+ final class CheckoutViewController: UIViewController {
291
+ override func viewDidLoad() {
292
+ super.viewDidLoad()
293
+ URLSession.shared.dataTask(with: URL(string: "https://example.com")!)
294
+ UserDefaults.standard.set(true, forKey: "seen")
295
+ }
296
+ }
297
+ `;
298
+ const ignored = `
299
+ final class CheckoutViewController: UIViewController {
300
+ private let viewModel: CheckoutViewModel
301
+
302
+ init(viewModel: CheckoutViewModel) {
303
+ self.viewModel = viewModel
304
+ super.init(nibName: nil, bundle: nil)
305
+ }
306
+
307
+ override func viewDidLoad() {
308
+ super.viewDidLoad()
309
+ viewModel.load()
310
+ }
311
+ }
312
+
313
+ let text = "URLSession.shared.dataTask"
314
+ // UserDefaults.standard.set(true, forKey: "seen")
315
+ `;
316
+
317
+ assert.equal(hasSwiftMassiveViewControllerResponsibilityUsage(source), true);
318
+ assert.equal(hasSwiftMassiveViewControllerResponsibilityUsage(ignored), false);
319
+ });
320
+
321
+ test('hasSwiftNonIBOutletImplicitlyUnwrappedOptionalUsage detecta IUO fuera de IBOutlet', () => {
322
+ const source = `
323
+ final class CheckoutViewModel {
324
+ var selectedOrder: Order!
325
+ private let formatter: DateFormatter!
326
+ }
327
+ `;
328
+ const ignored = `
329
+ final class CheckoutViewController: UIViewController {
330
+ @IBOutlet weak var titleLabel: UILabel!
331
+ @IBOutlet
332
+ private weak var tableView: UITableView!
333
+ let text = "var selectedOrder: Order!"
334
+ // var selectedOrder: Order!
335
+ }
336
+ `;
337
+
338
+ assert.equal(hasSwiftNonIBOutletImplicitlyUnwrappedOptionalUsage(source), true);
339
+ assert.equal(hasSwiftNonIBOutletImplicitlyUnwrappedOptionalUsage(ignored), false);
340
+ });
341
+
286
342
  test('detectores de logging iOS detectan logs ad-hoc y PII en produccion', () => {
287
343
  const adHoc = `
288
344
  print(user.id)
@@ -528,6 +528,37 @@ export const hasSwiftCustomSingletonUsage = (source: string): boolean => {
528
528
  });
529
529
  };
530
530
 
531
+ export const hasSwiftMassiveViewControllerResponsibilityUsage = (source: string): boolean => {
532
+ const sanitized = sanitizeSwiftSourceForMultilineRegex(source);
533
+ const viewControllerPattern =
534
+ /\bclass\s+[A-Za-z_][A-Za-z0-9_]*\s*:\s*(?:[A-Za-z_][A-Za-z0-9_]*\s*,\s*)*UIViewController\b[\s\S]{0,8000}?\n\}/g;
535
+ const infrastructurePattern =
536
+ /\b(?:URLSession\s*\.\s*shared|JSONSerialization|UserDefaults\s*\.\s*standard|NSManagedObjectContext|NSPersistentContainer|NSFetchRequest|FileManager\s*\.\s*default)\b/;
537
+
538
+ for (const match of sanitized.matchAll(viewControllerPattern)) {
539
+ const body = match[0] ?? '';
540
+ if (infrastructurePattern.test(body)) {
541
+ return true;
542
+ }
543
+ }
544
+ return false;
545
+ };
546
+
547
+ export const hasSwiftNonIBOutletImplicitlyUnwrappedOptionalUsage = (source: string): boolean => {
548
+ const lines = source.split(/\r?\n/);
549
+ const implicitlyUnwrappedPropertyPattern =
550
+ /\b(?:var|let)\s+[A-Za-z_][A-Za-z0-9_]*\s*:\s*(?:[A-Za-z_][A-Za-z0-9_.<>?]*\s*)!\s*(?:[=,{]|$)/;
551
+
552
+ return lines.some((line, index) => {
553
+ const sanitizedLine = stripSwiftLineForSemanticScan(line);
554
+ if (!implicitlyUnwrappedPropertyPattern.test(sanitizedLine)) {
555
+ return false;
556
+ }
557
+ const previousLine = index > 0 ? stripSwiftLineForSemanticScan(lines[index - 1] ?? '') : '';
558
+ return !/\B@IBOutlet\b/.test(`${previousLine} ${sanitizedLine}`);
559
+ });
560
+ };
561
+
531
562
  export const hasSwiftAdHocLoggingUsage = (source: string): boolean => {
532
563
  return collectSwiftRegexLines(
533
564
  source,
@@ -649,6 +649,8 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
649
649
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftStrongDelegateReferenceUsage, ruleId: 'heuristics.ios.memory.strong-delegate.ast', code: 'HEURISTICS_IOS_MEMORY_STRONG_DELEGATE_AST', message: 'AST heuristic detected a strong delegate/dataSource reference; weak delegates remain the preferred baseline to avoid retain cycles.' },
650
650
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftStrongSelfEscapingClosureUsage, ruleId: 'heuristics.ios.memory.strong-self-escaping-closure.ast', code: 'HEURISTICS_IOS_MEMORY_STRONG_SELF_ESCAPING_CLOSURE_AST', message: 'AST heuristic detected strong self capture in an escaping iOS closure; weak or unowned captures remain the preferred baseline when ownership is not explicit.' },
651
651
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftCustomSingletonUsage, ruleId: 'heuristics.ios.architecture.custom-singleton.ast', code: 'HEURISTICS_IOS_ARCHITECTURE_CUSTOM_SINGLETON_AST', message: 'AST heuristic detected a custom static shared singleton in iOS production code; dependency injection remains the preferred baseline for app-owned services.' },
652
+ { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftMassiveViewControllerResponsibilityUsage, ruleId: 'heuristics.ios.architecture.massive-view-controller.ast', code: 'HEURISTICS_IOS_ARCHITECTURE_MASSIVE_VIEW_CONTROLLER_AST', message: 'AST heuristic detected a UIViewController with direct infrastructure/data access; move data access behind application/domain boundaries.' },
653
+ { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftNonIBOutletImplicitlyUnwrappedOptionalUsage, ruleId: 'heuristics.ios.safety.non-iboutlet-iuo.ast', code: 'HEURISTICS_IOS_SAFETY_NON_IBOUTLET_IUO_AST', message: 'AST heuristic detected an implicitly unwrapped optional outside IBOutlet wiring; explicit optionals or initialization guarantees remain the preferred baseline.' },
652
654
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftAdHocLoggingUsage, ruleId: 'heuristics.ios.logging.adhoc-print.ast', code: 'HEURISTICS_IOS_LOGGING_ADHOC_PRINT_AST', message: 'AST heuristic detected print/debugPrint/dump/NSLog/os_log usage in iOS production code.' },
653
655
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftSensitiveLoggingUsage, ruleId: 'heuristics.ios.logging.sensitive-data.ast', code: 'HEURISTICS_IOS_LOGGING_SENSITIVE_DATA_AST', message: 'AST heuristic detected sensitive data in an iOS logging call.' },
654
656
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftAlamofireUsage, ruleId: 'heuristics.ios.networking.alamofire.ast', code: 'HEURISTICS_IOS_NETWORKING_ALAMOFIRE_AST', message: 'AST heuristic detected Alamofire usage in iOS production code; URLSession remains the preferred baseline for new code.' },
@@ -238,6 +238,44 @@ export const iosRules: RuleSet = [
238
238
  code: 'HEURISTICS_IOS_ARCHITECTURE_CUSTOM_SINGLETON_AST',
239
239
  },
240
240
  },
241
+ {
242
+ id: 'heuristics.ios.architecture.massive-view-controller.ast',
243
+ description: 'Detects UIViewController classes with direct infrastructure/data access.',
244
+ severity: 'WARN',
245
+ platform: 'ios',
246
+ locked: true,
247
+ when: {
248
+ kind: 'Heuristic',
249
+ where: {
250
+ ruleId: 'heuristics.ios.architecture.massive-view-controller.ast',
251
+ },
252
+ },
253
+ then: {
254
+ kind: 'Finding',
255
+ message:
256
+ 'AST heuristic detected a UIViewController with direct infrastructure/data access; move data access behind application/domain boundaries.',
257
+ code: 'HEURISTICS_IOS_ARCHITECTURE_MASSIVE_VIEW_CONTROLLER_AST',
258
+ },
259
+ },
260
+ {
261
+ id: 'heuristics.ios.safety.non-iboutlet-iuo.ast',
262
+ description: 'Detects implicitly unwrapped optionals outside IBOutlet wiring.',
263
+ severity: 'WARN',
264
+ platform: 'ios',
265
+ locked: true,
266
+ when: {
267
+ kind: 'Heuristic',
268
+ where: {
269
+ ruleId: 'heuristics.ios.safety.non-iboutlet-iuo.ast',
270
+ },
271
+ },
272
+ then: {
273
+ kind: 'Finding',
274
+ message:
275
+ 'AST heuristic detected an implicitly unwrapped optional outside IBOutlet wiring; explicit optionals or initialization guarantees remain the preferred baseline.',
276
+ code: 'HEURISTICS_IOS_SAFETY_NON_IBOUTLET_IUO_AST',
277
+ },
278
+ },
241
279
  {
242
280
  id: 'heuristics.ios.logging.adhoc-print.ast',
243
281
  description: 'Detects print/debugPrint/dump/NSLog/os_log usage in iOS production code.',
@@ -66,6 +66,22 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
66
66
  'ios.architecture.custom-singleton',
67
67
  ['heuristics.ios.architecture.custom-singleton.ast']
68
68
  ),
69
+ 'skills.ios.guideline.ios.massive-view-controllers-viewcontrollers-que-mezclan-presentacio-n-nav': heuristicDetector(
70
+ 'ios.architecture.massive-view-controller',
71
+ ['heuristics.ios.architecture.massive-view-controller.ast']
72
+ ),
73
+ 'skills.ios.guideline.ios.mvc-evitar-massive-view-controller-no-escalable': heuristicDetector(
74
+ 'ios.architecture.massive-view-controller',
75
+ ['heuristics.ios.architecture.massive-view-controller.ast']
76
+ ),
77
+ 'skills.ios.guideline.ios.implicitly-unwrapped-solo-para-iboutlets-y-casos-muy-especi-ficos': heuristicDetector(
78
+ 'ios.safety.non-iboutlet-iuo',
79
+ ['heuristics.ios.safety.non-iboutlet-iuo.ast']
80
+ ),
81
+ 'skills.ios.guideline.ios.singletons-dificultan-testing': heuristicDetector(
82
+ 'ios.architecture.custom-singleton',
83
+ ['heuristics.ios.architecture.custom-singleton.ast']
84
+ ),
69
85
  'skills.ios.guideline.ios.prohibido-print-y-logs-ad-hoc': heuristicDetector(
70
86
  'ios.logging.adhoc-print',
71
87
  ['heuristics.ios.logging.adhoc-print.ast']
@@ -444,6 +444,23 @@ const normalizeKnownRuleTarget = (
444
444
  ) {
445
445
  return 'skills.ios.guideline.ios.no-singleton-usar-inyeccio-n-de-dependencias-no-compartir-instancias-g';
446
446
  }
447
+ if (
448
+ includes('massive view controller') ||
449
+ includes('massive view controllers') ||
450
+ includes('viewcontrollers que mezclan') ||
451
+ includes('viewcontroller que mezclan') ||
452
+ (includes('mvc') && includes('evitar'))
453
+ ) {
454
+ return 'skills.ios.guideline.ios.massive-view-controllers-viewcontrollers-que-mezclan-presentacio-n-nav';
455
+ }
456
+ if (
457
+ includes('implicitly unwrapped') ||
458
+ includes('implicit unwrapped') ||
459
+ includes('iboutlet') ||
460
+ includes('iboutlets')
461
+ ) {
462
+ return 'skills.ios.guideline.ios.implicitly-unwrapped-solo-para-iboutlets-y-casos-muy-especi-ficos';
463
+ }
447
464
  if (
448
465
  includes('mixing legacy xctest style') ||
449
466
  includes('mixed xctest and swift testing') ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.209",
3
+ "version": "6.3.211",
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-13T12:45:06.608Z",
4
+ "generatedAt": "2026-05-13T12:56:51.940Z",
5
5
  "bundles": [
6
6
  {
7
7
  "name": "android-guidelines",
@@ -5764,7 +5764,7 @@
5764
5764
  "name": "ios-guidelines",
5765
5765
  "version": "1.0.0",
5766
5766
  "source": "file:vendor/skills/ios-enterprise-rules/SKILL.md",
5767
- "hash": "5c20c168bf6f54010cfac75cce1311a03ed2345ac20eef7b2a4458ad8efd3fc4",
5767
+ "hash": "1395678f8abd2b8eddb9d4270fd205517ed4ce4e3796def5f7846592990ec778",
5768
5768
  "rules": [
5769
5769
  {
5770
5770
  "id": "skills.ios.guideline.ios.accessibility-identifiers-para-localizar-elementos",
@@ -6736,7 +6736,7 @@
6736
6736
  "sourcePath": "vendor/skills/ios-enterprise-rules/SKILL.md",
6737
6737
  "confidence": "MEDIUM",
6738
6738
  "locked": true,
6739
- "evaluationMode": "DECLARATIVE",
6739
+ "evaluationMode": "AUTO",
6740
6740
  "origin": "core"
6741
6741
  },
6742
6742
  {
@@ -6957,14 +6957,14 @@
6957
6957
  },
6958
6958
  {
6959
6959
  "id": "skills.ios.guideline.ios.massive-view-controllers-viewcontrollers-que-mezclan-presentacio-n-nav",
6960
- "description": "Massive View Controllers - ViewControllers que mezclan presentación, navegación, estado, acceso a datos o coordinación de infraestructura",
6960
+ "description": "MVC (evitar) - Massive View Controller, no escalable",
6961
6961
  "severity": "ERROR",
6962
6962
  "platform": "ios",
6963
6963
  "sourceSkill": "ios-guidelines",
6964
6964
  "sourcePath": "vendor/skills/ios-enterprise-rules/SKILL.md",
6965
- "confidence": "HIGH",
6965
+ "confidence": "MEDIUM",
6966
6966
  "locked": true,
6967
- "evaluationMode": "DECLARATIVE",
6967
+ "evaluationMode": "AUTO",
6968
6968
  "origin": "core"
6969
6969
  },
6970
6970
  {
@@ -6991,18 +6991,6 @@
6991
6991
  "evaluationMode": "DECLARATIVE",
6992
6992
  "origin": "core"
6993
6993
  },
6994
- {
6995
- "id": "skills.ios.guideline.ios.mvc-evitar-massive-view-controller-no-escalable",
6996
- "description": "MVC (evitar) - Massive View Controller, no escalable",
6997
- "severity": "ERROR",
6998
- "platform": "ios",
6999
- "sourceSkill": "ios-guidelines",
7000
- "sourcePath": "vendor/skills/ios-enterprise-rules/SKILL.md",
7001
- "confidence": "MEDIUM",
7002
- "locked": true,
7003
- "evaluationMode": "DECLARATIVE",
7004
- "origin": "core"
7005
- },
7006
6994
  {
7007
6995
  "id": "skills.ios.guideline.ios.mvvm-c-coordinator-para-navegacio-n",
7008
6996
  "description": "MVVM-C - + Coordinator para navegación",