pumuki 6.3.220 → 6.3.222
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/core/facts/detectors/text/ios.test.ts +61 -0
- package/core/facts/detectors/text/ios.ts +14 -0
- package/core/facts/extractHeuristicFacts.ts +2 -0
- package/core/rules/presets/heuristics/ios.ts +36 -0
- package/integrations/config/skillsDetectorRegistry.ts +8 -0
- package/integrations/config/skillsMarkdownRules.ts +14 -0
- package/package.json +1 -1
- package/skills.lock.json +4 -4
|
@@ -61,6 +61,8 @@ import {
|
|
|
61
61
|
hasSwiftSensitiveUserDefaultsStorageUsage,
|
|
62
62
|
hasSwiftInsecureTransportUsage,
|
|
63
63
|
hasSwiftJSONSerializationUsage,
|
|
64
|
+
hasSwiftExplicitColorStaticMemberUsage,
|
|
65
|
+
hasSwiftClosureBasedViewBuilderContentUsage,
|
|
64
66
|
hasSwiftInlineForEachTransformUsage,
|
|
65
67
|
hasSwiftStringFormatUsage,
|
|
66
68
|
hasSwiftStrongDelegateReferenceUsage,
|
|
@@ -733,6 +735,8 @@ GeometryReader { proxy in
|
|
|
733
735
|
Text("x").frame(width: proxy.size.width)
|
|
734
736
|
}
|
|
735
737
|
Text("Headline").fontWeight(.bold)
|
|
738
|
+
Text("State").foregroundStyle(Color.green)
|
|
739
|
+
let content: () -> Content
|
|
736
740
|
let filtered = items.filter { $0.title.contains(searchText) }
|
|
737
741
|
ForEach(items.indices, id: \\.self) { index in
|
|
738
742
|
Text(items[index].title)
|
|
@@ -779,6 +783,8 @@ MainActor.assumeIsolated { reload() }
|
|
|
779
783
|
assert.equal(hasSwiftContainsUserFilterUsage(source), true);
|
|
780
784
|
assert.equal(hasSwiftGeometryReaderUsage(source), true);
|
|
781
785
|
assert.equal(hasSwiftFontWeightBoldUsage(source), true);
|
|
786
|
+
assert.equal(hasSwiftExplicitColorStaticMemberUsage(source), true);
|
|
787
|
+
assert.equal(hasSwiftClosureBasedViewBuilderContentUsage(source), true);
|
|
782
788
|
assert.equal(hasSwiftObservableObjectUsage(source), true);
|
|
783
789
|
assert.equal(hasSwiftLegacySwiftUiObservableWrapperUsage(source), true);
|
|
784
790
|
assert.equal(hasSwiftNavigationViewUsage(source), true);
|
|
@@ -818,6 +824,8 @@ let r = "@preconcurrency import LegacyFramework"
|
|
|
818
824
|
let s = "nonisolated(unsafe) static var sharedBridge: Model?"
|
|
819
825
|
let t = "MainActor.assumeIsolated { reload() }"
|
|
820
826
|
let u = "ForEach(items.filter { $0.isVisible }) { item in }"
|
|
827
|
+
let v = "Color.green"
|
|
828
|
+
let w = "let content: () -> Content"
|
|
821
829
|
`;
|
|
822
830
|
assert.equal(hasSwiftPreconcurrencyUsage(source), false);
|
|
823
831
|
assert.equal(hasSwiftNonisolatedUnsafeUsage(source), false);
|
|
@@ -827,6 +835,8 @@ let u = "ForEach(items.filter { $0.isVisible }) { item in }"
|
|
|
827
835
|
assert.equal(hasSwiftContainsUserFilterUsage(source), false);
|
|
828
836
|
assert.equal(hasSwiftGeometryReaderUsage(source), false);
|
|
829
837
|
assert.equal(hasSwiftFontWeightBoldUsage(source), false);
|
|
838
|
+
assert.equal(hasSwiftExplicitColorStaticMemberUsage(source), false);
|
|
839
|
+
assert.equal(hasSwiftClosureBasedViewBuilderContentUsage(source), false);
|
|
830
840
|
assert.equal(hasSwiftTaskDetachedUsage(source), false);
|
|
831
841
|
assert.equal(hasSwiftNavigationViewUsage(source), false);
|
|
832
842
|
assert.equal(hasSwiftForegroundColorUsage(source), false);
|
|
@@ -844,6 +854,8 @@ let u = "ForEach(items.filter { $0.isVisible }) { item in }"
|
|
|
844
854
|
test('detectores snapshot SwiftUI ignoran reemplazos modernos', () => {
|
|
845
855
|
const source = `
|
|
846
856
|
Text("Primary").foregroundStyle(.blue)
|
|
857
|
+
Text("State").foregroundStyle(.green)
|
|
858
|
+
@ViewBuilder let content: Content
|
|
847
859
|
Image("hero").clipShape(.rect(cornerRadius: 12))
|
|
848
860
|
Text("Headline").bold()
|
|
849
861
|
TabView {
|
|
@@ -877,6 +889,8 @@ ScrollView {
|
|
|
877
889
|
assert.equal(hasSwiftContainsUserFilterUsage(source), false);
|
|
878
890
|
assert.equal(hasSwiftGeometryReaderUsage(source), false);
|
|
879
891
|
assert.equal(hasSwiftFontWeightBoldUsage(source), false);
|
|
892
|
+
assert.equal(hasSwiftExplicitColorStaticMemberUsage(source), false);
|
|
893
|
+
assert.equal(hasSwiftClosureBasedViewBuilderContentUsage(source), false);
|
|
880
894
|
assert.equal(hasSwiftForegroundColorUsage(source), false);
|
|
881
895
|
assert.equal(hasSwiftCornerRadiusUsage(source), false);
|
|
882
896
|
assert.equal(hasSwiftTabItemUsage(source), false);
|
|
@@ -885,6 +899,53 @@ ScrollView {
|
|
|
885
899
|
assert.equal(hasSwiftLegacyOnChangeUsage(source), false);
|
|
886
900
|
});
|
|
887
901
|
|
|
902
|
+
test('hasSwiftExplicitColorStaticMemberUsage detecta Color.* y preserva static member lookup', () => {
|
|
903
|
+
const source = `
|
|
904
|
+
struct StatusView: View {
|
|
905
|
+
var body: some View {
|
|
906
|
+
Text("Ready").foregroundStyle(Color.green)
|
|
907
|
+
Circle().fill(Color.primary)
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
`;
|
|
911
|
+
const safe = `
|
|
912
|
+
struct StatusView: View {
|
|
913
|
+
var body: some View {
|
|
914
|
+
Text("Ready").foregroundStyle(.green)
|
|
915
|
+
Circle().fill(.primary)
|
|
916
|
+
Rectangle().fill(Color("BrandPrimary"))
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
let ignored = "Color.green"
|
|
920
|
+
// Color.primary
|
|
921
|
+
`;
|
|
922
|
+
|
|
923
|
+
assert.equal(hasSwiftExplicitColorStaticMemberUsage(source), true);
|
|
924
|
+
assert.equal(hasSwiftExplicitColorStaticMemberUsage(safe), false);
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
test('hasSwiftClosureBasedViewBuilderContentUsage detecta content closure y preserva @ViewBuilder let content', () => {
|
|
928
|
+
const source = `
|
|
929
|
+
struct Card<Content: View>: View {
|
|
930
|
+
private let content: () -> Content
|
|
931
|
+
|
|
932
|
+
init(@ViewBuilder content: @escaping () -> Content) {
|
|
933
|
+
self.content = content
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
`;
|
|
937
|
+
const safe = `
|
|
938
|
+
struct Card<Content: View>: View {
|
|
939
|
+
@ViewBuilder let content: Content
|
|
940
|
+
}
|
|
941
|
+
let ignored = "let content: () -> Content"
|
|
942
|
+
// let content: () -> Content
|
|
943
|
+
`;
|
|
944
|
+
|
|
945
|
+
assert.equal(hasSwiftClosureBasedViewBuilderContentUsage(source), true);
|
|
946
|
+
assert.equal(hasSwiftClosureBasedViewBuilderContentUsage(safe), false);
|
|
947
|
+
});
|
|
948
|
+
|
|
888
949
|
test('hasSwiftLegacyXCTestImportUsage detecta XCTest unitario y excluye UI/performance', () => {
|
|
889
950
|
const unitTest = `
|
|
890
951
|
import XCTest
|
|
@@ -863,6 +863,20 @@ export const hasSwiftFontWeightBoldUsage = (source: string): boolean => {
|
|
|
863
863
|
return hasSwiftSanitizedRegexMatch(source, /\.\s*fontWeight\s*\(\s*\.bold\s*\)/g);
|
|
864
864
|
};
|
|
865
865
|
|
|
866
|
+
export const hasSwiftExplicitColorStaticMemberUsage = (source: string): boolean => {
|
|
867
|
+
return hasSwiftSanitizedRegexMatch(
|
|
868
|
+
source,
|
|
869
|
+
/\bColor\s*\.\s*(?:accentColor|black|blue|brown|clear|cyan|gray|green|indigo|mint|orange|pink|primary|purple|red|secondary|teal|white|yellow)\b/g
|
|
870
|
+
);
|
|
871
|
+
};
|
|
872
|
+
|
|
873
|
+
export const hasSwiftClosureBasedViewBuilderContentUsage = (source: string): boolean => {
|
|
874
|
+
return hasSwiftSanitizedRegexMatch(
|
|
875
|
+
source,
|
|
876
|
+
/\b(?:let|var)\s+content\s*:\s*(?:\(\s*\)\s*->|@\s*escaping\s*\(\s*\)\s*->)\s*(?:some\s+View|Content)\b/g
|
|
877
|
+
);
|
|
878
|
+
};
|
|
879
|
+
|
|
866
880
|
export const hasSwiftLegacySwiftUiObservableWrapperUsage = (source: string): boolean => {
|
|
867
881
|
return hasSwiftSanitizedRegexMatch(source, /@\s*(?:StateObject|ObservedObject)\b/);
|
|
868
882
|
};
|
|
@@ -682,6 +682,8 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
|
|
|
682
682
|
{ platform: 'ios', pathCheck: isIOSApplicationOrPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftContainsUserFilterUsage, ruleId: 'heuristics.ios.contains-user-filter.ast', code: 'HEURISTICS_IOS_CONTAINS_USER_FILTER_AST', message: 'AST heuristic detected contains() in a user-facing filter where localizedStandardContains() may be preferred.' },
|
|
683
683
|
{ platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftGeometryReaderUsage, ruleId: 'heuristics.ios.geometryreader.ast', code: 'HEURISTICS_IOS_GEOMETRYREADER_AST', message: 'AST heuristic detected GeometryReader usage that may be replaceable with modern layout APIs.' },
|
|
684
684
|
{ platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftFontWeightBoldUsage, ruleId: 'heuristics.ios.font-weight-bold.ast', code: 'HEURISTICS_IOS_FONT_WEIGHT_BOLD_AST', message: 'AST heuristic detected fontWeight(.bold) usage where bold() may be preferred.' },
|
|
685
|
+
{ platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftExplicitColorStaticMemberUsage, ruleId: 'heuristics.ios.swiftui.explicit-color-static-member.ast', code: 'HEURISTICS_IOS_SWIFTUI_EXPLICIT_COLOR_STATIC_MEMBER_AST', message: 'AST heuristic detected Color.* static member usage where SwiftUI static member lookup may be preferred.' },
|
|
686
|
+
{ platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftClosureBasedViewBuilderContentUsage, ruleId: 'heuristics.ios.swiftui.closure-based-viewbuilder-content.ast', code: 'HEURISTICS_IOS_SWIFTUI_CLOSURE_BASED_VIEWBUILDER_CONTENT_AST', message: 'AST heuristic detected closure-based content storage; @ViewBuilder let content: Content remains the preferred SwiftUI container baseline.' },
|
|
685
687
|
{ 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.' },
|
|
686
688
|
{ 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.' },
|
|
687
689
|
{ 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.' },
|
|
@@ -857,6 +857,42 @@ export const iosRules: RuleSet = [
|
|
|
857
857
|
code: 'HEURISTICS_IOS_FONT_WEIGHT_BOLD_AST',
|
|
858
858
|
},
|
|
859
859
|
},
|
|
860
|
+
{
|
|
861
|
+
id: 'heuristics.ios.swiftui.explicit-color-static-member.ast',
|
|
862
|
+
description: 'Detects Color.* static member usage where SwiftUI static member lookup is available.',
|
|
863
|
+
severity: 'WARN',
|
|
864
|
+
platform: 'ios',
|
|
865
|
+
locked: true,
|
|
866
|
+
when: {
|
|
867
|
+
kind: 'Heuristic',
|
|
868
|
+
where: {
|
|
869
|
+
ruleId: 'heuristics.ios.swiftui.explicit-color-static-member.ast',
|
|
870
|
+
},
|
|
871
|
+
},
|
|
872
|
+
then: {
|
|
873
|
+
kind: 'Finding',
|
|
874
|
+
message: 'AST heuristic detected Color.* static member usage where SwiftUI static member lookup may be preferred.',
|
|
875
|
+
code: 'HEURISTICS_IOS_SWIFTUI_EXPLICIT_COLOR_STATIC_MEMBER_AST',
|
|
876
|
+
},
|
|
877
|
+
},
|
|
878
|
+
{
|
|
879
|
+
id: 'heuristics.ios.swiftui.closure-based-viewbuilder-content.ast',
|
|
880
|
+
description: 'Detects closure-based SwiftUI content properties where @ViewBuilder let content: Content is preferred.',
|
|
881
|
+
severity: 'WARN',
|
|
882
|
+
platform: 'ios',
|
|
883
|
+
locked: true,
|
|
884
|
+
when: {
|
|
885
|
+
kind: 'Heuristic',
|
|
886
|
+
where: {
|
|
887
|
+
ruleId: 'heuristics.ios.swiftui.closure-based-viewbuilder-content.ast',
|
|
888
|
+
},
|
|
889
|
+
},
|
|
890
|
+
then: {
|
|
891
|
+
kind: 'Finding',
|
|
892
|
+
message: 'AST heuristic detected closure-based content storage; @ViewBuilder let content: Content remains the preferred SwiftUI container baseline.',
|
|
893
|
+
code: 'HEURISTICS_IOS_SWIFTUI_CLOSURE_BASED_VIEWBUILDER_CONTENT_AST',
|
|
894
|
+
},
|
|
895
|
+
},
|
|
860
896
|
{
|
|
861
897
|
id: 'heuristics.ios.navigation-view.ast',
|
|
862
898
|
description: 'Detects NavigationView usage in iOS production code.',
|
|
@@ -242,6 +242,14 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
|
|
|
242
242
|
'skills.ios.no-font-weight-bold': heuristicDetector('ios.font-weight-bold', [
|
|
243
243
|
'heuristics.ios.font-weight-bold.ast',
|
|
244
244
|
]),
|
|
245
|
+
'skills.ios.guideline.ios-swiftui-expert.prefer-static-member-lookup-blue-vs-color-blue':
|
|
246
|
+
heuristicDetector('ios.swiftui.explicit-color-static-member', [
|
|
247
|
+
'heuristics.ios.swiftui.explicit-color-static-member.ast',
|
|
248
|
+
]),
|
|
249
|
+
'skills.ios.guideline.ios-swiftui-expert.prefer-viewbuilder-let-content-content-over-closure-based-content-prop':
|
|
250
|
+
heuristicDetector('ios.swiftui.closure-based-viewbuilder-content', [
|
|
251
|
+
'heuristics.ios.swiftui.closure-based-viewbuilder-content.ast',
|
|
252
|
+
]),
|
|
245
253
|
'skills.ios.no-scrollview-shows-indicators': heuristicDetector(
|
|
246
254
|
'ios.scrollview-shows-indicators',
|
|
247
255
|
['heuristics.ios.scrollview-shows-indicators.ast']
|
|
@@ -339,6 +339,20 @@ const normalizeKnownRuleTarget = (
|
|
|
339
339
|
) {
|
|
340
340
|
return 'skills.ios.no-font-weight-bold';
|
|
341
341
|
}
|
|
342
|
+
if (
|
|
343
|
+
includes('static member lookup') ||
|
|
344
|
+
includes('color.blue') ||
|
|
345
|
+
(includes('.blue') && includes('color'))
|
|
346
|
+
) {
|
|
347
|
+
return 'skills.ios.guideline.ios-swiftui-expert.prefer-static-member-lookup-blue-vs-color-blue';
|
|
348
|
+
}
|
|
349
|
+
if (
|
|
350
|
+
includes('viewbuilder let content') ||
|
|
351
|
+
(includes('closure-based content') && includes('content')) ||
|
|
352
|
+
(includes('content: content') && includes('viewbuilder'))
|
|
353
|
+
) {
|
|
354
|
+
return 'skills.ios.guideline.ios-swiftui-expert.prefer-viewbuilder-let-content-content-over-closure-based-content-prop';
|
|
355
|
+
}
|
|
342
356
|
if (
|
|
343
357
|
includes('scrollindicators hidden') ||
|
|
344
358
|
includes('scroll indicators hidden') ||
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.222",
|
|
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-
|
|
4
|
+
"generatedAt": "2026-05-13T16:04:37.359Z",
|
|
5
5
|
"bundles": [
|
|
6
6
|
{
|
|
7
7
|
"name": "android-guidelines",
|
|
@@ -8632,7 +8632,7 @@
|
|
|
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": "
|
|
8635
|
+
"hash": "938525eec7fea70ecba6c27711800036b964e4b668f80b177cc918cb268e08c5",
|
|
8636
8636
|
"rules": [
|
|
8637
8637
|
{
|
|
8638
8638
|
"id": "skills.ios.guideline.ios-swiftui-expert.always-mark-state-and-stateobject-as-private-makes-dependencies-clear",
|
|
@@ -8715,7 +8715,7 @@
|
|
|
8715
8715
|
"sourcePath": "vendor/skills/swiftui-expert-skill/SKILL.md",
|
|
8716
8716
|
"confidence": "MEDIUM",
|
|
8717
8717
|
"locked": true,
|
|
8718
|
-
"evaluationMode": "
|
|
8718
|
+
"evaluationMode": "AUTO",
|
|
8719
8719
|
"origin": "core"
|
|
8720
8720
|
},
|
|
8721
8721
|
{
|
|
@@ -8727,7 +8727,7 @@
|
|
|
8727
8727
|
"sourcePath": "vendor/skills/swiftui-expert-skill/SKILL.md",
|
|
8728
8728
|
"confidence": "MEDIUM",
|
|
8729
8729
|
"locked": true,
|
|
8730
|
-
"evaluationMode": "
|
|
8730
|
+
"evaluationMode": "AUTO",
|
|
8731
8731
|
"origin": "core"
|
|
8732
8732
|
},
|
|
8733
8733
|
{
|