pumuki 6.3.222 → 6.3.224
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 +88 -0
- package/core/facts/detectors/text/ios.ts +30 -0
- package/core/facts/extractHeuristicFacts.ts +2 -0
- package/core/rules/presets/heuristics/ios.ts +37 -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
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
hasSwiftMixedTestingFrameworksUsage,
|
|
36
36
|
hasSwiftLegacyXCTestImportUsage,
|
|
37
37
|
hasSwiftModernizableXCTestSuiteUsage,
|
|
38
|
+
hasSwiftNonLazyScrollForEachUsage,
|
|
38
39
|
hasSwiftAssumeIsolatedUsage,
|
|
39
40
|
hasSwiftCoreDataLayerLeakUsage,
|
|
40
41
|
hasSwiftSwiftDataLayerLeakUsage,
|
|
@@ -63,6 +64,7 @@ import {
|
|
|
63
64
|
hasSwiftJSONSerializationUsage,
|
|
64
65
|
hasSwiftExplicitColorStaticMemberUsage,
|
|
65
66
|
hasSwiftClosureBasedViewBuilderContentUsage,
|
|
67
|
+
hasSwiftRedundantReactiveStateAssignmentUsage,
|
|
66
68
|
hasSwiftInlineForEachTransformUsage,
|
|
67
69
|
hasSwiftStringFormatUsage,
|
|
68
70
|
hasSwiftStrongDelegateReferenceUsage,
|
|
@@ -122,6 +124,44 @@ test('hasSwiftAnyViewUsage ignora comentarios, strings y coincidencias parciales
|
|
|
122
124
|
assert.equal(hasSwiftAnyViewUsage(source), false);
|
|
123
125
|
});
|
|
124
126
|
|
|
127
|
+
test('hasSwiftNonLazyScrollForEachUsage detecta ScrollView con stack no lazy y preserva LazyVStack', () => {
|
|
128
|
+
const source = `
|
|
129
|
+
struct FeedView: View {
|
|
130
|
+
let items: [Item]
|
|
131
|
+
|
|
132
|
+
var body: some View {
|
|
133
|
+
ScrollView {
|
|
134
|
+
VStack(spacing: 12) {
|
|
135
|
+
ForEach(items) { item in
|
|
136
|
+
FeedRow(item: item)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
`;
|
|
143
|
+
const safe = `
|
|
144
|
+
struct FeedView: View {
|
|
145
|
+
let items: [Item]
|
|
146
|
+
|
|
147
|
+
var body: some View {
|
|
148
|
+
ScrollView {
|
|
149
|
+
LazyVStack(spacing: 12) {
|
|
150
|
+
ForEach(items) { item in
|
|
151
|
+
FeedRow(item: item)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
let sample = "ScrollView { VStack { ForEach(items) } }"
|
|
156
|
+
// ScrollView { VStack { ForEach(items) } }
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
`;
|
|
160
|
+
|
|
161
|
+
assert.equal(hasSwiftNonLazyScrollForEachUsage(source), true);
|
|
162
|
+
assert.equal(hasSwiftNonLazyScrollForEachUsage(safe), false);
|
|
163
|
+
});
|
|
164
|
+
|
|
125
165
|
test('hasSwiftForceTryUsage detecta try! y descarta try?', () => {
|
|
126
166
|
const positive = `
|
|
127
167
|
func load() {
|
|
@@ -737,6 +777,9 @@ GeometryReader { proxy in
|
|
|
737
777
|
Text("Headline").fontWeight(.bold)
|
|
738
778
|
Text("State").foregroundStyle(Color.green)
|
|
739
779
|
let content: () -> Content
|
|
780
|
+
.onChange(of: query) { newValue in
|
|
781
|
+
query = newValue
|
|
782
|
+
}
|
|
740
783
|
let filtered = items.filter { $0.title.contains(searchText) }
|
|
741
784
|
ForEach(items.indices, id: \\.self) { index in
|
|
742
785
|
Text(items[index].title)
|
|
@@ -785,6 +828,7 @@ MainActor.assumeIsolated { reload() }
|
|
|
785
828
|
assert.equal(hasSwiftFontWeightBoldUsage(source), true);
|
|
786
829
|
assert.equal(hasSwiftExplicitColorStaticMemberUsage(source), true);
|
|
787
830
|
assert.equal(hasSwiftClosureBasedViewBuilderContentUsage(source), true);
|
|
831
|
+
assert.equal(hasSwiftRedundantReactiveStateAssignmentUsage(source), true);
|
|
788
832
|
assert.equal(hasSwiftObservableObjectUsage(source), true);
|
|
789
833
|
assert.equal(hasSwiftLegacySwiftUiObservableWrapperUsage(source), true);
|
|
790
834
|
assert.equal(hasSwiftNavigationViewUsage(source), true);
|
|
@@ -826,6 +870,7 @@ let t = "MainActor.assumeIsolated { reload() }"
|
|
|
826
870
|
let u = "ForEach(items.filter { $0.isVisible }) { item in }"
|
|
827
871
|
let v = "Color.green"
|
|
828
872
|
let w = "let content: () -> Content"
|
|
873
|
+
let x = ".onChange(of: query) { newValue in query = newValue }"
|
|
829
874
|
`;
|
|
830
875
|
assert.equal(hasSwiftPreconcurrencyUsage(source), false);
|
|
831
876
|
assert.equal(hasSwiftNonisolatedUnsafeUsage(source), false);
|
|
@@ -837,6 +882,7 @@ let w = "let content: () -> Content"
|
|
|
837
882
|
assert.equal(hasSwiftFontWeightBoldUsage(source), false);
|
|
838
883
|
assert.equal(hasSwiftExplicitColorStaticMemberUsage(source), false);
|
|
839
884
|
assert.equal(hasSwiftClosureBasedViewBuilderContentUsage(source), false);
|
|
885
|
+
assert.equal(hasSwiftRedundantReactiveStateAssignmentUsage(source), false);
|
|
840
886
|
assert.equal(hasSwiftTaskDetachedUsage(source), false);
|
|
841
887
|
assert.equal(hasSwiftNavigationViewUsage(source), false);
|
|
842
888
|
assert.equal(hasSwiftForegroundColorUsage(source), false);
|
|
@@ -891,6 +937,7 @@ ScrollView {
|
|
|
891
937
|
assert.equal(hasSwiftFontWeightBoldUsage(source), false);
|
|
892
938
|
assert.equal(hasSwiftExplicitColorStaticMemberUsage(source), false);
|
|
893
939
|
assert.equal(hasSwiftClosureBasedViewBuilderContentUsage(source), false);
|
|
940
|
+
assert.equal(hasSwiftRedundantReactiveStateAssignmentUsage(source), false);
|
|
894
941
|
assert.equal(hasSwiftForegroundColorUsage(source), false);
|
|
895
942
|
assert.equal(hasSwiftCornerRadiusUsage(source), false);
|
|
896
943
|
assert.equal(hasSwiftTabItemUsage(source), false);
|
|
@@ -946,6 +993,47 @@ let ignored = "let content: () -> Content"
|
|
|
946
993
|
assert.equal(hasSwiftClosureBasedViewBuilderContentUsage(safe), false);
|
|
947
994
|
});
|
|
948
995
|
|
|
996
|
+
test('hasSwiftRedundantReactiveStateAssignmentUsage detecta asignaciones reactivas redundantes y preserva guard de cambio', () => {
|
|
997
|
+
const source = `
|
|
998
|
+
struct SearchView: View {
|
|
999
|
+
@State private var query = ""
|
|
1000
|
+
|
|
1001
|
+
var body: some View {
|
|
1002
|
+
TextField("Search", text: $query)
|
|
1003
|
+
.onChange(of: query) { newValue in
|
|
1004
|
+
query = newValue
|
|
1005
|
+
}
|
|
1006
|
+
.onReceive(model.$value) { value in
|
|
1007
|
+
self.query = value
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
`;
|
|
1012
|
+
const safe = `
|
|
1013
|
+
struct SearchView: View {
|
|
1014
|
+
@State private var query = ""
|
|
1015
|
+
|
|
1016
|
+
var body: some View {
|
|
1017
|
+
TextField("Search", text: $query)
|
|
1018
|
+
.onChange(of: query) { newValue in
|
|
1019
|
+
if query != newValue {
|
|
1020
|
+
query = newValue
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
.onReceive(model.$value) { value in
|
|
1024
|
+
guard self.query != value else { return }
|
|
1025
|
+
self.query = value
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
let ignored = ".onChange(of: query) { newValue in query = newValue }"
|
|
1030
|
+
// .onReceive(model.$value) { value in query = value }
|
|
1031
|
+
`;
|
|
1032
|
+
|
|
1033
|
+
assert.equal(hasSwiftRedundantReactiveStateAssignmentUsage(source), true);
|
|
1034
|
+
assert.equal(hasSwiftRedundantReactiveStateAssignmentUsage(safe), false);
|
|
1035
|
+
});
|
|
1036
|
+
|
|
949
1037
|
test('hasSwiftLegacyXCTestImportUsage detecta XCTest unitario y excluye UI/performance', () => {
|
|
950
1038
|
const unitTest = `
|
|
951
1039
|
import XCTest
|
|
@@ -380,6 +380,14 @@ export const hasSwiftAnyViewUsage = (source: string): boolean => {
|
|
|
380
380
|
});
|
|
381
381
|
};
|
|
382
382
|
|
|
383
|
+
export const hasSwiftNonLazyScrollForEachUsage = (source: string): boolean => {
|
|
384
|
+
const swiftSource = sanitizeSwiftSourceForMultilineRegex(source);
|
|
385
|
+
const nonLazyScrollableCollectionPattern =
|
|
386
|
+
/\bScrollView\s*(?:\([^)]*\))?\s*\{[\s\S]{0,2000}\b(?:VStack|HStack)\s*(?:\([^)]*\))?\s*\{[\s\S]{0,1200}\bForEach\s*\(/;
|
|
387
|
+
|
|
388
|
+
return nonLazyScrollableCollectionPattern.test(swiftSource);
|
|
389
|
+
};
|
|
390
|
+
|
|
383
391
|
export const hasSwiftDispatchQueueUsage = (source: string): boolean => {
|
|
384
392
|
return scanCodeLikeSource(source, ({ source: swiftSource, index, current }) => {
|
|
385
393
|
if (current !== 'D' || !hasIdentifierAt(swiftSource, index, 'DispatchQueue')) {
|
|
@@ -877,6 +885,28 @@ export const hasSwiftClosureBasedViewBuilderContentUsage = (source: string): boo
|
|
|
877
885
|
);
|
|
878
886
|
};
|
|
879
887
|
|
|
888
|
+
export const hasSwiftRedundantReactiveStateAssignmentUsage = (source: string): boolean => {
|
|
889
|
+
const sanitized = sanitizeSwiftSourceForMultilineRegex(source);
|
|
890
|
+
const reactiveAssignmentPattern =
|
|
891
|
+
/\.(?:onChange|onReceive)\s*\([^)]*\)\s*\{[\s\S]{0,500}?\b(?:self\s*\.\s*)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(?:newValue|value|output|receivedValue)\b/g;
|
|
892
|
+
|
|
893
|
+
for (const match of sanitized.matchAll(reactiveAssignmentPattern)) {
|
|
894
|
+
const target = match[1];
|
|
895
|
+
const segment = match[0] ?? '';
|
|
896
|
+
if (!target) {
|
|
897
|
+
continue;
|
|
898
|
+
}
|
|
899
|
+
const guardedAssignmentPattern = new RegExp(
|
|
900
|
+
`\\b(?:if|guard)\\s+(?:self\\s*\\.\\s*)?${target}\\s*!=\\s*(?:newValue|value|output|receivedValue)\\b`
|
|
901
|
+
);
|
|
902
|
+
if (!guardedAssignmentPattern.test(segment)) {
|
|
903
|
+
return true;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
return false;
|
|
908
|
+
};
|
|
909
|
+
|
|
880
910
|
export const hasSwiftLegacySwiftUiObservableWrapperUsage = (source: string): boolean => {
|
|
881
911
|
return hasSwiftSanitizedRegexMatch(source, /@\s*(?:StateObject|ObservedObject)\b/);
|
|
882
912
|
};
|
|
@@ -684,6 +684,8 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
|
|
|
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
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
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.' },
|
|
687
|
+
{ platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftRedundantReactiveStateAssignmentUsage, ruleId: 'heuristics.ios.swiftui.redundant-reactive-state-assignment.ast', code: 'HEURISTICS_IOS_SWIFTUI_REDUNDANT_REACTIVE_STATE_ASSIGNMENT_AST', message: 'AST heuristic detected reactive state assignment without a value-change guard; check for value changes before assigning state in hot paths.' },
|
|
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.' },
|
|
687
689
|
{ 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.' },
|
|
688
690
|
{ 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.' },
|
|
689
691
|
{ 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.' },
|
|
@@ -893,6 +893,43 @@ export const iosRules: RuleSet = [
|
|
|
893
893
|
code: 'HEURISTICS_IOS_SWIFTUI_CLOSURE_BASED_VIEWBUILDER_CONTENT_AST',
|
|
894
894
|
},
|
|
895
895
|
},
|
|
896
|
+
{
|
|
897
|
+
id: 'heuristics.ios.swiftui.redundant-reactive-state-assignment.ast',
|
|
898
|
+
description: 'Detects onChange/onReceive state assignments without a value-change guard.',
|
|
899
|
+
severity: 'WARN',
|
|
900
|
+
platform: 'ios',
|
|
901
|
+
locked: true,
|
|
902
|
+
when: {
|
|
903
|
+
kind: 'Heuristic',
|
|
904
|
+
where: {
|
|
905
|
+
ruleId: 'heuristics.ios.swiftui.redundant-reactive-state-assignment.ast',
|
|
906
|
+
},
|
|
907
|
+
},
|
|
908
|
+
then: {
|
|
909
|
+
kind: 'Finding',
|
|
910
|
+
message: 'AST heuristic detected reactive state assignment without a value-change guard; check for value changes before assigning state in hot paths.',
|
|
911
|
+
code: 'HEURISTICS_IOS_SWIFTUI_REDUNDANT_REACTIVE_STATE_ASSIGNMENT_AST',
|
|
912
|
+
},
|
|
913
|
+
},
|
|
914
|
+
{
|
|
915
|
+
id: 'heuristics.ios.swiftui.non-lazy-scroll-foreach.ast',
|
|
916
|
+
description: 'Detects ScrollView content backed by non-lazy stacks and ForEach.',
|
|
917
|
+
severity: 'WARN',
|
|
918
|
+
platform: 'ios',
|
|
919
|
+
locked: true,
|
|
920
|
+
when: {
|
|
921
|
+
kind: 'Heuristic',
|
|
922
|
+
where: {
|
|
923
|
+
ruleId: 'heuristics.ios.swiftui.non-lazy-scroll-foreach.ast',
|
|
924
|
+
},
|
|
925
|
+
},
|
|
926
|
+
then: {
|
|
927
|
+
kind: 'Finding',
|
|
928
|
+
message:
|
|
929
|
+
'AST heuristic detected ScrollView with a non-lazy stack feeding ForEach; LazyVStack/LazyHStack remain the preferred baseline for large scrollable collections.',
|
|
930
|
+
code: 'HEURISTICS_IOS_SWIFTUI_NON_LAZY_SCROLL_FOREACH_AST',
|
|
931
|
+
},
|
|
932
|
+
},
|
|
896
933
|
{
|
|
897
934
|
id: 'heuristics.ios.navigation-view.ast',
|
|
898
935
|
description: 'Detects NavigationView usage in iOS production code.',
|
|
@@ -250,6 +250,14 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
|
|
|
250
250
|
heuristicDetector('ios.swiftui.closure-based-viewbuilder-content', [
|
|
251
251
|
'heuristics.ios.swiftui.closure-based-viewbuilder-content.ast',
|
|
252
252
|
]),
|
|
253
|
+
'skills.ios.guideline.ios-swiftui-expert.avoid-redundant-state-updates-in-onreceive-onchange-scroll-handlers':
|
|
254
|
+
heuristicDetector('ios.swiftui.redundant-reactive-state-assignment', [
|
|
255
|
+
'heuristics.ios.swiftui.redundant-reactive-state-assignment.ast',
|
|
256
|
+
]),
|
|
257
|
+
'skills.ios.guideline.ios-swiftui-expert.use-lazyvstack-lazyhstack-for-large-lists':
|
|
258
|
+
heuristicDetector('ios.swiftui.non-lazy-scroll-foreach', [
|
|
259
|
+
'heuristics.ios.swiftui.non-lazy-scroll-foreach.ast',
|
|
260
|
+
]),
|
|
253
261
|
'skills.ios.no-scrollview-shows-indicators': heuristicDetector(
|
|
254
262
|
'ios.scrollview-shows-indicators',
|
|
255
263
|
['heuristics.ios.scrollview-shows-indicators.ast']
|
|
@@ -353,6 +353,20 @@ const normalizeKnownRuleTarget = (
|
|
|
353
353
|
) {
|
|
354
354
|
return 'skills.ios.guideline.ios-swiftui-expert.prefer-viewbuilder-let-content-content-over-closure-based-content-prop';
|
|
355
355
|
}
|
|
356
|
+
if (
|
|
357
|
+
includes('redundant state updates') ||
|
|
358
|
+
(includes('onreceive') && includes('onchange') && includes('state updates')) ||
|
|
359
|
+
(includes('check for value changes') && includes('assigning state'))
|
|
360
|
+
) {
|
|
361
|
+
return 'skills.ios.guideline.ios-swiftui-expert.avoid-redundant-state-updates-in-onreceive-onchange-scroll-handlers';
|
|
362
|
+
}
|
|
363
|
+
if (
|
|
364
|
+
(includes('lazyvstack') && includes('lazyhstack') && includes('large lists')) ||
|
|
365
|
+
(includes('lazyvstack') && includes('foreach') && includes('scrollview')) ||
|
|
366
|
+
(includes('lazyhstack') && includes('foreach') && includes('scrollview'))
|
|
367
|
+
) {
|
|
368
|
+
return 'skills.ios.guideline.ios-swiftui-expert.use-lazyvstack-lazyhstack-for-large-lists';
|
|
369
|
+
}
|
|
356
370
|
if (
|
|
357
371
|
includes('scrollindicators hidden') ||
|
|
358
372
|
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.224",
|
|
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:
|
|
4
|
+
"generatedAt": "2026-05-13T16:15:52.668Z",
|
|
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": "2acbd66e86e9e809f0c4bdb5d215618096f5773e359085e3a4dc707b83c1ebb1",
|
|
8636
8636
|
"rules": [
|
|
8637
8637
|
{
|
|
8638
8638
|
"id": "skills.ios.guideline.ios-swiftui-expert.always-mark-state-and-stateobject-as-private-makes-dependencies-clear",
|
|
@@ -8667,7 +8667,7 @@
|
|
|
8667
8667
|
"sourcePath": "vendor/skills/swiftui-expert-skill/SKILL.md",
|
|
8668
8668
|
"confidence": "MEDIUM",
|
|
8669
8669
|
"locked": true,
|
|
8670
|
-
"evaluationMode": "
|
|
8670
|
+
"evaluationMode": "AUTO",
|
|
8671
8671
|
"origin": "core"
|
|
8672
8672
|
},
|
|
8673
8673
|
{
|
|
@@ -8787,7 +8787,7 @@
|
|
|
8787
8787
|
"sourcePath": "vendor/skills/swiftui-expert-skill/SKILL.md",
|
|
8788
8788
|
"confidence": "MEDIUM",
|
|
8789
8789
|
"locked": true,
|
|
8790
|
-
"evaluationMode": "
|
|
8790
|
+
"evaluationMode": "AUTO",
|
|
8791
8791
|
"origin": "core"
|
|
8792
8792
|
},
|
|
8793
8793
|
{
|