pumuki 6.3.258 → 6.3.260

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [6.3.260] - 2026-05-14
4
+
5
+ - iOS: `skills.ios.no-force-unwrap` now emits actionable AST-style evidence for Swift postfix force unwraps, including exact lines, primary/related nodes and remediation toward guarded optional handling.
6
+
7
+ ## [6.3.259] - 2026-05-14
8
+
9
+ - iOS: `skills.ios.prefer-swift-testing` now emits actionable AST-style evidence for modernizable `XCTestCase` suites, including exact lines, primary/related nodes and native Swift Testing `import Testing`/`@Suite`/`@Test` remediation.
10
+
3
11
  ## [6.3.258] - 2026-05-14
4
12
 
5
13
  - iOS: `skills.ios.no-xctunwrap` now emits actionable AST-style evidence for legacy `XCTUnwrap` calls, including exact lines, primary/related nodes and native Swift Testing `#require` remediation.
@@ -6,6 +6,7 @@ import {
6
6
  findSwiftOpenClosedSwitchMatch,
7
7
  findSwiftConcreteDependencyDipMatch,
8
8
  findSwiftPresentationSrpMatch,
9
+ collectSwiftModernizableXCTestSuiteLines,
9
10
  collectSwiftLegacyExpectationDescriptionLines,
10
11
  collectSwiftMixedTestingFrameworkLines,
11
12
  collectSwiftQuickNimbleLines,
@@ -81,6 +82,7 @@ import {
81
82
  hasSwiftJSONSerializationUsage,
82
83
  hasSwiftExplicitColorStaticMemberUsage,
83
84
  hasSwiftClosureBasedViewBuilderContentUsage,
85
+ collectSwiftForceUnwrapLines,
84
86
  hasSwiftLargeConfigContextViewPropertyUsage,
85
87
  hasSwiftUiConditionalSameViewIdentityUsage,
86
88
  hasSwiftUiParentOwnedSheetActionUsage,
@@ -105,6 +107,7 @@ let token = optionalToken!
105
107
  let value = loadUser()!
106
108
  `;
107
109
  assert.equal(hasSwiftForceUnwrap(source), true);
110
+ assert.deepEqual(collectSwiftForceUnwrapLines(source), [2, 3]);
108
111
  });
109
112
 
110
113
  test('hasSwiftForceUnwrap excluye type annotations, force cast y operadores', () => {
@@ -115,6 +118,7 @@ if left != right { print("ok") }
115
118
  let flag = value!!
116
119
  `;
117
120
  assert.equal(hasSwiftForceUnwrap(source), false);
121
+ assert.deepEqual(collectSwiftForceUnwrapLines(source), []);
118
122
  });
119
123
 
120
124
  test('hasSwiftForceUnwrap excluye comparaciones seguras contra nil', () => {
@@ -128,6 +132,7 @@ if waitersByKey[key] != nil {
128
132
  }
129
133
  `;
130
134
  assert.equal(hasSwiftForceUnwrap(source), false);
135
+ assert.deepEqual(collectSwiftForceUnwrapLines(source), []);
131
136
  });
132
137
 
133
138
  test('hasSwiftAnyViewUsage detecta AnyView en codigo real', () => {
@@ -1624,6 +1629,8 @@ final class LoginUITests: XCTestCase {
1624
1629
  assert.equal(hasSwiftModernizableXCTestSuiteUsage(legacySuite), true);
1625
1630
  assert.equal(hasSwiftModernizableXCTestSuiteUsage(mixedSuite), false);
1626
1631
  assert.equal(hasSwiftModernizableXCTestSuiteUsage(uiSuite), false);
1632
+ assert.deepEqual(collectSwiftModernizableXCTestSuiteLines(legacySuite), [2, 4, 5]);
1633
+ assert.deepEqual(collectSwiftModernizableXCTestSuiteLines(mixedSuite), []);
1627
1634
  });
1628
1635
 
1629
1636
  test('hasSwiftMixedTestingFrameworksUsage detecta mezcla XCTestCase y Testing/@Test', () => {
@@ -342,7 +342,7 @@ const isForceUnwrapAt = (source: string, index: number): boolean => {
342
342
 
343
343
  const previousChar = source[previousIndex];
344
344
  const previousIdentifier = readIdentifierBackward(source, previousIndex);
345
- if (previousIdentifier.value === 'as') {
345
+ if (previousIdentifier.value === 'as' || previousIdentifier.value === 'try') {
346
346
  return false;
347
347
  }
348
348
  if (
@@ -370,6 +370,20 @@ export const hasSwiftForceUnwrap = (source: string): boolean => {
370
370
  });
371
371
  };
372
372
 
373
+ export const collectSwiftForceUnwrapLines = (source: string): readonly number[] => {
374
+ const lines: number[] = [];
375
+ source.split(/\r?\n/).forEach((line, index) => {
376
+ const sanitizedLine = stripSwiftLineForSemanticScan(line);
377
+ for (let cursor = 0; cursor < sanitizedLine.length; cursor += 1) {
378
+ if (sanitizedLine[cursor] === '!' && isForceUnwrapAt(sanitizedLine, cursor)) {
379
+ lines.push(index + 1);
380
+ break;
381
+ }
382
+ }
383
+ });
384
+ return sortedUniqueLines(lines);
385
+ };
386
+
373
387
  export const hasSwiftAnyViewUsage = (source: string): boolean => {
374
388
  return scanCodeLikeSource(source, ({ source: swiftSource, index, current }) => {
375
389
  if (current !== 'A') {
@@ -1328,6 +1342,18 @@ export const hasSwiftModernizableXCTestSuiteUsage = (source: string): boolean =>
1328
1342
  return true;
1329
1343
  };
1330
1344
 
1345
+ export const collectSwiftModernizableXCTestSuiteLines = (source: string): readonly number[] => {
1346
+ if (!hasSwiftModernizableXCTestSuiteUsage(source)) {
1347
+ return [];
1348
+ }
1349
+
1350
+ return sortedUniqueLines([
1351
+ ...collectSwiftRegexLines(source, /\bimport\s+XCTest\b/),
1352
+ ...collectSwiftRegexLines(source, /\bclass\s+[A-Za-z_][A-Za-z0-9_]*\s*:\s*XCTestCase\b/),
1353
+ ...collectSwiftRegexLines(source, /^\s*(?:override\s+)?func\s+test[A-Za-z0-9_]*\s*\(/),
1354
+ ]);
1355
+ };
1356
+
1331
1357
  export const hasSwiftMixedTestingFrameworksUsage = (source: string): boolean => {
1332
1358
  if (!hasSwiftXCTestImportUsage(source) || !hasSwiftXCTestCaseSubclassUsage(source)) {
1333
1359
  return false;
@@ -642,7 +642,7 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
642
642
  // iOS
643
643
  { platform: 'ios', pathCheck: isIOSPodfilePath, excludePaths: [], detect: detectsTrackedFilePresence, ruleId: 'heuristics.ios.dependencies.cocoapods.ast', code: 'HEURISTICS_IOS_DEPENDENCIES_COCOAPODS_AST', message: 'AST heuristic detected CocoaPods dependency files in an iOS project; Swift Package Manager remains the preferred baseline for new code.' },
644
644
  { platform: 'ios', pathCheck: isIOSCartfilePath, excludePaths: [], detect: detectsTrackedFilePresence, ruleId: 'heuristics.ios.dependencies.carthage.ast', code: 'HEURISTICS_IOS_DEPENDENCIES_CARTHAGE_AST', message: 'AST heuristic detected Carthage dependency files in an iOS project; Swift Package Manager remains the preferred baseline for new code.' },
645
- { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftForceUnwrap, ruleId: 'heuristics.ios.force-unwrap.ast', code: 'HEURISTICS_IOS_FORCE_UNWRAP_AST', message: 'AST heuristic detected force unwrap usage.' },
645
+ { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftForceUnwrap, locateLines: TextIOS.collectSwiftForceUnwrapLines, primaryNode: (lines) => ({ kind: 'member', name: 'force unwrap postfix !', lines }), relatedNodes: (lines) => [{ kind: 'member', name: 'replacement: guarded optional binding or explicit failure path', lines }], why: 'Force unwrap turns optional handling into a runtime crash path instead of a checked domain, UI or infrastructure decision.', impact: 'A nil value can terminate the app outside the error boundary, making production behavior non-deterministic and hard to recover or test.', expected_fix: 'Replace postfix ! with guard let, if let, nil coalescing, throwing validation, or an explicit fallback. In modern Swift tests prefer #require when the unwrap is part of an assertion contract.', ruleId: 'heuristics.ios.force-unwrap.ast', code: 'HEURISTICS_IOS_FORCE_UNWRAP_AST', message: 'AST heuristic detected force unwrap usage.' },
646
646
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftAnyViewUsage, ruleId: 'heuristics.ios.anyview.ast', code: 'HEURISTICS_IOS_ANYVIEW_AST', message: 'AST heuristic detected AnyView usage.' },
647
647
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftForceTryUsage, ruleId: 'heuristics.ios.force-try.ast', code: 'HEURISTICS_IOS_FORCE_TRY_AST', message: 'AST heuristic detected force try usage.' },
648
648
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftForceCastUsage, ruleId: 'heuristics.ios.force-cast.ast', code: 'HEURISTICS_IOS_FORCE_CAST_AST', message: 'AST heuristic detected force cast usage.' },
@@ -717,7 +717,7 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
717
717
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftLegacyOnChangeUsage, ruleId: 'heuristics.ios.legacy-onchange.ast', code: 'HEURISTICS_IOS_LEGACY_ONCHANGE_AST', message: 'AST heuristic detected legacy onChange usage where modern overloads may be preferred.' },
718
718
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftUIScreenMainBoundsUsage, ruleId: 'heuristics.ios.uiscreen-main-bounds.ast', code: 'HEURISTICS_IOS_UISCREEN_MAIN_BOUNDS_AST', message: 'AST heuristic detected UIScreen.main.bounds usage.' },
719
719
  { platform: 'ios', pathCheck: isIOSSwiftTestPath, excludePaths: [], detect: TextIOS.hasSwiftLegacyXCTestImportUsage, ruleId: 'heuristics.ios.testing.xctest-import.ast', code: 'HEURISTICS_IOS_TESTING_XCTEST_IMPORT_AST', message: 'AST heuristic detected XCTest-only test usage where Swift Testing may be preferred.' },
720
- { platform: 'ios', pathCheck: isIOSSwiftTestPath, excludePaths: [], detect: TextIOS.hasSwiftModernizableXCTestSuiteUsage, ruleId: 'heuristics.ios.testing.xctest-suite-modernizable.ast', code: 'HEURISTICS_IOS_TESTING_XCTEST_SUITE_MODERNIZABLE_AST', message: 'AST heuristic detected XCTestCase/test... suite that may be modernizable to Swift Testing with import Testing and @Test.' },
720
+ { platform: 'ios', pathCheck: isIOSSwiftTestPath, excludePaths: [], detect: TextIOS.hasSwiftModernizableXCTestSuiteUsage, locateLines: TextIOS.collectSwiftModernizableXCTestSuiteLines, primaryNode: (lines) => ({ kind: 'class', name: 'modernizable XCTestCase test suite', lines }), relatedNodes: (lines) => [{ kind: 'class', name: 'replacement: Swift Testing @Suite/@Test suite', lines }], why: 'XCTestCase suites with test... methods keep unit tests on the legacy XCTest lifecycle even when the file can be expressed as a native Swift Testing suite.', impact: 'New or modernizable tests drift away from the preferred Swift Testing contract, making suite migration, diagnostics and rule enforcement harder to audit.', expected_fix: 'Replace import XCTest and XCTestCase/test... unit suites with import Testing, @Suite where useful and @Test functions. Keep XCTestCase only for explicit UI, performance or brownfield compatibility cases.', ruleId: 'heuristics.ios.testing.xctest-suite-modernizable.ast', code: 'HEURISTICS_IOS_TESTING_XCTEST_SUITE_MODERNIZABLE_AST', message: 'AST heuristic detected modernizable XCTestCase/test... suite; use native Swift Testing where applicable.' },
721
721
  { platform: 'ios', pathCheck: isIOSSwiftTestPath, excludePaths: [], detect: TextIOS.hasSwiftXCTestAssertionUsage, locateLines: TextIOS.collectSwiftXCTestAssertionLines, primaryNode: (lines) => ({ kind: 'call', name: 'legacy XCTest assertion call', lines }), relatedNodes: (lines) => [{ kind: 'call', name: 'replacement: #expect(...)', lines }], why: 'XCTAssert* and XCTFail keep modern unit tests tied to XCTest assertion APIs even when Swift Testing can express the same expectation natively.', impact: 'Assertion style drifts across the test suite and makes migration to Swift Testing harder to audit because failures use mixed vocabularies and diagnostics.', expected_fix: 'Replace XCTAssert* and XCTFail with Swift Testing #expect(...) or a thrown test failure pattern when the target already supports Swift Testing. Keep XCTest assertions only for explicit legacy, UI or performance test compatibility.', ruleId: 'heuristics.ios.testing.xctassert.ast', code: 'HEURISTICS_IOS_TESTING_XCTASSERT_AST', message: 'AST heuristic detected legacy XCTest assertion calls; use native Swift Testing #expect where applicable.' },
722
722
  { platform: 'ios', pathCheck: isIOSSwiftTestPath, excludePaths: [], detect: TextIOS.hasSwiftXCTUnwrapUsage, locateLines: TextIOS.collectSwiftXCTUnwrapLines, primaryNode: (lines) => ({ kind: 'call', name: 'legacy XCTest unwrap call', lines }), relatedNodes: (lines) => [{ kind: 'call', name: 'replacement: #require(...)', lines }], why: 'XCTUnwrap keeps optional unwrapping tied to XCTest even when Swift Testing can express required values natively.', impact: 'Tests retain mixed assertion vocabulary and weaker migration traceability because required optional values are not represented through Swift Testing diagnostics.', expected_fix: 'Replace try XCTUnwrap(optionalValue) with try #require(optionalValue) when the target already supports Swift Testing. Keep XCTUnwrap only for explicit XCTest compatibility, UI or performance test cases.', ruleId: 'heuristics.ios.testing.xctunwrap.ast', code: 'HEURISTICS_IOS_TESTING_XCTUNWRAP_AST', message: 'AST heuristic detected legacy XCTUnwrap calls; use native Swift Testing #require where applicable.' },
723
723
  { platform: 'ios', pathCheck: isIOSSwiftTestPath, excludePaths: [], detect: TextIOS.hasSwiftWaitForExpectationsUsage, locateLines: TextIOS.collectSwiftWaitForExpectationsLines, primaryNode: (lines) => ({ kind: 'call', name: 'legacy XCTest wait call', lines }), relatedNodes: (lines) => [{ kind: 'call', name: 'replacement: await fulfillment(of:timeout:)', lines }], why: 'Legacy XCTest wait APIs block the current test thread and hide async intent that Swift concurrency can express directly.', impact: 'Async tests become less deterministic, harder to cancel and easier to keep tied to XCTest-only migration paths.', expected_fix: 'Replace wait(for:timeout:), self.wait(for:timeout:) or waitForExpectations(timeout:) with await fulfillment(of:timeout:) when the test target supports async XCTest migration.', 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.' },
@@ -6,6 +6,14 @@ This file keeps only the operational highlights and rollout notes that matter wh
6
6
 
7
7
  ## 2026-04 (CLI stability and macOS notifications)
8
8
 
9
+ ### 2026-05-14 (v6.3.260)
10
+
11
+ - Published `pumuki@6.3.260` with AST-style line/node evidence for `skills.ios.no-force-unwrap`, making postfix `!` crashes remediable through guarded optional handling or explicit failure paths.
12
+
13
+ ### 2026-05-14 (v6.3.259)
14
+
15
+ - Published `pumuki@6.3.259` with AST-style line/node evidence for `skills.ios.prefer-swift-testing`, making modernizable `XCTestCase` suites remediable through native Swift Testing `import Testing`, `@Suite` and `@Test`.
16
+
9
17
  ### 2026-05-14 (v6.3.258)
10
18
 
11
19
  - Published `pumuki@6.3.258` with AST-style line/node evidence for `skills.ios.no-xctunwrap`, making legacy `XCTUnwrap` calls remediable through Swift Testing `#require`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.258",
3
+ "version": "6.3.260",
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": {