pumuki 6.3.301 → 6.3.302

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.
@@ -32,6 +32,7 @@ import {
32
32
  hasSwiftDispatchSemaphoreUsage,
33
33
  hasSwiftAdHocLoggingUsage,
34
34
  hasSwiftAlamofireUsage,
35
+ collectSwiftComposableArchitectureUsageLines,
35
36
  hasSwiftEmptyCatchUsage,
36
37
  collectSwiftNSErrorThrowLines,
37
38
  hasSwiftNSErrorThrowUsage,
@@ -155,6 +156,7 @@ import {
155
156
  hasSwiftStrongDelegateReferenceUsage,
156
157
  hasSwiftStrongSelfEscapingClosureUsage,
157
158
  hasSwiftSwinjectUsage,
159
+ hasSwiftComposableArchitectureUsage,
158
160
  hasSwiftTabItemUsage,
159
161
  hasSwiftTaskDetachedUsage,
160
162
  hasSwiftUnownedSelfCaptureUsage,
@@ -1296,6 +1298,55 @@ let text = "import Swinject"
1296
1298
  assert.equal(hasSwiftSwinjectUsage(ignored), false);
1297
1299
  });
1298
1300
 
1301
+ test('hasSwiftComposableArchitectureUsage detecta TCA y preserva arquitectura nativa', () => {
1302
+ const source = `
1303
+ import SwiftUI
1304
+ import ComposableArchitecture
1305
+
1306
+ struct CheckoutFeature: Reducer {
1307
+ struct State: Equatable {}
1308
+ enum Action {}
1309
+ var body: some ReducerOf<Self> {
1310
+ Reduce { state, action in .none }
1311
+ }
1312
+ }
1313
+
1314
+ struct CheckoutView: View {
1315
+ let store: StoreOf<CheckoutFeature>
1316
+ var body: some View {
1317
+ WithViewStore(store, observe: { $0 }) { viewStore in
1318
+ Text("Checkout")
1319
+ }
1320
+ }
1321
+ }
1322
+ `;
1323
+ const native = `
1324
+ import SwiftUI
1325
+
1326
+ @Observable
1327
+ final class CheckoutViewModel {
1328
+ func submit() async {}
1329
+ }
1330
+
1331
+ struct CheckoutView: View {
1332
+ @State private var model = CheckoutViewModel()
1333
+ var body: some View {
1334
+ Button("Checkout") { Task { await model.submit() } }
1335
+ }
1336
+ }
1337
+ `;
1338
+ const ignored = `
1339
+ let sample = "import ComposableArchitecture"
1340
+ // let store: StoreOf<CheckoutFeature>
1341
+ `;
1342
+
1343
+ assert.equal(hasSwiftComposableArchitectureUsage(source), true);
1344
+ assert.deepEqual(collectSwiftComposableArchitectureUsageLines(source), [3, 8, 14]);
1345
+ assert.equal(hasSwiftComposableArchitectureUsage(native), false);
1346
+ assert.equal(hasSwiftComposableArchitectureUsage(ignored), false);
1347
+ assert.deepEqual(collectSwiftComposableArchitectureUsageLines(ignored), []);
1348
+ });
1349
+
1299
1350
  test('hasSwiftMassiveViewControllerResponsibilityUsage detecta ViewControllers con acceso directo a infraestructura', () => {
1300
1351
  const source = `
1301
1352
  final class CheckoutViewController: UIViewController {
@@ -1154,6 +1154,18 @@ export const hasSwiftSwinjectUsage = (source: string): boolean => {
1154
1154
  );
1155
1155
  };
1156
1156
 
1157
+ export const collectSwiftComposableArchitectureUsageLines = (source: string): readonly number[] => {
1158
+ return sortedUniqueLines([
1159
+ ...collectSwiftRegexLines(source, /\bimport\s+ComposableArchitecture\b/),
1160
+ ...collectSwiftRegexLines(source, /\b(?:StoreOf|ViewStore|WithViewStore|ReducerOf)\s*</),
1161
+ ...collectSwiftRegexLines(source, /\b(?:Store|Reducer|Effect|TestStore)\s*</),
1162
+ ]);
1163
+ };
1164
+
1165
+ export const hasSwiftComposableArchitectureUsage = (source: string): boolean => {
1166
+ return collectSwiftComposableArchitectureUsageLines(source).length > 0;
1167
+ };
1168
+
1157
1169
  export const hasSwiftMassiveViewControllerResponsibilityUsage = (source: string): boolean => {
1158
1170
  const sanitized = sanitizeSwiftSourceForMultilineRegex(source);
1159
1171
  const viewControllerPattern =
@@ -787,6 +787,7 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
787
787
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftWarningSuppressionUsage, locateLines: TextIOS.collectSwiftWarningSuppressionLines, primaryNode: (lines) => ({ kind: 'member', name: 'Swift warning or lint suppression directive', lines }), relatedNodes: (lines) => [{ kind: 'member', name: 'replacement: fix the warned code or configure the exception centrally', lines }], why: 'Local warning and lint suppressions hide code-quality failures from the normal compiler/tooling feedback loop.', impact: 'Suppressed diagnostics can turn into future errors or let unsafe production code bypass Pumuki and repository skills without a visible remediation path.', expected_fix: 'Remove local swiftlint/swiftformat/periphery disable directives and #warning markers by fixing the underlying code, or move a justified project-wide exception into policy/config with traceability.', ruleId: 'heuristics.ios.maintainability.warning-suppression.ast', code: 'HEURISTICS_IOS_MAINTAINABILITY_WARNING_SUPPRESSION_AST', message: 'AST heuristic detected local warning/lint suppression in iOS production code; fix the underlying issue instead of hiding diagnostics.' },
788
788
  { 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.' },
789
789
  { 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.' },
790
+ { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftComposableArchitectureUsage, locateLines: TextIOS.collectSwiftComposableArchitectureUsageLines, primaryNode: (lines) => ({ kind: 'call', name: 'Composable Architecture dependency node', lines }), relatedNodes: (lines) => [{ kind: 'call', name: 'replacement: native SwiftUI state, use case, actor or environment boundary', lines }], why: 'The iOS skill baseline prefers native SwiftUI, structured concurrency and explicit architecture boundaries over third-party TCA runtime primitives.', impact: 'TCA imports and Store/Reducer nodes introduce a non-native dependency model that can bypass the repository architecture contract and make remediation depend on framework-specific DSL semantics.', expected_fix: 'Replace ComposableArchitecture usage with native SwiftUI state, @Observable view models, use cases, actors and explicit Environment/dependency boundaries approved by the repo.', ruleId: 'heuristics.ios.architecture.tca-composable-architecture.ast', code: 'HEURISTICS_IOS_ARCHITECTURE_TCA_COMPOSABLE_ARCHITECTURE_AST', message: 'AST heuristic detected The Composable Architecture usage in iOS production code; use native SwiftUI and explicit architecture boundaries.' },
790
791
  { 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.' },
791
792
  { 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.' },
792
793
  { platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftMagicNumberLayoutUsage, locateLines: TextIOS.collectSwiftMagicNumberLayoutLines, primaryNode: (lines) => ({ kind: 'call', name: 'SwiftUI layout numeric literal', lines }), relatedNodes: (lines) => [{ kind: 'property', name: 'replacement: named metric constant or design token', lines }], why: 'Inline numeric layout literals hide design intent and make visual remediation dependent on scanning the whole view body.', impact: 'Pixel-perfect slices can be blocked without a concrete node unless the finding points to the exact spacing, frame or padding call to fix.', expected_fix: 'Move repeated or meaningful layout numbers into named constants or design tokens, or use relative layout APIs when the number encodes screen geometry.', 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.' },
@@ -3,7 +3,7 @@ import test from 'node:test';
3
3
  import { iosRules } from './ios';
4
4
 
5
5
  test('iosRules define reglas heurísticas locked para plataforma ios', () => {
6
- assert.equal(iosRules.length, 117);
6
+ assert.equal(iosRules.length, 118);
7
7
 
8
8
  const ids = iosRules.map((rule) => rule.id);
9
9
  assert.deepEqual(ids, [
@@ -36,6 +36,7 @@ test('iosRules define reglas heurísticas locked para plataforma ios', () => {
36
36
  'heuristics.ios.maintainability.warning-suppression.ast',
37
37
  'heuristics.ios.architecture.custom-singleton.ast',
38
38
  'heuristics.ios.architecture.swinject.ast',
39
+ 'heuristics.ios.architecture.tca-composable-architecture.ast',
39
40
  'heuristics.ios.architecture.massive-view-controller.ast',
40
41
  'heuristics.ios.maintainability.magic-number-layout.ast',
41
42
  'heuristics.ios.uikit.manual-frame-layout.ast',
@@ -235,6 +236,10 @@ test('iosRules define reglas heurísticas locked para plataforma ios', () => {
235
236
  byId.get('heuristics.ios.networking.alamofire.ast')?.then.code,
236
237
  'HEURISTICS_IOS_NETWORKING_ALAMOFIRE_AST'
237
238
  );
239
+ assert.equal(
240
+ byId.get('heuristics.ios.architecture.tca-composable-architecture.ast')?.then.code,
241
+ 'HEURISTICS_IOS_ARCHITECTURE_TCA_COMPOSABLE_ARCHITECTURE_AST'
242
+ );
238
243
  assert.equal(
239
244
  byId.get('heuristics.ios.json.jsonserialization.ast')?.then.code,
240
245
  'HEURISTICS_IOS_JSON_JSONSERIALIZATION_AST'
@@ -539,6 +539,25 @@ export const iosRules: RuleSet = [
539
539
  code: 'HEURISTICS_IOS_ARCHITECTURE_SWINJECT_AST',
540
540
  },
541
541
  },
542
+ {
543
+ id: 'heuristics.ios.architecture.tca-composable-architecture.ast',
544
+ description: 'Detects The Composable Architecture usage in iOS production code.',
545
+ severity: 'WARN',
546
+ platform: 'ios',
547
+ locked: true,
548
+ when: {
549
+ kind: 'Heuristic',
550
+ where: {
551
+ ruleId: 'heuristics.ios.architecture.tca-composable-architecture.ast',
552
+ },
553
+ },
554
+ then: {
555
+ kind: 'Finding',
556
+ message:
557
+ 'AST heuristic detected The Composable Architecture usage; native SwiftUI state, use cases, actors and explicit environment boundaries remain the preferred baseline.',
558
+ code: 'HEURISTICS_IOS_ARCHITECTURE_TCA_COMPOSABLE_ARCHITECTURE_AST',
559
+ },
560
+ },
542
561
  {
543
562
  id: 'heuristics.ios.architecture.massive-view-controller.ast',
544
563
  description: 'Detects UIViewController classes with direct infrastructure/data access.',
@@ -176,6 +176,14 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
176
176
  'ios.architecture.swinject',
177
177
  ['heuristics.ios.architecture.swinject.ast']
178
178
  ),
179
+ 'skills.ios.guideline.ios.tca-the-composable-architecture-para-apps-grandes-funcional':
180
+ heuristicDetector('ios.architecture.tca-composable-architecture', [
181
+ 'heuristics.ios.architecture.tca-composable-architecture.ast',
182
+ ]),
183
+ 'skills.ios.guideline.ios.navegacio-n-event-driven-estilo-tca-cuando-el-modelo-lo-requiera-event':
184
+ heuristicDetector('ios.navigation.event-driven-no-tca-runtime', [
185
+ 'heuristics.ios.architecture.tca-composable-architecture.ast',
186
+ ]),
179
187
  'skills.ios.guideline.ios.magic-numbers-usar-constantes-con-nombres': heuristicDetector(
180
188
  'ios.maintainability.magic-number-layout',
181
189
  ['heuristics.ios.maintainability.magic-number-layout.ast']
@@ -246,6 +254,7 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
246
254
  'heuristics.ios.dependencies.cocoapods.ast',
247
255
  'heuristics.ios.dependencies.carthage.ast',
248
256
  'heuristics.ios.architecture.swinject.ast',
257
+ 'heuristics.ios.architecture.tca-composable-architecture.ast',
249
258
  ]),
250
259
  'skills.ios.guideline.ios.libreri-as-de-terceros-usar-apis-nativas': heuristicDetector(
251
260
  'ios.dependencies.third-party-native-baseline',
@@ -254,6 +263,7 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
254
263
  'heuristics.ios.dependencies.cocoapods.ast',
255
264
  'heuristics.ios.dependencies.carthage.ast',
256
265
  'heuristics.ios.architecture.swinject.ast',
266
+ 'heuristics.ios.architecture.tca-composable-architecture.ast',
257
267
  ]
258
268
  ),
259
269
  'skills.ios.guideline.ios.keychain-passwords-tokens-no-userdefaults': heuristicDetector(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.301",
3
+ "version": "6.3.302",
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-19T17:54:54.241Z",
4
+ "generatedAt": "2026-05-19T19:44:36.203Z",
5
5
  "bundles": [
6
6
  {
7
7
  "name": "android-guidelines",