pumuki 6.3.262 → 6.3.264

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [6.3.264] - 2026-05-14
4
+
5
+ - iOS: `skills.ios.no-callback-style` now emits actionable AST-style evidence for escaping callback signatures outside approved bridge layers, including exact lines, primary/related nodes and remediation toward `async/await` APIs or explicit adapters.
6
+
7
+ ## [6.3.263] - 2026-05-14
8
+
9
+ - iOS: `skills.ios.no-anyview` now emits actionable AST-style evidence for SwiftUI `AnyView`, including exact lines, primary/related nodes and remediation toward concrete view composition or `@ViewBuilder` branches.
10
+
3
11
  ## [6.3.262] - 2026-05-14
4
12
 
5
13
  - iOS: `skills.ios.no-force-cast` now emits actionable AST-style evidence for Swift `as!` expressions, including exact lines, primary/related nodes and remediation toward conditional casts or typed boundaries.
@@ -13,6 +13,8 @@ import {
13
13
  collectSwiftWaitForExpectationsLines,
14
14
  collectSwiftXCTestAssertionLines,
15
15
  collectSwiftXCTUnwrapLines,
16
+ collectSwiftAnyViewLines,
17
+ collectSwiftCallbackStyleSignatureLines,
16
18
  hasSwiftAnyViewUsage,
17
19
  hasSwiftAsyncWithoutAwaitUsage,
18
20
  hasSwiftCallbackStyleSignature,
@@ -144,11 +146,13 @@ func render() -> some View {
144
146
  }
145
147
  `;
146
148
  assert.equal(hasSwiftAnyViewUsage(source), true);
149
+ assert.deepEqual(collectSwiftAnyViewLines(source), [3]);
147
150
  });
148
151
 
149
152
  test('hasSwiftAnyViewUsage ignora comentarios, strings y coincidencias parciales', () => {
150
153
  const source = `\n// AnyView(Text("debug"))\nlet value = "AnyView(Text(\\"debug\\"))"\nlet customAnyViewBuilder = true\n`;
151
154
  assert.equal(hasSwiftAnyViewUsage(source), false);
155
+ assert.deepEqual(collectSwiftAnyViewLines(source), []);
152
156
  });
153
157
 
154
158
  test('hasSwiftEmptyCatchUsage detecta catch vacio e ignora comentarios y strings', () => {
@@ -480,11 +484,14 @@ func run(handler: @MainActor @escaping () -> Void) {}
480
484
  `;
481
485
  assert.equal(hasSwiftCallbackStyleSignature(completionSignature), true);
482
486
  assert.equal(hasSwiftCallbackStyleSignature(handlerSignature), true);
487
+ assert.deepEqual(collectSwiftCallbackStyleSignatureLines(completionSignature), [2]);
488
+ assert.deepEqual(collectSwiftCallbackStyleSignatureLines(handlerSignature), [2]);
483
489
  });
484
490
 
485
491
  test('hasSwiftCallbackStyleSignature ignora usos fuera de firmas callback', () => {
486
492
  const source = `\n// @escaping completion: @escaping () -> Void\nlet text = "@escaping completion: @escaping () -> Void"\n`;
487
493
  assert.equal(hasSwiftCallbackStyleSignature(source), false);
494
+ assert.deepEqual(collectSwiftCallbackStyleSignatureLines(source), []);
488
495
  });
489
496
 
490
497
  test('detecta primitivas GCD y OperationQueue en codigo ejecutable', () => {
@@ -394,6 +394,19 @@ export const hasSwiftAnyViewUsage = (source: string): boolean => {
394
394
  });
395
395
  };
396
396
 
397
+ export const collectSwiftAnyViewLines = (source: string): readonly number[] => {
398
+ return sortedUniqueLines(collectSwiftRegexLines(source, /\bAnyView\b/));
399
+ };
400
+
401
+ export const collectSwiftCallbackStyleSignatureLines = (source: string): readonly number[] => {
402
+ return sortedUniqueLines(
403
+ collectSwiftRegexLines(
404
+ source,
405
+ /\b(?:completion|completionHandler|callback|handler)\s*:\s*(?:@[A-Za-z_][A-Za-z0-9_]*(?:\([^)]*\))?\s+)*@escaping\b/
406
+ )
407
+ );
408
+ };
409
+
397
410
  export const hasSwiftNonLazyScrollForEachUsage = (source: string): boolean => {
398
411
  const swiftSource = sanitizeSwiftSourceForMultilineRegex(source);
399
412
  const nonLazyScrollableCollectionPattern =
@@ -643,10 +643,10 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
643
643
  { platform: 'ios', pathCheck: isIOSPodfilePath, excludePaths: [], detect: detectsTrackedFilePresence, ruleId: 'heuristics.ios.dependencies.cocoapods.ast', code: 'HEURISTICS_IOS_DEPENDENCIES_COCOAPODS_AST', message: 'AST heuristic detected CocoaPods dependency files in an iOS project; Swift Package Manager remains the preferred baseline for new code.' },
644
644
  { platform: 'ios', pathCheck: isIOSCartfilePath, excludePaths: [], detect: detectsTrackedFilePresence, ruleId: 'heuristics.ios.dependencies.carthage.ast', code: 'HEURISTICS_IOS_DEPENDENCIES_CARTHAGE_AST', message: 'AST heuristic detected Carthage dependency files in an iOS project; Swift Package Manager remains the preferred baseline for new code.' },
645
645
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftForceUnwrap, locateLines: TextIOS.collectSwiftForceUnwrapLines, primaryNode: (lines) => ({ kind: 'member', name: 'force unwrap postfix !', lines }), relatedNodes: (lines) => [{ kind: 'member', name: 'replacement: guarded optional binding or explicit failure path', lines }], why: 'Force unwrap turns optional handling into a runtime crash path instead of a checked domain, UI or infrastructure decision.', impact: 'A nil value can terminate the app outside the error boundary, making production behavior non-deterministic and hard to recover or test.', expected_fix: 'Replace postfix ! with guard let, if let, nil coalescing, throwing validation, or an explicit fallback. In modern Swift tests prefer #require when the unwrap is part of an assertion contract.', ruleId: 'heuristics.ios.force-unwrap.ast', code: 'HEURISTICS_IOS_FORCE_UNWRAP_AST', message: 'AST heuristic detected force unwrap usage.' },
646
- { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftAnyViewUsage, ruleId: 'heuristics.ios.anyview.ast', code: 'HEURISTICS_IOS_ANYVIEW_AST', message: 'AST heuristic detected AnyView usage.' },
646
+ { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftAnyViewUsage, locateLines: TextIOS.collectSwiftAnyViewLines, primaryNode: (lines) => ({ kind: 'call', name: 'type erasure wrapper AnyView', lines }), relatedNodes: (lines) => [{ kind: 'call', name: 'replacement: concrete View composition or @ViewBuilder branch', lines }], why: 'AnyView erases SwiftUI view identity and type information, hiding structural changes from the compiler and making diffing less predictable.', impact: 'SwiftUI may lose optimization opportunities, navigation/sheet branches become harder to reason about, and remediating UI regressions requires reading dynamic wrappers instead of concrete view composition.', expected_fix: 'Replace AnyView with concrete some View composition, @ViewBuilder branching, generic View parameters, or small extracted subviews that preserve static SwiftUI identity.', ruleId: 'heuristics.ios.anyview.ast', code: 'HEURISTICS_IOS_ANYVIEW_AST', message: 'AST heuristic detected AnyView usage.' },
647
647
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftForceTryUsage, locateLines: TextIOS.collectSwiftForceTryLines, primaryNode: (lines) => ({ kind: 'call', name: 'force try expression try!', lines }), relatedNodes: (lines) => [{ kind: 'call', name: 'replacement: do/catch or throwing boundary', lines }], why: 'Force try converts a throwable operation into an unconditional runtime crash instead of preserving the typed error boundary.', impact: 'A recoverable domain, network, persistence or decoding error can terminate the app and bypass user-facing recovery, telemetry and tests.', expected_fix: 'Replace try! with do/catch, try await propagation, throws on the current boundary, or a guarded fallback that handles the error explicitly.', ruleId: 'heuristics.ios.force-try.ast', code: 'HEURISTICS_IOS_FORCE_TRY_AST', message: 'AST heuristic detected force try usage.' },
648
648
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftForceCastUsage, locateLines: TextIOS.collectSwiftForceCastLines, primaryNode: (lines) => ({ kind: 'call', name: 'force cast expression as!', lines }), relatedNodes: (lines) => [{ kind: 'call', name: 'replacement: conditional cast or typed boundary', lines }], why: 'Force cast converts a type mismatch into an unconditional runtime crash instead of preserving a checked domain or presentation boundary.', impact: 'Unexpected payloads, dependency substitutions or navigation models can terminate the app instead of producing a recoverable validation path.', expected_fix: 'Replace as! with as?, guard let, pattern matching, generic constraints, protocol boundaries, or a typed mapper that validates the runtime value explicitly.', ruleId: 'heuristics.ios.force-cast.ast', code: 'HEURISTICS_IOS_FORCE_CAST_AST', message: 'AST heuristic detected force cast usage.' },
649
- { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath, isApprovedIOSBridgePath], detect: TextIOS.hasSwiftCallbackStyleSignature, ruleId: 'heuristics.ios.callback-style.ast', code: 'HEURISTICS_IOS_CALLBACK_STYLE_AST', message: 'AST heuristic detected callback-style API signature outside bridge layers.' },
649
+ { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath, isApprovedIOSBridgePath], detect: TextIOS.hasSwiftCallbackStyleSignature, locateLines: TextIOS.collectSwiftCallbackStyleSignatureLines, primaryNode: (lines) => ({ kind: 'call', name: 'escaping callback-style API signature', lines }), relatedNodes: (lines) => [{ kind: 'call', name: 'replacement: async/await API or explicit bridge adapter', lines }], why: 'Callback-style completion APIs outside bridge layers bypass Swift structured concurrency and make cancellation, isolation and error flow implicit.', impact: 'Consumers must reason about escaping lifetime, actor hops and callback ordering manually, which increases race, leak and flaky-test risk in production iOS flows.', expected_fix: 'Expose async/await or AsyncSequence APIs in production boundaries. Keep callbacks only inside approved bridge/adapters that wrap legacy SDKs and document the conversion point explicitly.', ruleId: 'heuristics.ios.callback-style.ast', code: 'HEURISTICS_IOS_CALLBACK_STYLE_AST', message: 'AST heuristic detected callback-style API signature outside bridge layers.' },
650
650
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftDispatchQueueUsage, ruleId: 'heuristics.ios.dispatchqueue.ast', code: 'HEURISTICS_IOS_DISPATCHQUEUE_AST', message: 'AST heuristic detected DispatchQueue usage.' },
651
651
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftDispatchGroupUsage, ruleId: 'heuristics.ios.dispatchgroup.ast', code: 'HEURISTICS_IOS_DISPATCHGROUP_AST', message: 'AST heuristic detected DispatchGroup usage.' },
652
652
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftDispatchSemaphoreUsage, ruleId: 'heuristics.ios.dispatchsemaphore.ast', code: 'HEURISTICS_IOS_DISPATCHSEMAPHORE_AST', message: 'AST heuristic detected DispatchSemaphore usage.' },
@@ -6,6 +6,14 @@ This file keeps only the operational highlights and rollout notes that matter wh
6
6
 
7
7
  ## 2026-04 (CLI stability and macOS notifications)
8
8
 
9
+ ### 2026-05-14 (v6.3.264)
10
+
11
+ - Published `pumuki@6.3.264` with AST-style line/node evidence for `skills.ios.no-callback-style`, making escaping callback APIs outside bridge layers remediable through `async/await`, `AsyncSequence` or explicit legacy adapters.
12
+
13
+ ### 2026-05-14 (v6.3.263)
14
+
15
+ - Published `pumuki@6.3.263` with AST-style line/node evidence for `skills.ios.no-anyview`, making SwiftUI `AnyView` type erasure remediable through concrete composition, generics or `@ViewBuilder` branches.
16
+
9
17
  ### 2026-05-14 (v6.3.262)
10
18
 
11
19
  - Published `pumuki@6.3.262` with AST-style line/node evidence for `skills.ios.no-force-cast`, making `as!` crashes remediable through conditional casts, typed boundaries or explicit mappers.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.262",
3
+ "version": "6.3.264",
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": {