pumuki 6.3.211 → 6.3.213
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 +33 -0
- package/core/facts/detectors/text/ios.ts +7 -0
- package/core/facts/extractHeuristicFacts.ts +1 -0
- package/core/rules/presets/heuristics/ios.ts +19 -0
- package/integrations/config/skillsDetectorRegistry.ts +4 -0
- package/integrations/config/skillsMarkdownRules.ts +22 -1
- package/package.json +1 -1
- package/skills.lock.json +4 -4
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
hasSwiftLegacySwiftUiObservableWrapperUsage,
|
|
31
31
|
hasSwiftMainThreadBlockingSleepUsage,
|
|
32
32
|
hasSwiftMassiveViewControllerResponsibilityUsage,
|
|
33
|
+
hasSwiftMagicNumberLayoutUsage,
|
|
33
34
|
hasSwiftMixedTestingFrameworksUsage,
|
|
34
35
|
hasSwiftLegacyXCTestImportUsage,
|
|
35
36
|
hasSwiftModernizableXCTestSuiteUsage,
|
|
@@ -339,6 +340,38 @@ final class CheckoutViewController: UIViewController {
|
|
|
339
340
|
assert.equal(hasSwiftNonIBOutletImplicitlyUnwrappedOptionalUsage(ignored), false);
|
|
340
341
|
});
|
|
341
342
|
|
|
343
|
+
test('hasSwiftMagicNumberLayoutUsage detecta numeros magicos de layout SwiftUI', () => {
|
|
344
|
+
const source = `
|
|
345
|
+
struct ProfileView: View {
|
|
346
|
+
var body: some View {
|
|
347
|
+
VStack(spacing: 12) {
|
|
348
|
+
Text("Profile")
|
|
349
|
+
.padding(16)
|
|
350
|
+
.frame(width: 320, height: 44)
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
`;
|
|
355
|
+
const constants = `
|
|
356
|
+
struct ProfileView: View {
|
|
357
|
+
private enum Metrics {
|
|
358
|
+
static let spacing: CGFloat = 12
|
|
359
|
+
static let cardPadding: CGFloat = 16
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
var body: some View {
|
|
363
|
+
VStack(spacing: Metrics.spacing) {
|
|
364
|
+
Text("Profile")
|
|
365
|
+
.padding(Metrics.cardPadding)
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
`;
|
|
370
|
+
|
|
371
|
+
assert.equal(hasSwiftMagicNumberLayoutUsage(source), true);
|
|
372
|
+
assert.equal(hasSwiftMagicNumberLayoutUsage(constants), false);
|
|
373
|
+
});
|
|
374
|
+
|
|
342
375
|
test('detectores de logging iOS detectan logs ad-hoc y PII en produccion', () => {
|
|
343
376
|
const adHoc = `
|
|
344
377
|
print(user.id)
|
|
@@ -559,6 +559,13 @@ export const hasSwiftNonIBOutletImplicitlyUnwrappedOptionalUsage = (source: stri
|
|
|
559
559
|
});
|
|
560
560
|
};
|
|
561
561
|
|
|
562
|
+
export const hasSwiftMagicNumberLayoutUsage = (source: string): boolean => {
|
|
563
|
+
const swiftUiLayoutNumberPattern =
|
|
564
|
+
/(?:\b(?:VStack|HStack|ZStack|LazyVStack|LazyHStack)\s*\([^)]*\bspacing\s*:\s*|\.(?:padding|frame|offset|position|shadow|blur)\s*\([^)]*(?:\b(?:width|height|spacing|radius|x|y)\s*:\s*)?)\b(?:[3-9]|[1-9][0-9]+)(?:\.[0-9]+)?\b/;
|
|
565
|
+
|
|
566
|
+
return collectSwiftRegexLines(source, swiftUiLayoutNumberPattern).length > 0;
|
|
567
|
+
};
|
|
568
|
+
|
|
562
569
|
export const hasSwiftAdHocLoggingUsage = (source: string): boolean => {
|
|
563
570
|
return collectSwiftRegexLines(
|
|
564
571
|
source,
|
|
@@ -651,6 +651,7 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
|
|
|
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
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
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.' },
|
|
654
|
+
{ platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftMagicNumberLayoutUsage, ruleId: 'heuristics.ios.maintainability.magic-number-layout.ast', code: 'HEURISTICS_IOS_MAINTAINABILITY_MAGIC_NUMBER_LAYOUT_AST', message: 'AST heuristic detected SwiftUI layout magic numbers; named constants or design tokens remain the preferred baseline.' },
|
|
654
655
|
{ 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.' },
|
|
655
656
|
{ 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.' },
|
|
656
657
|
{ 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.' },
|
|
@@ -257,6 +257,25 @@ export const iosRules: RuleSet = [
|
|
|
257
257
|
code: 'HEURISTICS_IOS_ARCHITECTURE_MASSIVE_VIEW_CONTROLLER_AST',
|
|
258
258
|
},
|
|
259
259
|
},
|
|
260
|
+
{
|
|
261
|
+
id: 'heuristics.ios.maintainability.magic-number-layout.ast',
|
|
262
|
+
description: 'Detects SwiftUI layout magic numbers in presentation code.',
|
|
263
|
+
severity: 'WARN',
|
|
264
|
+
platform: 'ios',
|
|
265
|
+
locked: true,
|
|
266
|
+
when: {
|
|
267
|
+
kind: 'Heuristic',
|
|
268
|
+
where: {
|
|
269
|
+
ruleId: 'heuristics.ios.maintainability.magic-number-layout.ast',
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
then: {
|
|
273
|
+
kind: 'Finding',
|
|
274
|
+
message:
|
|
275
|
+
'AST heuristic detected SwiftUI layout magic numbers; named constants or design tokens remain the preferred baseline.',
|
|
276
|
+
code: 'HEURISTICS_IOS_MAINTAINABILITY_MAGIC_NUMBER_LAYOUT_AST',
|
|
277
|
+
},
|
|
278
|
+
},
|
|
260
279
|
{
|
|
261
280
|
id: 'heuristics.ios.safety.non-iboutlet-iuo.ast',
|
|
262
281
|
description: 'Detects implicitly unwrapped optionals outside IBOutlet wiring.',
|
|
@@ -82,6 +82,10 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
|
|
|
82
82
|
'ios.architecture.custom-singleton',
|
|
83
83
|
['heuristics.ios.architecture.custom-singleton.ast']
|
|
84
84
|
),
|
|
85
|
+
'skills.ios.guideline.ios.magic-numbers-usar-constantes-con-nombres': heuristicDetector(
|
|
86
|
+
'ios.maintainability.magic-number-layout',
|
|
87
|
+
['heuristics.ios.maintainability.magic-number-layout.ast']
|
|
88
|
+
),
|
|
85
89
|
'skills.ios.guideline.ios.prohibido-print-y-logs-ad-hoc': heuristicDetector(
|
|
86
90
|
'ios.logging.adhoc-print',
|
|
87
91
|
['heuristics.ios.logging.adhoc-print.ast']
|
|
@@ -108,6 +108,17 @@ const resolveDefaultStageForKnownRule = (
|
|
|
108
108
|
return KNOWN_RULE_DEFAULT_STAGE[ruleId];
|
|
109
109
|
};
|
|
110
110
|
|
|
111
|
+
const KNOWN_RULE_DEFAULT_SEVERITY: Readonly<Record<string, Severity>> = {
|
|
112
|
+
'skills.ios.guideline.ios.magic-numbers-usar-constantes-con-nombres': 'WARN',
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const resolveDefaultSeverityForKnownRule = (
|
|
116
|
+
ruleId: string,
|
|
117
|
+
inferredSeverity: Severity
|
|
118
|
+
): Severity => {
|
|
119
|
+
return KNOWN_RULE_DEFAULT_SEVERITY[ruleId] ?? inferredSeverity;
|
|
120
|
+
};
|
|
121
|
+
|
|
111
122
|
const isRuleCandidateLine = (line: string): boolean => {
|
|
112
123
|
if (CHECK_RULE_PREFIX.test(line)) {
|
|
113
124
|
return true;
|
|
@@ -461,6 +472,14 @@ const normalizeKnownRuleTarget = (
|
|
|
461
472
|
) {
|
|
462
473
|
return 'skills.ios.guideline.ios.implicitly-unwrapped-solo-para-iboutlets-y-casos-muy-especi-ficos';
|
|
463
474
|
}
|
|
475
|
+
if (
|
|
476
|
+
includes('magic numbers') ||
|
|
477
|
+
includes('magic number') ||
|
|
478
|
+
includes('constantes con nombres') ||
|
|
479
|
+
includes('named constants')
|
|
480
|
+
) {
|
|
481
|
+
return 'skills.ios.guideline.ios.magic-numbers-usar-constantes-con-nombres';
|
|
482
|
+
}
|
|
464
483
|
if (
|
|
465
484
|
includes('mixing legacy xctest style') ||
|
|
466
485
|
includes('mixed xctest and swift testing') ||
|
|
@@ -636,7 +655,9 @@ export const extractCompiledRulesFromSkillMarkdown = (params: {
|
|
|
636
655
|
rules.push({
|
|
637
656
|
id: nextId,
|
|
638
657
|
description,
|
|
639
|
-
severity:
|
|
658
|
+
severity: knownRuleId
|
|
659
|
+
? resolveDefaultSeverityForKnownRule(knownRuleId, inferRuleSeverity(rawLine))
|
|
660
|
+
: inferRuleSeverity(rawLine),
|
|
640
661
|
platform,
|
|
641
662
|
sourceSkill: params.sourceSkill,
|
|
642
663
|
sourcePath: params.sourcePath,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.213",
|
|
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-13T13:08:40.281Z",
|
|
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": "
|
|
5767
|
+
"hash": "8ed400269188d9b92c2a95b660bb2b69bf2992197bbe7d2fc382920444507aeb",
|
|
5768
5768
|
"rules": [
|
|
5769
5769
|
{
|
|
5770
5770
|
"id": "skills.ios.guideline.ios.accessibility-identifiers-para-localizar-elementos",
|
|
@@ -6910,13 +6910,13 @@
|
|
|
6910
6910
|
{
|
|
6911
6911
|
"id": "skills.ios.guideline.ios.magic-numbers-usar-constantes-con-nombres",
|
|
6912
6912
|
"description": "Magic numbers - Usar constantes con nombres",
|
|
6913
|
-
"severity": "
|
|
6913
|
+
"severity": "WARN",
|
|
6914
6914
|
"platform": "ios",
|
|
6915
6915
|
"sourceSkill": "ios-guidelines",
|
|
6916
6916
|
"sourcePath": "vendor/skills/ios-enterprise-rules/SKILL.md",
|
|
6917
6917
|
"confidence": "HIGH",
|
|
6918
6918
|
"locked": true,
|
|
6919
|
-
"evaluationMode": "
|
|
6919
|
+
"evaluationMode": "AUTO",
|
|
6920
6920
|
"origin": "core"
|
|
6921
6921
|
},
|
|
6922
6922
|
{
|