pumuki 6.3.213 → 6.3.215
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 +65 -0
- package/core/facts/detectors/text/ios.ts +14 -0
- package/core/facts/extractHeuristicFacts.ts +2 -0
- package/core/rules/presets/heuristics/ios.ts +38 -0
- package/integrations/config/skillsDetectorRegistry.ts +8 -0
- package/integrations/config/skillsMarkdownRules.ts +6 -0
- package/package.json +1 -1
- package/skills.lock.json +4 -4
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
hasSwiftPassedValueStateWrapperUsage,
|
|
52
52
|
hasSwiftPhysicalTextAlignmentUsage,
|
|
53
53
|
hasSwiftPreconcurrencyUsage,
|
|
54
|
+
hasSwiftQuickNimbleUsage,
|
|
54
55
|
hasSwiftSheetIsPresentedUsage,
|
|
55
56
|
hasSwiftScrollViewShowsIndicatorsUsage,
|
|
56
57
|
hasSwiftSensitiveLoggingUsage,
|
|
@@ -60,6 +61,7 @@ import {
|
|
|
60
61
|
hasSwiftStringFormatUsage,
|
|
61
62
|
hasSwiftStrongDelegateReferenceUsage,
|
|
62
63
|
hasSwiftStrongSelfEscapingClosureUsage,
|
|
64
|
+
hasSwiftSwinjectUsage,
|
|
63
65
|
hasSwiftTabItemUsage,
|
|
64
66
|
hasSwiftTaskDetachedUsage,
|
|
65
67
|
hasSwiftWaitForExpectationsUsage,
|
|
@@ -286,6 +288,34 @@ final class APIClient {
|
|
|
286
288
|
assert.equal(hasSwiftCustomSingletonUsage(ignored), false);
|
|
287
289
|
});
|
|
288
290
|
|
|
291
|
+
test('hasSwiftSwinjectUsage detecta DI de terceros y preserva DI nativa', () => {
|
|
292
|
+
const source = `
|
|
293
|
+
import Swinject
|
|
294
|
+
|
|
295
|
+
final class AppAssembly {
|
|
296
|
+
private let container = Container()
|
|
297
|
+
private let assembler = Assembler([])
|
|
298
|
+
}
|
|
299
|
+
`;
|
|
300
|
+
const native = `
|
|
301
|
+
struct AppDependencies {
|
|
302
|
+
let apiClient: APIClient
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
private struct DependenciesKey: EnvironmentKey {
|
|
306
|
+
static let defaultValue = AppDependencies(apiClient: URLSessionAPIClient())
|
|
307
|
+
}
|
|
308
|
+
`;
|
|
309
|
+
const ignored = `
|
|
310
|
+
let text = "import Swinject"
|
|
311
|
+
// let container = Container()
|
|
312
|
+
`;
|
|
313
|
+
|
|
314
|
+
assert.equal(hasSwiftSwinjectUsage(source), true);
|
|
315
|
+
assert.equal(hasSwiftSwinjectUsage(native), false);
|
|
316
|
+
assert.equal(hasSwiftSwinjectUsage(ignored), false);
|
|
317
|
+
});
|
|
318
|
+
|
|
289
319
|
test('hasSwiftMassiveViewControllerResponsibilityUsage detecta ViewControllers con acceso directo a infraestructura', () => {
|
|
290
320
|
const source = `
|
|
291
321
|
final class CheckoutViewController: UIViewController {
|
|
@@ -940,6 +970,41 @@ struct LoginModernTests {
|
|
|
940
970
|
assert.equal(hasSwiftMixedTestingFrameworksUsage(modernOnly), false);
|
|
941
971
|
});
|
|
942
972
|
|
|
973
|
+
test('hasSwiftQuickNimbleUsage detecta Quick y Nimble en tests Swift', () => {
|
|
974
|
+
const quickSpec = `
|
|
975
|
+
import Quick
|
|
976
|
+
import Nimble
|
|
977
|
+
|
|
978
|
+
final class CheckoutSpec: QuickSpec {
|
|
979
|
+
override class func spec() {
|
|
980
|
+
describe("checkout") {
|
|
981
|
+
it("loads") {
|
|
982
|
+
expect(true).to(beTrue())
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
`;
|
|
988
|
+
const nativeSwiftTesting = `
|
|
989
|
+
import Testing
|
|
990
|
+
|
|
991
|
+
@Suite
|
|
992
|
+
struct CheckoutTests {
|
|
993
|
+
@Test func loads() {
|
|
994
|
+
#expect(true)
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
`;
|
|
998
|
+
const ignored = `
|
|
999
|
+
let text = "import Quick"
|
|
1000
|
+
// import Nimble
|
|
1001
|
+
`;
|
|
1002
|
+
|
|
1003
|
+
assert.equal(hasSwiftQuickNimbleUsage(quickSpec), true);
|
|
1004
|
+
assert.equal(hasSwiftQuickNimbleUsage(nativeSwiftTesting), false);
|
|
1005
|
+
assert.equal(hasSwiftQuickNimbleUsage(ignored), false);
|
|
1006
|
+
});
|
|
1007
|
+
|
|
943
1008
|
test('hasSwiftXCTestAssertionUsage detecta XCTAssert y XCTFail reales', () => {
|
|
944
1009
|
const source = `
|
|
945
1010
|
XCTAssertEqual(value, expected)
|
|
@@ -528,6 +528,13 @@ export const hasSwiftCustomSingletonUsage = (source: string): boolean => {
|
|
|
528
528
|
});
|
|
529
529
|
};
|
|
530
530
|
|
|
531
|
+
export const hasSwiftSwinjectUsage = (source: string): boolean => {
|
|
532
|
+
return hasSwiftSanitizedRegexMatch(
|
|
533
|
+
source,
|
|
534
|
+
/\bimport\s+Swinject\b|\b(?:Container|Assembler)\s*\(/
|
|
535
|
+
);
|
|
536
|
+
};
|
|
537
|
+
|
|
531
538
|
export const hasSwiftMassiveViewControllerResponsibilityUsage = (source: string): boolean => {
|
|
532
539
|
const sanitized = sanitizeSwiftSourceForMultilineRegex(source);
|
|
533
540
|
const viewControllerPattern =
|
|
@@ -1050,6 +1057,13 @@ export const hasSwiftMixedTestingFrameworksUsage = (source: string): boolean =>
|
|
|
1050
1057
|
return hasSwiftTestingImportUsage(source) || hasSwiftTestingSuiteAttributeUsage(source);
|
|
1051
1058
|
};
|
|
1052
1059
|
|
|
1060
|
+
export const hasSwiftQuickNimbleUsage = (source: string): boolean => {
|
|
1061
|
+
return hasSwiftSanitizedRegexMatch(
|
|
1062
|
+
source,
|
|
1063
|
+
/\bimport\s+(?:Quick|Nimble)\b|\bclass\s+[A-Za-z_][A-Za-z0-9_]*\s*:\s*QuickSpec\b/
|
|
1064
|
+
);
|
|
1065
|
+
};
|
|
1066
|
+
|
|
1053
1067
|
export const hasSwiftXCTestAssertionUsage = (source: string): boolean => {
|
|
1054
1068
|
return (
|
|
1055
1069
|
collectSwiftRegexLines(source, /\bXCTAssert[A-Za-z0-9_]*\s*\(/).length > 0 ||
|
|
@@ -649,6 +649,7 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
|
|
|
649
649
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftStrongDelegateReferenceUsage, ruleId: 'heuristics.ios.memory.strong-delegate.ast', code: 'HEURISTICS_IOS_MEMORY_STRONG_DELEGATE_AST', message: 'AST heuristic detected a strong delegate/dataSource reference; weak delegates remain the preferred baseline to avoid retain cycles.' },
|
|
650
650
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftStrongSelfEscapingClosureUsage, ruleId: 'heuristics.ios.memory.strong-self-escaping-closure.ast', code: 'HEURISTICS_IOS_MEMORY_STRONG_SELF_ESCAPING_CLOSURE_AST', message: 'AST heuristic detected strong self capture in an escaping iOS closure; weak or unowned captures remain the preferred baseline when ownership is not explicit.' },
|
|
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
|
+
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftSwinjectUsage, ruleId: 'heuristics.ios.architecture.swinject.ast', code: 'HEURISTICS_IOS_ARCHITECTURE_SWINJECT_AST', message: 'AST heuristic detected Swinject usage; manual dependency injection or SwiftUI Environment remain the preferred native baseline.' },
|
|
652
653
|
{ 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
654
|
{ 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
655
|
{ 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.' },
|
|
@@ -694,6 +695,7 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
|
|
|
694
695
|
{ platform: 'ios', pathCheck: isIOSSwiftTestPath, excludePaths: [], detect: TextIOS.hasSwiftWaitForExpectationsUsage, ruleId: 'heuristics.ios.testing.wait-for-expectations.ast', code: 'HEURISTICS_IOS_TESTING_WAIT_FOR_EXPECTATIONS_AST', message: 'AST heuristic detected wait(for:)/waitForExpectations usage where await fulfillment(of:) may be preferred.' },
|
|
695
696
|
{ platform: 'ios', pathCheck: isIOSSwiftTestPath, excludePaths: [], detect: TextIOS.hasSwiftLegacyExpectationDescriptionUsage, ruleId: 'heuristics.ios.testing.legacy-expectation-description.ast', code: 'HEURISTICS_IOS_TESTING_LEGACY_EXPECTATION_DESCRIPTION_AST', message: 'AST heuristic detected expectation(description:) usage without modern fulfillment/confirmation flow.' },
|
|
696
697
|
{ platform: 'ios', pathCheck: isIOSSwiftTestPath, excludePaths: [], detect: TextIOS.hasSwiftMixedTestingFrameworksUsage, ruleId: 'heuristics.ios.testing.mixed-frameworks.ast', code: 'HEURISTICS_IOS_TESTING_MIXED_FRAMEWORKS_AST', message: 'AST heuristic detected XCTestCase and Swift Testing markers mixed in the same test file without explicit compatibility reason.' },
|
|
698
|
+
{ platform: 'ios', pathCheck: isIOSSwiftTestPath, excludePaths: [], detect: TextIOS.hasSwiftQuickNimbleUsage, ruleId: 'heuristics.ios.testing.quick-nimble.ast', code: 'HEURISTICS_IOS_TESTING_QUICK_NIMBLE_AST', message: 'AST heuristic detected Quick/Nimble usage where native Swift Testing remains the preferred baseline.' },
|
|
697
699
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftNSManagedObjectBoundaryUsage, ruleId: 'heuristics.ios.core-data.nsmanagedobject-boundary.ast', code: 'HEURISTICS_IOS_CORE_DATA_NSMANAGEDOBJECT_BOUNDARY_AST', message: 'AST heuristic detected NSManagedObject in a shared boundary.' },
|
|
698
700
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftNSManagedObjectAsyncBoundaryUsage, ruleId: 'heuristics.ios.core-data.nsmanagedobject-async-boundary.ast', code: 'HEURISTICS_IOS_CORE_DATA_NSMANAGEDOBJECT_ASYNC_BOUNDARY_AST', message: 'AST heuristic detected NSManagedObject in an async boundary.' },
|
|
699
701
|
{ platform: 'ios', pathCheck: isIOSApplicationOrPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftCoreDataLayerLeakUsage, ruleId: 'heuristics.ios.core-data.layer-leak.ast', code: 'HEURISTICS_IOS_CORE_DATA_LAYER_LEAK_AST', message: 'AST heuristic detected Core Data APIs leaking into application/presentation code.' },
|
|
@@ -238,6 +238,25 @@ export const iosRules: RuleSet = [
|
|
|
238
238
|
code: 'HEURISTICS_IOS_ARCHITECTURE_CUSTOM_SINGLETON_AST',
|
|
239
239
|
},
|
|
240
240
|
},
|
|
241
|
+
{
|
|
242
|
+
id: 'heuristics.ios.architecture.swinject.ast',
|
|
243
|
+
description: 'Detects Swinject usage in iOS production code.',
|
|
244
|
+
severity: 'WARN',
|
|
245
|
+
platform: 'ios',
|
|
246
|
+
locked: true,
|
|
247
|
+
when: {
|
|
248
|
+
kind: 'Heuristic',
|
|
249
|
+
where: {
|
|
250
|
+
ruleId: 'heuristics.ios.architecture.swinject.ast',
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
then: {
|
|
254
|
+
kind: 'Finding',
|
|
255
|
+
message:
|
|
256
|
+
'AST heuristic detected Swinject usage; manual dependency injection or SwiftUI Environment remain the preferred native baseline.',
|
|
257
|
+
code: 'HEURISTICS_IOS_ARCHITECTURE_SWINJECT_AST',
|
|
258
|
+
},
|
|
259
|
+
},
|
|
241
260
|
{
|
|
242
261
|
id: 'heuristics.ios.architecture.massive-view-controller.ast',
|
|
243
262
|
description: 'Detects UIViewController classes with direct infrastructure/data access.',
|
|
@@ -1073,6 +1092,25 @@ export const iosRules: RuleSet = [
|
|
|
1073
1092
|
code: 'HEURISTICS_IOS_TESTING_MIXED_FRAMEWORKS_AST',
|
|
1074
1093
|
},
|
|
1075
1094
|
},
|
|
1095
|
+
{
|
|
1096
|
+
id: 'heuristics.ios.testing.quick-nimble.ast',
|
|
1097
|
+
description: 'Detects Quick/Nimble usage in iOS Swift tests.',
|
|
1098
|
+
severity: 'WARN',
|
|
1099
|
+
platform: 'ios',
|
|
1100
|
+
locked: true,
|
|
1101
|
+
when: {
|
|
1102
|
+
kind: 'Heuristic',
|
|
1103
|
+
where: {
|
|
1104
|
+
ruleId: 'heuristics.ios.testing.quick-nimble.ast',
|
|
1105
|
+
},
|
|
1106
|
+
},
|
|
1107
|
+
then: {
|
|
1108
|
+
kind: 'Finding',
|
|
1109
|
+
message:
|
|
1110
|
+
'AST heuristic detected Quick/Nimble usage where native Swift Testing remains the preferred baseline.',
|
|
1111
|
+
code: 'HEURISTICS_IOS_TESTING_QUICK_NIMBLE_AST',
|
|
1112
|
+
},
|
|
1113
|
+
},
|
|
1076
1114
|
{
|
|
1077
1115
|
id: 'heuristics.ios.core-data.nsmanagedobject-boundary.ast',
|
|
1078
1116
|
description: 'Detects NSManagedObject usage in shared iOS boundaries.',
|
|
@@ -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.swinject-prohibido-di-manual-o-environment': heuristicDetector(
|
|
86
|
+
'ios.architecture.swinject',
|
|
87
|
+
['heuristics.ios.architecture.swinject.ast']
|
|
88
|
+
),
|
|
85
89
|
'skills.ios.guideline.ios.magic-numbers-usar-constantes-con-nombres': heuristicDetector(
|
|
86
90
|
'ios.maintainability.magic-number-layout',
|
|
87
91
|
['heuristics.ios.maintainability.magic-number-layout.ast']
|
|
@@ -259,6 +263,10 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
|
|
|
259
263
|
'skills.ios.no-mixed-testing-frameworks': heuristicDetector('ios.testing.mixed-frameworks', [
|
|
260
264
|
'heuristics.ios.testing.mixed-frameworks.ast',
|
|
261
265
|
]),
|
|
266
|
+
'skills.ios.guideline.ios.quick-nimble-prohibido-usar-swift-testing-nativo': heuristicDetector(
|
|
267
|
+
'ios.testing.quick-nimble',
|
|
268
|
+
['heuristics.ios.testing.quick-nimble.ast']
|
|
269
|
+
),
|
|
262
270
|
'skills.ios.no-nsmanagedobject-boundary': heuristicDetector(
|
|
263
271
|
'ios.core-data.nsmanagedobject-boundary',
|
|
264
272
|
['heuristics.ios.core-data.nsmanagedobject-boundary.ast']
|
|
@@ -480,6 +480,9 @@ const normalizeKnownRuleTarget = (
|
|
|
480
480
|
) {
|
|
481
481
|
return 'skills.ios.guideline.ios.magic-numbers-usar-constantes-con-nombres';
|
|
482
482
|
}
|
|
483
|
+
if (includes('swinject')) {
|
|
484
|
+
return 'skills.ios.guideline.ios.swinject-prohibido-di-manual-o-environment';
|
|
485
|
+
}
|
|
483
486
|
if (
|
|
484
487
|
includes('mixing legacy xctest style') ||
|
|
485
488
|
includes('mixed xctest and swift testing') ||
|
|
@@ -487,6 +490,9 @@ const normalizeKnownRuleTarget = (
|
|
|
487
490
|
) {
|
|
488
491
|
return 'skills.ios.no-mixed-testing-frameworks';
|
|
489
492
|
}
|
|
493
|
+
if (includes('quick nimble') || includes('quick/nimble')) {
|
|
494
|
+
return 'skills.ios.guideline.ios.quick-nimble-prohibido-usar-swift-testing-nativo';
|
|
495
|
+
}
|
|
490
496
|
if (
|
|
491
497
|
includes('nsmanagedobject across boundaries') ||
|
|
492
498
|
includes('passing nsmanagedobject') ||
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.215",
|
|
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-13T13:
|
|
4
|
+
"generatedAt": "2026-05-13T13:18:11.822Z",
|
|
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": "45d9315671888e71b9f038d52026b4d1da3e3ac41312749be77c5ed44b52958b",
|
|
5768
5768
|
"rules": [
|
|
5769
5769
|
{
|
|
5770
5770
|
"id": "skills.ios.guideline.ios.accessibility-identifiers-para-localizar-elementos",
|
|
@@ -7468,7 +7468,7 @@
|
|
|
7468
7468
|
"sourcePath": "vendor/skills/ios-enterprise-rules/SKILL.md",
|
|
7469
7469
|
"confidence": "HIGH",
|
|
7470
7470
|
"locked": true,
|
|
7471
|
-
"evaluationMode": "
|
|
7471
|
+
"evaluationMode": "AUTO",
|
|
7472
7472
|
"origin": "core"
|
|
7473
7473
|
},
|
|
7474
7474
|
{
|
|
@@ -7912,7 +7912,7 @@
|
|
|
7912
7912
|
"sourcePath": "vendor/skills/ios-enterprise-rules/SKILL.md",
|
|
7913
7913
|
"confidence": "HIGH",
|
|
7914
7914
|
"locked": true,
|
|
7915
|
-
"evaluationMode": "
|
|
7915
|
+
"evaluationMode": "AUTO",
|
|
7916
7916
|
"origin": "core"
|
|
7917
7917
|
},
|
|
7918
7918
|
{
|