pumuki 6.3.295 → 6.3.297

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.297] - 2026-05-19
4
+
5
+ - `PUMUKI-INC-157`: staged no-code/documentation/repin scopes no longer fail the skills contract with `EVIDENCE_SKILLS_PLATFORMS_UNDETECTED`. When the effective staged scope has no iOS, Android, backend or frontend code paths, platform skills are reported as `NOT_APPLICABLE` instead of blocking, while code-bearing slices keep the hard skills gate.
6
+
7
+ ## [6.3.296] - 2026-05-19
8
+
9
+ - `PUMUKI-INC-156`: follow-up after RuralGo replay. Broad SwiftUI findings are now considered non-actionable even when they include a `primary_node` if that node spans many lines. Only a bounded AST/node range can hard-block a staged diff; file-level brownfield SwiftUI debt remains advisory for the atomic slice.
10
+
3
11
  ## [6.3.295] - 2026-05-19
4
12
 
5
13
  - `PUMUKI-INC-156`: staged PRE_WRITE/PRE_COMMIT no longer blocks an atomic iOS slice with broad file-level SwiftUI skill findings that expose dozens of lines without a precise AST node introduced by the diff. Precise AST/skills findings still block in every severity when they intersect changed lines; broad brownfield file debt is retained as advisory with remediation demanding line/node-level evidence or a dedicated remediation slice.
package/VERSION CHANGED
@@ -1 +1 @@
1
- 6.3.294
1
+ 6.3.297
@@ -8,6 +8,7 @@ import {
8
8
  findSwiftPresentationSrpMatch,
9
9
  collectSwiftModernizableXCTestSuiteLines,
10
10
  collectSwiftLegacyExpectationDescriptionLines,
11
+ collectSwiftMakeSUTWithoutMemoryTrackingLines,
11
12
  collectSwiftMixedTestingFrameworkLines,
12
13
  collectSwiftQuickNimbleLines,
13
14
  collectSwiftWaitForExpectationsLines,
@@ -78,6 +79,7 @@ import {
78
79
  hasSwiftEnvironmentObjectUsage,
79
80
  hasSwiftLowContrastStaticColorPairUsage,
80
81
  hasSwiftMainThreadBlockingSleepUsage,
82
+ hasSwiftMakeSUTWithoutMemoryTrackingUsage,
81
83
  hasSwiftMassiveViewControllerResponsibilityUsage,
82
84
  hasSwiftMagicNumberLayoutUsage,
83
85
  hasSwiftMixedTestingFrameworksUsage,
@@ -128,6 +130,7 @@ import {
128
130
  hasSwiftExplicitColorStaticMemberUsage,
129
131
  hasSwiftClosureBasedViewBuilderContentUsage,
130
132
  collectSwiftForceUnwrapLines,
133
+ collectSwiftUIKitManualFrameLayoutLines,
131
134
  collectSwiftWarningSuppressionLines,
132
135
  hasSwiftLargeConfigContextViewPropertyUsage,
133
136
  hasSwiftUiConditionalSameViewIdentityUsage,
@@ -147,6 +150,7 @@ import {
147
150
  hasSwiftXCTestAssertionUsage,
148
151
  hasSwiftXCTUnwrapUsage,
149
152
  hasSwiftUncheckedSendableUsage,
153
+ hasSwiftUIKitManualFrameLayoutUsage,
150
154
  } from './ios';
151
155
 
152
156
  test('hasSwiftForceUnwrap detecta force unwrap postfix en expresiones', () => {
@@ -1129,6 +1133,44 @@ struct ProfileView: View {
1129
1133
  assert.deepEqual(collectSwiftMagicNumberLayoutLines(constants), []);
1130
1134
  });
1131
1135
 
1136
+ test('hasSwiftUIKitManualFrameLayoutUsage detecta layout manual UIKit y preserva Auto Layout y SwiftUI frame', () => {
1137
+ const source = `
1138
+ final class CheckoutView: UIView {
1139
+ private let titleLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 320, height: 44))
1140
+
1141
+ func layoutBadge() {
1142
+ badgeView.frame = CGRect(x: 16, y: 16, width: 80, height: 32)
1143
+ }
1144
+ }
1145
+ `;
1146
+ const safe = `
1147
+ final class CheckoutView: UIView {
1148
+ private let titleLabel = UILabel()
1149
+
1150
+ func installConstraints() {
1151
+ titleLabel.translatesAutoresizingMaskIntoConstraints = false
1152
+ NSLayoutConstraint.activate([
1153
+ titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor)
1154
+ ])
1155
+ }
1156
+ }
1157
+
1158
+ struct CheckoutSwiftUIView: View {
1159
+ var body: some View {
1160
+ Text("Checkout").frame(maxWidth: .infinity)
1161
+ }
1162
+ }
1163
+
1164
+ let sample = "UILabel(frame: CGRect(x: 0, y: 0, width: 10, height: 10))"
1165
+ // badgeView.frame = CGRect(x: 0, y: 0, width: 10, height: 10)
1166
+ `;
1167
+
1168
+ assert.equal(hasSwiftUIKitManualFrameLayoutUsage(source), true);
1169
+ assert.deepEqual(collectSwiftUIKitManualFrameLayoutLines(source), [3, 6]);
1170
+ assert.equal(hasSwiftUIKitManualFrameLayoutUsage(safe), false);
1171
+ assert.deepEqual(collectSwiftUIKitManualFrameLayoutLines(safe), []);
1172
+ });
1173
+
1132
1174
  test('detectores de logging iOS detectan logs ad-hoc y PII en produccion', () => {
1133
1175
  const adHoc = `
1134
1176
  print(user.id)
@@ -2217,6 +2259,47 @@ final class BuyerOnboardingStringsTests: XCTestCase {
2217
2259
  assert.deepEqual(collectSwiftXCTUnwrapLines(brownfieldSpec), []);
2218
2260
  });
2219
2261
 
2262
+ test('hasSwiftMakeSUTWithoutMemoryTrackingUsage detecta specs con makeSUT sin trackForMemoryLeaks', () => {
2263
+ const source = `
2264
+ import XCTest
2265
+
2266
+ final class BuyerAuthScreenTests: XCTestCase {
2267
+ func testRendersTitle() {
2268
+ let sut = makeSUT()
2269
+ XCTAssertNotNil(sut)
2270
+ }
2271
+
2272
+ private func makeSUT() -> BuyerAuthScreen {
2273
+ BuyerAuthScreen()
2274
+ }
2275
+ }
2276
+ `;
2277
+ const safe = `
2278
+ import XCTest
2279
+
2280
+ final class BuyerAuthScreenTests: XCTestCase {
2281
+ func testRendersTitle() {
2282
+ let sut = makeSUT()
2283
+ XCTAssertNotNil(sut)
2284
+ }
2285
+
2286
+ private func makeSUT() -> BuyerAuthScreen {
2287
+ let sut = BuyerAuthScreen()
2288
+ trackForMemoryLeaks(sut)
2289
+ return sut
2290
+ }
2291
+ }
2292
+
2293
+ let sample = "makeSUT() without trackForMemoryLeaks"
2294
+ // private func makeSUT() -> Sample { Sample() }
2295
+ `;
2296
+
2297
+ assert.equal(hasSwiftMakeSUTWithoutMemoryTrackingUsage(source), true);
2298
+ assert.deepEqual(collectSwiftMakeSUTWithoutMemoryTrackingLines(source), [6, 10]);
2299
+ assert.equal(hasSwiftMakeSUTWithoutMemoryTrackingUsage(safe), false);
2300
+ assert.deepEqual(collectSwiftMakeSUTWithoutMemoryTrackingLines(safe), []);
2301
+ });
2302
+
2220
2303
  test('hasSwiftMixedTestingFrameworksUsage detecta mezcla XCTestCase y Testing/@Test', () => {
2221
2304
  const mixedSuite = `
2222
2305
  import XCTest
@@ -1701,6 +1701,17 @@ export const hasSwiftNSLayoutConstraintUsage = (source: string): boolean => {
1701
1701
  });
1702
1702
  };
1703
1703
 
1704
+ export const collectSwiftUIKitManualFrameLayoutLines = (source: string): readonly number[] => {
1705
+ const manualFramePattern =
1706
+ /\b(?:UIView|UIStackView|UILabel|UIButton|UIImageView|UITableView|UICollectionView|UIScrollView|UITextField|UITextView|UIViewController)\s*\(\s*frame\s*:\s*CGRect\s*\(|\.\s*frame\s*=\s*CGRect\s*\(/g;
1707
+
1708
+ return collectSwiftRegexLines(source, manualFramePattern);
1709
+ };
1710
+
1711
+ export const hasSwiftUIKitManualFrameLayoutUsage = (source: string): boolean => {
1712
+ return collectSwiftUIKitManualFrameLayoutLines(source).length > 0;
1713
+ };
1714
+
1704
1715
  export const hasSwiftUntypedNavigationLinkDestinationUsage = (source: string): boolean => {
1705
1716
  const swiftSource = sanitizeSwiftSourceForMultilineRegex(source);
1706
1717
  const destinationParameterPattern = /\bNavigationLink\s*\([^)]*\bdestination\s*:/;
@@ -1832,6 +1843,19 @@ const hasSwiftBrownfieldXCTestQualityPattern = (source: string): boolean => {
1832
1843
  return /\bmakeSUT\s*\(/.test(sanitized) && /\btrackForMemoryLeaks\s*\(/.test(sanitized);
1833
1844
  };
1834
1845
 
1846
+ export const collectSwiftMakeSUTWithoutMemoryTrackingLines = (source: string): readonly number[] => {
1847
+ const sanitized = sanitizeSwiftSourceForMultilineRegex(source);
1848
+ if (!/\bmakeSUT\s*\(/.test(sanitized) || /\btrackForMemoryLeaks\s*\(/.test(sanitized)) {
1849
+ return [];
1850
+ }
1851
+
1852
+ return collectSwiftRegexLines(source, /\bmakeSUT\s*\(/g);
1853
+ };
1854
+
1855
+ export const hasSwiftMakeSUTWithoutMemoryTrackingUsage = (source: string): boolean => {
1856
+ return collectSwiftMakeSUTWithoutMemoryTrackingLines(source).length > 0;
1857
+ };
1858
+
1835
1859
  export const hasSwiftLegacyXCTestImportUsage = (source: string): boolean => {
1836
1860
  if (!hasSwiftXCTestImportUsage(source)) {
1837
1861
  return false;
@@ -778,6 +778,7 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
778
778
  { 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.' },
779
779
  { 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.' },
780
780
  { 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.' },
781
+ { platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftUIKitManualFrameLayoutUsage, locateLines: TextIOS.collectSwiftUIKitManualFrameLayoutLines, primaryNode: (lines) => ({ kind: 'call', name: 'UIKit manual frame CGRect layout', lines }), relatedNodes: (lines) => [{ kind: 'call', name: 'replacement: Auto Layout constraints or SwiftUI relative layout', lines }], why: 'Manual UIKit frames hard-code geometry instead of using Auto Layout constraints or SwiftUI relative layout, so the layout cannot adapt reliably to devices, Dynamic Type or localization.', impact: 'Pixel-perfect work can regress across screens because fixed CGRect values bypass constraint solving and produce file-level ambiguity unless the exact frame node is reported.', expected_fix: 'Replace UIView(frame: CGRect(...)) and .frame = CGRect(...) layout with Auto Layout anchors/NSLayoutConstraint, UIStackView constraints, or SwiftUI relative layout where the screen is SwiftUI-first.', ruleId: 'heuristics.ios.uikit.manual-frame-layout.ast', code: 'HEURISTICS_IOS_UIKIT_MANUAL_FRAME_LAYOUT_AST', message: 'AST heuristic detected UIKit manual frame layout; use Auto Layout constraints or SwiftUI relative layout.' },
781
782
  { 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.' },
782
783
  { 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.' },
783
784
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftHardcodedSensitiveStringUsage, locateLines: TextIOS.collectSwiftHardcodedSensitiveStringLines, primaryNode: (lines) => ({ kind: 'property', name: 'hardcoded sensitive Swift string', lines }), relatedNodes: (lines) => [{ kind: 'property', name: 'replacement: Keychain, secure config or environment-specific secret source', lines }], why: 'Sensitive strings assigned directly to token/password/secret properties create static production secrets and cannot be rotated safely.', impact: 'Credentials or identifiers can leak through source, binaries, logs or screenshots and block release until the concrete assignment is removed.', expected_fix: 'Read sensitive values from Keychain, secure configuration, injected environment or a repository-approved secret provider. Keep user-facing copy in localization assets, not sensitive variables.', ruleId: 'heuristics.ios.security.hardcoded-sensitive-string.ast', code: 'HEURISTICS_IOS_SECURITY_HARDCODED_SENSITIVE_STRING_AST', message: 'AST heuristic detected hardcoded sensitive Swift string; Keychain, secure config or environment-specific secrets 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, 105);
6
+ assert.equal(iosRules.length, 106);
7
7
 
8
8
  const ids = iosRules.map((rule) => rule.id);
9
9
  assert.deepEqual(ids, [
@@ -35,6 +35,7 @@ test('iosRules define reglas heurísticas locked para plataforma ios', () => {
35
35
  'heuristics.ios.architecture.swinject.ast',
36
36
  'heuristics.ios.architecture.massive-view-controller.ast',
37
37
  'heuristics.ios.maintainability.magic-number-layout.ast',
38
+ 'heuristics.ios.uikit.manual-frame-layout.ast',
38
39
  'heuristics.ios.safety.non-iboutlet-iuo.ast',
39
40
  'heuristics.ios.logging.adhoc-print.ast',
40
41
  'heuristics.ios.logging.sensitive-data.ast',
@@ -155,6 +156,10 @@ test('iosRules define reglas heurísticas locked para plataforma ios', () => {
155
156
  byId.get('heuristics.ios.maintainability.warning-suppression.ast')?.then.code,
156
157
  'HEURISTICS_IOS_MAINTAINABILITY_WARNING_SUPPRESSION_AST'
157
158
  );
159
+ assert.equal(
160
+ byId.get('heuristics.ios.uikit.manual-frame-layout.ast')?.then.code,
161
+ 'HEURISTICS_IOS_UIKIT_MANUAL_FRAME_LAYOUT_AST'
162
+ );
158
163
  assert.equal(
159
164
  byId.get('heuristics.ios.swiftui.onchange-task.ast')?.then.code,
160
165
  'HEURISTICS_IOS_SWIFTUI_ONCHANGE_TASK_AST'
@@ -520,6 +520,25 @@ export const iosRules: RuleSet = [
520
520
  code: 'HEURISTICS_IOS_MAINTAINABILITY_MAGIC_NUMBER_LAYOUT_AST',
521
521
  },
522
522
  },
523
+ {
524
+ id: 'heuristics.ios.uikit.manual-frame-layout.ast',
525
+ description: 'Detects UIKit manual frame CGRect layout in iOS presentation code.',
526
+ severity: 'WARN',
527
+ platform: 'ios',
528
+ locked: true,
529
+ when: {
530
+ kind: 'Heuristic',
531
+ where: {
532
+ ruleId: 'heuristics.ios.uikit.manual-frame-layout.ast',
533
+ },
534
+ },
535
+ then: {
536
+ kind: 'Finding',
537
+ message:
538
+ 'AST heuristic detected UIKit manual frame layout; use Auto Layout constraints or SwiftUI relative layout.',
539
+ code: 'HEURISTICS_IOS_UIKIT_MANUAL_FRAME_LAYOUT_AST',
540
+ },
541
+ },
523
542
  {
524
543
  id: 'heuristics.ios.safety.non-iboutlet-iuo.ast',
525
544
  description: 'Detects implicitly unwrapped optionals outside IBOutlet wiring.',
@@ -167,6 +167,10 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
167
167
  heuristicDetector('ios.maintainability.magic-number-layout', [
168
168
  'heuristics.ios.maintainability.magic-number-layout.ast',
169
169
  ]),
170
+ 'skills.ios.guideline.ios.auto-layout-nslayoutconstraint': heuristicDetector(
171
+ 'ios.uikit.manual-frame-layout',
172
+ ['heuristics.ios.uikit.manual-frame-layout.ast']
173
+ ),
170
174
  'skills.ios.guideline.ios.prohibido-print-y-logs-ad-hoc': heuristicDetector(
171
175
  'ios.logging.adhoc-print',
172
176
  ['heuristics.ios.logging.adhoc-print.ast']
@@ -542,6 +542,14 @@ const hasWorktreeCodePlatforms = (params: {
542
542
  );
543
543
  };
544
544
 
545
+ const hasEffectiveChangedCodePlatforms = (params: {
546
+ changedPaths: ReadonlyArray<string>;
547
+ requiredPlatforms: ReadonlyArray<PreWriteSkillsPlatform>;
548
+ }): boolean =>
549
+ params.changedPaths.some((filePath) =>
550
+ params.requiredPlatforms.some((platform) => isPlatformPath(platform, filePath))
551
+ );
552
+
545
553
  const toLockRequiredPlatforms = (
546
554
  requiredLock: SkillsLockV1 | undefined
547
555
  ): ReadonlyArray<PreWriteSkillsPlatform> => {
@@ -792,6 +800,25 @@ const toSkillsContractAssessment = (params: {
792
800
  skillsEnforcement: SkillsEnforcementResolution;
793
801
  }): AiGateSkillsContractAssessment => {
794
802
  const requiredPlatforms = toLockRequiredPlatforms(params.requiredLock);
803
+ const effectiveChangedPaths = collectPreWriteEffectiveChangedPaths(params.repoRoot);
804
+ const hasEffectiveChangedPaths = effectiveChangedPaths.length > 0;
805
+ const hasEffectiveCodePlatforms = hasEffectiveChangedCodePlatforms({
806
+ changedPaths: effectiveChangedPaths,
807
+ requiredPlatforms,
808
+ });
809
+ const isNoCodeEffectiveScope =
810
+ requiredPlatforms.length > 0 && hasEffectiveChangedPaths && !hasEffectiveCodePlatforms;
811
+
812
+ if (isNoCodeEffectiveScope) {
813
+ return {
814
+ stage: params.stage,
815
+ enforced: false,
816
+ status: 'NOT_APPLICABLE',
817
+ detected_platforms: [],
818
+ requirements: [],
819
+ violations: [],
820
+ };
821
+ }
795
822
 
796
823
  if (params.evidenceResult.kind !== 'valid') {
797
824
  return {
@@ -841,7 +868,7 @@ const toSkillsContractAssessment = (params: {
841
868
  const explicitlyDetectedPlatforms = toDetectedSkillsPlatforms(params.evidenceResult.evidence.platforms);
842
869
  const inferredPlatforms = toCoverageInferredPlatforms(coverage);
843
870
  const repoTreeDetectedPlatforms =
844
- params.stage !== 'PRE_WRITE' && requiredPlatforms.length > 0
871
+ params.stage !== 'PRE_WRITE' && requiredPlatforms.length > 0 && !hasEffectiveChangedPaths
845
872
  ? toRepoTreeDetectedPlatforms({
846
873
  repoRoot: params.repoRoot,
847
874
  platforms: requiredPlatforms,
@@ -871,13 +898,16 @@ const toSkillsContractAssessment = (params: {
871
898
 
872
899
  if (requiredPlatforms.length > 0 && detectedPlatforms.length === 0) {
873
900
  if (
874
- params.stage === 'PRE_WRITE'
875
- && (
901
+ (
876
902
  pendingChanges === 0
877
- || !hasWorktreeCodePlatforms({
878
- repoRoot: params.repoRoot,
879
- requiredPlatforms,
880
- })
903
+ || !hasEffectiveCodePlatforms
904
+ || (
905
+ params.stage === 'PRE_WRITE'
906
+ && !hasWorktreeCodePlatforms({
907
+ repoRoot: params.repoRoot,
908
+ requiredPlatforms,
909
+ })
910
+ )
881
911
  )
882
912
  ) {
883
913
  return {
@@ -203,8 +203,12 @@ const isBroadFileLevelFinding = (finding: Finding): boolean => {
203
203
  if (findingLines.length <= BROAD_BROWNFIELD_FINDING_LINES_THRESHOLD) {
204
204
  return false;
205
205
  }
206
+ const nodeLines = [
207
+ ...normalizeFindingLines(finding.primary_node?.lines),
208
+ ...(finding.related_nodes ?? []).flatMap((node) => normalizeFindingLines(node.lines)),
209
+ ];
206
210
  const hasPreciseNode =
207
- typeof finding.primary_node !== 'undefined' || (finding.related_nodes?.length ?? 0) > 0;
211
+ nodeLines.length > 0 && nodeLines.length <= BROAD_BROWNFIELD_FINDING_LINES_THRESHOLD;
208
212
  return !hasPreciseNode;
209
213
  };
210
214
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.295",
3
+ "version": "6.3.297",
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-14T09:20:03.683Z",
4
+ "generatedAt": "2026-05-19T16:39:15.905Z",
5
5
  "bundles": [
6
6
  {
7
7
  "name": "android-guidelines",