pumuki 6.3.226 → 6.3.227

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.
@@ -38,6 +38,7 @@ import {
38
38
  hasSwiftNonLazyScrollForEachUsage,
39
39
  hasSwiftViewBodyObjectCreationUsage,
40
40
  hasSwiftUiImageDataDecodingUsage,
41
+ hasSwiftUiInlineActionLogicUsage,
41
42
  hasSwiftAssumeIsolatedUsage,
42
43
  hasSwiftCoreDataLayerLeakUsage,
43
44
  hasSwiftSwiftDataLayerLeakUsage,
@@ -220,6 +221,41 @@ struct AvatarView: View {
220
221
  assert.equal(hasSwiftUiImageDataDecodingUsage(safe), false);
221
222
  });
222
223
 
224
+ test('hasSwiftUiInlineActionLogicUsage detecta lógica inline en Button y preserva método referenciado', () => {
225
+ const source = `
226
+ struct CheckoutView: View {
227
+ @State private var isLoading = false
228
+
229
+ var body: some View {
230
+ Button {
231
+ if isLoading {
232
+ return
233
+ }
234
+ Task {
235
+ await submit()
236
+ }
237
+ } label: {
238
+ Text("Pay")
239
+ }
240
+ }
241
+ }
242
+ `;
243
+ const safe = `
244
+ struct CheckoutView: View {
245
+ var body: some View {
246
+ Button(action: submit) {
247
+ Text("Pay")
248
+ }
249
+ let sample = "Button { if loading { return } } label:"
250
+ // Button { if loading { return } } label:
251
+ }
252
+ }
253
+ `;
254
+
255
+ assert.equal(hasSwiftUiInlineActionLogicUsage(source), true);
256
+ assert.equal(hasSwiftUiInlineActionLogicUsage(safe), false);
257
+ });
258
+
223
259
  test('hasSwiftForceTryUsage detecta try! y descarta try?', () => {
224
260
  const positive = `
225
261
  func load() {
@@ -401,6 +401,16 @@ export const hasSwiftUiImageDataDecodingUsage = (source: string): boolean => {
401
401
  return /\bUIImage\s*\(\s*data\s*:/.test(swiftSource);
402
402
  };
403
403
 
404
+ export const hasSwiftUiInlineActionLogicUsage = (source: string): boolean => {
405
+ const swiftSource = sanitizeSwiftSourceForMultilineRegex(source);
406
+ const inlineButtonActionPattern =
407
+ /\bButton\s*\{[\s\S]{0,900}\b(?:if|guard|switch|for|while|Task)\b[\s\S]{0,900}\}\s*label\s*:/;
408
+ const inlineActionParameterPattern =
409
+ /\bButton\s*\([^)]*\baction\s*:\s*\{[\s\S]{0,900}\b(?:if|guard|switch|for|while|Task)\b[\s\S]{0,900}\}/;
410
+
411
+ return inlineButtonActionPattern.test(swiftSource) || inlineActionParameterPattern.test(swiftSource);
412
+ };
413
+
404
414
  export const hasSwiftDispatchQueueUsage = (source: string): boolean => {
405
415
  return scanCodeLikeSource(source, ({ source: swiftSource, index, current }) => {
406
416
  if (current !== 'D' || !hasIdentifierAt(swiftSource, index, 'DispatchQueue')) {
@@ -688,6 +688,7 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
688
688
  { platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftNonLazyScrollForEachUsage, ruleId: 'heuristics.ios.swiftui.non-lazy-scroll-foreach.ast', code: 'HEURISTICS_IOS_SWIFTUI_NON_LAZY_SCROLL_FOREACH_AST', message: 'AST heuristic detected ScrollView with a non-lazy stack feeding ForEach; LazyVStack/LazyHStack remain the preferred baseline for large scrollable collections.' },
689
689
  { platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftViewBodyObjectCreationUsage, ruleId: 'heuristics.ios.swiftui.body-object-creation.ast', code: 'HEURISTICS_IOS_SWIFTUI_BODY_OBJECT_CREATION_AST', message: 'AST heuristic detected formatter object creation inside SwiftUI body; keep body simple and move expensive objects out of render paths.' },
690
690
  { platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftUiImageDataDecodingUsage, ruleId: 'heuristics.ios.swiftui.image-data-decoding.ast', code: 'HEURISTICS_IOS_SWIFTUI_IMAGE_DATA_DECODING_AST', message: 'AST heuristic detected UIImage(data:) in SwiftUI presentation; downsample image data before rendering large images.' },
691
+ { platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftUiInlineActionLogicUsage, ruleId: 'heuristics.ios.swiftui.inline-action-logic.ast', code: 'HEURISTICS_IOS_SWIFTUI_INLINE_ACTION_LOGIC_AST', message: 'AST heuristic detected inline logic inside a SwiftUI action handler; action handlers should reference methods and keep view declarations focused.' },
691
692
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftNavigationViewUsage, ruleId: 'heuristics.ios.navigation-view.ast', code: 'HEURISTICS_IOS_NAVIGATION_VIEW_AST', message: 'AST heuristic detected NavigationView usage.' },
692
693
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftForegroundColorUsage, ruleId: 'heuristics.ios.foreground-color.ast', code: 'HEURISTICS_IOS_FOREGROUND_COLOR_AST', message: 'AST heuristic detected foregroundColor usage.' },
693
694
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftCornerRadiusUsage, ruleId: 'heuristics.ios.corner-radius.ast', code: 'HEURISTICS_IOS_CORNER_RADIUS_AST', message: 'AST heuristic detected cornerRadius usage.' },
@@ -968,6 +968,25 @@ export const iosRules: RuleSet = [
968
968
  code: 'HEURISTICS_IOS_SWIFTUI_IMAGE_DATA_DECODING_AST',
969
969
  },
970
970
  },
971
+ {
972
+ id: 'heuristics.ios.swiftui.inline-action-logic.ast',
973
+ description: 'Detects inline control-flow logic inside SwiftUI action handlers.',
974
+ severity: 'WARN',
975
+ platform: 'ios',
976
+ locked: true,
977
+ when: {
978
+ kind: 'Heuristic',
979
+ where: {
980
+ ruleId: 'heuristics.ios.swiftui.inline-action-logic.ast',
981
+ },
982
+ },
983
+ then: {
984
+ kind: 'Finding',
985
+ message:
986
+ 'AST heuristic detected inline logic inside a SwiftUI action handler; action handlers should reference methods and keep view declarations focused.',
987
+ code: 'HEURISTICS_IOS_SWIFTUI_INLINE_ACTION_LOGIC_AST',
988
+ },
989
+ },
971
990
  {
972
991
  id: 'heuristics.ios.navigation-view.ast',
973
992
  description: 'Detects NavigationView usage in iOS production code.',
@@ -266,6 +266,10 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
266
266
  heuristicDetector('ios.swiftui.image-data-decoding', [
267
267
  'heuristics.ios.swiftui.image-data-decoding.ast',
268
268
  ]),
269
+ 'skills.ios.guideline.ios-swiftui-expert.action-handlers-should-reference-methods-not-contain-inline-logic':
270
+ heuristicDetector('ios.swiftui.inline-action-logic', [
271
+ 'heuristics.ios.swiftui.inline-action-logic.ast',
272
+ ]),
269
273
  'skills.ios.no-scrollview-shows-indicators': heuristicDetector(
270
274
  'ios.scrollview-shows-indicators',
271
275
  ['heuristics.ios.scrollview-shows-indicators.ast']
@@ -16,7 +16,7 @@ const MARKDOWN_BOLD_PATTERN = /[*_]{1,3}/g;
16
16
  const MULTISPACE_PATTERN = /\s+/g;
17
17
  const AST_NODE_ID_PATTERN = /\bheuristics\.[a-z0-9._-]+\.ast\b/gi;
18
18
  const RULE_KEYWORDS =
19
- /\b(always|siempre|prefer|use|usar|avoid|evitar|never|nunca|must|obligatorio|required|disallow|do not|no|suggest)\b/i;
19
+ /\b(always|siempre|prefer|use|usar|avoid|evitar|never|nunca|must|obligatorio|required|disallow|do not|no|suggest|should)\b/i;
20
20
 
21
21
  const normalizeForLookup = (value: string): string => {
22
22
  return value
@@ -381,6 +381,13 @@ const normalizeKnownRuleTarget = (
381
381
  ) {
382
382
  return 'skills.ios.guideline.ios-swiftui-expert.suggest-image-downsampling-when-uiimage-data-is-encountered';
383
383
  }
384
+ if (
385
+ (includes('action handlers') && includes('reference methods')) ||
386
+ (includes('action handlers') && includes('inline logic')) ||
387
+ (includes('handlers should reference methods') && includes('inline logic'))
388
+ ) {
389
+ return 'skills.ios.guideline.ios-swiftui-expert.action-handlers-should-reference-methods-not-contain-inline-logic';
390
+ }
384
391
  if (
385
392
  includes('scrollindicators hidden') ||
386
393
  includes('scroll indicators hidden') ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.226",
3
+ "version": "6.3.227",
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-13T16:23:26.589Z",
4
+ "generatedAt": "2026-05-13T16:27:11.446Z",
5
5
  "bundles": [
6
6
  {
7
7
  "name": "android-guidelines",
@@ -8632,8 +8632,20 @@
8632
8632
  "name": "ios-swiftui-expert-guidelines",
8633
8633
  "version": "1.0.0",
8634
8634
  "source": "file:vendor/skills/swiftui-expert-skill/SKILL.md",
8635
- "hash": "10b8966d8204f1abb91af3254cfb809bf0a3822b8d09347c28639d23e6dfc1c7",
8635
+ "hash": "a69126588892428f136f55666d721346015d4684ded6dfa07509045db95f7f48",
8636
8636
  "rules": [
8637
+ {
8638
+ "id": "skills.ios.guideline.ios-swiftui-expert.action-handlers-should-reference-methods-not-contain-inline-logic",
8639
+ "description": "Action handlers should reference methods, not contain inline logic",
8640
+ "severity": "WARN",
8641
+ "platform": "ios",
8642
+ "sourceSkill": "ios-swiftui-expert-guidelines",
8643
+ "sourcePath": "vendor/skills/swiftui-expert-skill/SKILL.md",
8644
+ "confidence": "MEDIUM",
8645
+ "locked": true,
8646
+ "evaluationMode": "AUTO",
8647
+ "origin": "core"
8648
+ },
8637
8649
  {
8638
8650
  "id": "skills.ios.guideline.ios-swiftui-expert.always-mark-state-and-stateobject-as-private-makes-dependencies-clear",
8639
8651
  "description": "Always mark @State and @StateObject as private (makes dependencies clear)",
@@ -8742,6 +8754,18 @@
8742
8754
  "evaluationMode": "DECLARATIVE",
8743
8755
  "origin": "core"
8744
8756
  },
8757
+ {
8758
+ "id": "skills.ios.guideline.ios-swiftui-expert.sheets-should-own-their-actions-and-call-dismiss-internally",
8759
+ "description": "Sheets should own their actions and call dismiss() internally",
8760
+ "severity": "WARN",
8761
+ "platform": "ios",
8762
+ "sourceSkill": "ios-swiftui-expert-guidelines",
8763
+ "sourcePath": "vendor/skills/swiftui-expert-skill/SKILL.md",
8764
+ "confidence": "MEDIUM",
8765
+ "locked": true,
8766
+ "evaluationMode": "DECLARATIVE",
8767
+ "origin": "core"
8768
+ },
8745
8769
  {
8746
8770
  "id": "skills.ios.guideline.ios-swiftui-expert.suggest-image-downsampling-when-uiimage-data-is-encountered",
8747
8771
  "description": "Suggest image downsampling when UIImage(data:) is used (as optional optimization, see references/image-optimization.md)",
@@ -8946,6 +8970,18 @@
8946
8970
  "evaluationMode": "DECLARATIVE",
8947
8971
  "origin": "core"
8948
8972
  },
8973
+ {
8974
+ "id": "skills.ios.guideline.ios-swiftui-expert.views-should-work-in-any-context-don-t-assume-screen-size-or-presentat",
8975
+ "description": "Views should work in any context (don't assume screen size or presentation style)",
8976
+ "severity": "WARN",
8977
+ "platform": "ios",
8978
+ "sourceSkill": "ios-swiftui-expert-guidelines",
8979
+ "sourcePath": "vendor/skills/swiftui-expert-skill/SKILL.md",
8980
+ "confidence": "MEDIUM",
8981
+ "locked": true,
8982
+ "evaluationMode": "DECLARATIVE",
8983
+ "origin": "core"
8984
+ },
8949
8985
  {
8950
8986
  "id": "skills.ios.no-anyview",
8951
8987
  "description": "Avoid AnyView in list rows",