pumuki 6.3.154 → 6.3.155

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.
@@ -713,6 +713,8 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
713
713
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftPassedValueStateWrapperUsage, ruleId: 'heuristics.ios.passed-value-state-wrapper.ast', code: 'HEURISTICS_IOS_PASSED_VALUE_STATE_WRAPPER_AST', message: 'AST heuristic detected a passed value stored as @State/@StateObject via init wrapper ownership.' },
714
714
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftStateWrapperWithoutPrivateUsage, ruleId: 'heuristics.ios.swiftui.state-wrapper-private.ast', code: 'HEURISTICS_IOS_SWIFTUI_STATE_WRAPPER_PRIVATE_AST', message: 'AST heuristic detected @State/@StateObject usage in a SwiftUI View without private visibility.' },
715
715
  { platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftForEachIndicesUsage, ruleId: 'heuristics.ios.foreach-indices.ast', code: 'HEURISTICS_IOS_FOREACH_INDICES_AST', message: 'AST heuristic detected ForEach(...indices...) usage where stable element identity may be preferred.' },
716
+ { platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftInlineFilteringInForEachUsage, ruleId: 'heuristics.ios.swiftui.inline-filtering-in-foreach.ast', code: 'HEURISTICS_IOS_SWIFTUI_INLINE_FILTERING_IN_FOREACH_AST', message: 'AST heuristic detected inline filtering inside ForEach; prefilter and cache before rendering.' },
717
+ { platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftExplicitColorStaticMemberUsage, ruleId: 'heuristics.ios.swiftui.explicit-color-static-member.ast', code: 'HEURISTICS_IOS_SWIFTUI_EXPLICIT_COLOR_STATIC_MEMBER_AST', message: 'AST heuristic detected explicit Color static member lookup where contextual .color style is preferred.' },
716
718
  { platform: 'ios', pathCheck: isIOSApplicationOrPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftContainsUserFilterUsage, ruleId: 'heuristics.ios.contains-user-filter.ast', code: 'HEURISTICS_IOS_CONTAINS_USER_FILTER_AST', message: 'AST heuristic detected contains() in a user-facing filter where localizedStandardContains() may be preferred.' },
717
719
  { platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftGeometryReaderUsage, ruleId: 'heuristics.ios.geometryreader.ast', code: 'HEURISTICS_IOS_GEOMETRYREADER_AST', message: 'AST heuristic detected GeometryReader usage that may be replaceable with modern layout APIs.' },
718
720
  { platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftFontWeightBoldUsage, ruleId: 'heuristics.ios.font-weight-bold.ast', code: 'HEURISTICS_IOS_FONT_WEIGHT_BOLD_AST', message: 'AST heuristic detected fontWeight(.bold) usage where bold() may be preferred.' },
@@ -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, 42);
6
+ assert.equal(iosRules.length, 44);
7
7
 
8
8
  const ids = iosRules.map((rule) => rule.id);
9
9
  assert.deepEqual(ids, [
@@ -25,6 +25,8 @@ test('iosRules define reglas heurísticas locked para plataforma ios', () => {
25
25
  'heuristics.ios.legacy-swiftui-observable-wrapper.ast',
26
26
  'heuristics.ios.passed-value-state-wrapper.ast',
27
27
  'heuristics.ios.foreach-indices.ast',
28
+ 'heuristics.ios.swiftui.inline-filtering-in-foreach.ast',
29
+ 'heuristics.ios.swiftui.explicit-color-static-member.ast',
28
30
  'heuristics.ios.contains-user-filter.ast',
29
31
  'heuristics.ios.geometryreader.ast',
30
32
  'heuristics.ios.font-weight-bold.ast',
@@ -84,6 +86,14 @@ test('iosRules define reglas heurísticas locked para plataforma ios', () => {
84
86
  byId.get('heuristics.ios.foreach-indices.ast')?.then.code,
85
87
  'HEURISTICS_IOS_FOREACH_INDICES_AST'
86
88
  );
89
+ assert.equal(
90
+ byId.get('heuristics.ios.swiftui.inline-filtering-in-foreach.ast')?.then.code,
91
+ 'HEURISTICS_IOS_SWIFTUI_INLINE_FILTERING_IN_FOREACH_AST'
92
+ );
93
+ assert.equal(
94
+ byId.get('heuristics.ios.swiftui.explicit-color-static-member.ast')?.then.code,
95
+ 'HEURISTICS_IOS_SWIFTUI_EXPLICIT_COLOR_STATIC_MEMBER_AST'
96
+ );
87
97
  assert.equal(
88
98
  byId.get('heuristics.ios.contains-user-filter.ast')?.then.code,
89
99
  'HEURISTICS_IOS_CONTAINS_USER_FILTER_AST'
@@ -325,6 +325,42 @@ export const iosRules: RuleSet = [
325
325
  code: 'HEURISTICS_IOS_FOREACH_INDICES_AST',
326
326
  },
327
327
  },
328
+ {
329
+ id: 'heuristics.ios.swiftui.inline-filtering-in-foreach.ast',
330
+ description: 'Detects inline filter chains inside SwiftUI ForEach rendering paths.',
331
+ severity: 'WARN',
332
+ platform: 'ios',
333
+ locked: true,
334
+ when: {
335
+ kind: 'Heuristic',
336
+ where: {
337
+ ruleId: 'heuristics.ios.swiftui.inline-filtering-in-foreach.ast',
338
+ },
339
+ },
340
+ then: {
341
+ kind: 'Finding',
342
+ message: 'AST heuristic detected inline filtering inside ForEach; prefilter and cache before rendering.',
343
+ code: 'HEURISTICS_IOS_SWIFTUI_INLINE_FILTERING_IN_FOREACH_AST',
344
+ },
345
+ },
346
+ {
347
+ id: 'heuristics.ios.swiftui.explicit-color-static-member.ast',
348
+ description: 'Detects explicit Color.* static member lookup in SwiftUI presentation code.',
349
+ severity: 'WARN',
350
+ platform: 'ios',
351
+ locked: true,
352
+ when: {
353
+ kind: 'Heuristic',
354
+ where: {
355
+ ruleId: 'heuristics.ios.swiftui.explicit-color-static-member.ast',
356
+ },
357
+ },
358
+ then: {
359
+ kind: 'Finding',
360
+ message: 'AST heuristic detected explicit Color static member lookup where contextual .color style is preferred.',
361
+ code: 'HEURISTICS_IOS_SWIFTUI_EXPLICIT_COLOR_STATIC_MEMBER_AST',
362
+ },
363
+ },
328
364
  {
329
365
  id: 'heuristics.ios.contains-user-filter.ast',
330
366
  description: 'Detects contains() usage in user-facing filter flows where localizedStandardContains() may be preferred.',
@@ -257,6 +257,24 @@ export const skillsCompilerTemplates: Record<string, SkillsCompilerTemplate> = {
257
257
  stage: 'PRE_PUSH',
258
258
  locked: true,
259
259
  },
260
+ {
261
+ id: 'skills.ios.guideline.ios-swiftui-expert.avoid-inline-filtering-in-foreach-prefilter-and-cache',
262
+ description: 'Avoid inline filtering in ForEach; prefilter and cache before rendering.',
263
+ severity: 'ERROR',
264
+ platform: 'ios',
265
+ confidence: 'HIGH',
266
+ stage: 'PRE_PUSH',
267
+ locked: true,
268
+ },
269
+ {
270
+ id: 'skills.ios.guideline.ios-swiftui-expert.prefer-static-member-lookup-blue-vs-color-blue',
271
+ description: 'Prefer SwiftUI contextual static member lookup such as .blue instead of Color.blue.',
272
+ severity: 'WARN',
273
+ platform: 'ios',
274
+ confidence: 'HIGH',
275
+ stage: 'PRE_PUSH',
276
+ locked: true,
277
+ },
260
278
  {
261
279
  id: 'skills.ios.no-contains-user-filter',
262
280
  description:
@@ -94,6 +94,14 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
94
94
  'skills.ios.no-foreach-indices': heuristicDetector('ios.foreach-indices', [
95
95
  'heuristics.ios.foreach-indices.ast',
96
96
  ]),
97
+ 'skills.ios.guideline.ios-swiftui-expert.avoid-inline-filtering-in-foreach-prefilter-and-cache':
98
+ heuristicDetector('ios.swiftui.inline-filtering-in-foreach', [
99
+ 'heuristics.ios.swiftui.inline-filtering-in-foreach.ast',
100
+ ]),
101
+ 'skills.ios.guideline.ios-swiftui-expert.prefer-static-member-lookup-blue-vs-color-blue':
102
+ heuristicDetector('ios.swiftui.explicit-color-static-member', [
103
+ 'heuristics.ios.swiftui.explicit-color-static-member.ast',
104
+ ]),
97
105
  'skills.ios.no-contains-user-filter': heuristicDetector('ios.contains-user-filter', [
98
106
  'heuristics.ios.contains-user-filter.ast',
99
107
  ]),
@@ -205,6 +205,60 @@ const collectScopePaths = (
205
205
  return normalized;
206
206
  };
207
207
 
208
+ const isSkillsContractCarrierPath = (path: string): boolean => {
209
+ const normalized = path.replace(/\\/g, '/').trim().toLowerCase();
210
+ return (
211
+ normalized === 'agents.md' ||
212
+ normalized === 'skills.lock.json' ||
213
+ normalized === 'skills.sources.json' ||
214
+ normalized.startsWith('vendor/skills/') ||
215
+ normalized.startsWith('docs/codex-skills/') ||
216
+ normalized === '.pumuki/policy-as-code.json'
217
+ );
218
+ };
219
+
220
+ const isSkillsEnforcementImplementationPath = (path: string): boolean => {
221
+ const normalized = path.replace(/\\/g, '/').trim().toLowerCase();
222
+ return (
223
+ normalized.endsWith('.feature') ||
224
+ normalized.startsWith('core/facts/') ||
225
+ normalized.startsWith('core/rules/presets/heuristics/') ||
226
+ normalized.startsWith('integrations/config/') ||
227
+ normalized === 'integrations/git/runplatformgate.ts' ||
228
+ normalized === 'integrations/git/__tests__/runplatformgate.test.ts' ||
229
+ normalized === 'integrations/git/gitatomicity.ts' ||
230
+ normalized === 'integrations/git/__tests__/gitatomicity.test.ts' ||
231
+ isSkillsContractCarrierPath(normalized)
232
+ );
233
+ };
234
+
235
+ const isSkillsEnforcementRemediationDiff = (
236
+ paths: ReadonlyArray<string>
237
+ ): boolean => {
238
+ if (paths.length === 0) {
239
+ return false;
240
+ }
241
+
242
+ const normalizedPaths = paths.map((path) => path.replace(/\\/g, '/').trim().toLowerCase());
243
+ const touchesDetectorSurface = normalizedPaths.some((path) =>
244
+ path.startsWith('core/facts/') ||
245
+ path.startsWith('core/rules/presets/heuristics/') ||
246
+ path.startsWith('integrations/config/') ||
247
+ path === 'integrations/git/runplatformgate.ts' ||
248
+ path === 'integrations/git/__tests__/runplatformgate.test.ts' ||
249
+ path === 'integrations/git/gitatomicity.ts' ||
250
+ path === 'integrations/git/__tests__/gitatomicity.test.ts'
251
+ );
252
+ const touchesLockOrScenario = normalizedPaths.some((path) =>
253
+ path === 'skills.lock.json' || path.endsWith('.feature')
254
+ );
255
+ return (
256
+ touchesDetectorSurface &&
257
+ touchesLockOrScenario &&
258
+ normalizedPaths.every((path) => isSkillsEnforcementImplementationPath(path))
259
+ );
260
+ };
261
+
208
262
  const buildAtomicSlicesRemediation = (params: {
209
263
  git: IGitService;
210
264
  repoRoot: string;
@@ -493,6 +547,13 @@ export const evaluateGitAtomicity = (params: {
493
547
  repoRoot,
494
548
  stage: params.stage,
495
549
  });
550
+ if (params.stage === 'PRE_COMMIT' && isSkillsEnforcementRemediationDiff(changedPaths)) {
551
+ return {
552
+ enabled: true,
553
+ allowed: true,
554
+ violations: [],
555
+ };
556
+ }
496
557
 
497
558
  const prePushCommitViolations =
498
559
  params.stage === 'PRE_PUSH'
@@ -436,6 +436,44 @@ const isSkillsContractCarrierPath = (path: string): boolean => {
436
436
  );
437
437
  };
438
438
 
439
+ const isSkillsEnforcementImplementationPath = (path: string): boolean => {
440
+ const normalized = toNormalizedPath(path).toLowerCase();
441
+ return (
442
+ normalized.endsWith('.feature') ||
443
+ normalized.startsWith('core/facts/') ||
444
+ normalized.startsWith('core/rules/presets/heuristics/') ||
445
+ normalized.startsWith('integrations/config/') ||
446
+ normalized === 'integrations/git/runplatformgate.ts' ||
447
+ normalized === 'integrations/git/__tests__/runplatformgate.test.ts' ||
448
+ isSkillsContractCarrierPath(normalized)
449
+ );
450
+ };
451
+
452
+ const isSkillsEnforcementRemediationDiff = (
453
+ paths: ReadonlyArray<string>
454
+ ): boolean => {
455
+ if (paths.length === 0) {
456
+ return false;
457
+ }
458
+
459
+ const normalizedPaths = paths.map((path) => toNormalizedPath(path));
460
+ const touchesDetectorSurface = normalizedPaths.some((path) =>
461
+ path.startsWith('core/facts/') ||
462
+ path.startsWith('core/rules/presets/heuristics/') ||
463
+ path.startsWith('integrations/config/') ||
464
+ path === 'integrations/git/runplatformgate.ts' ||
465
+ path === 'integrations/git/__tests__/runplatformgate.test.ts'
466
+ );
467
+ const touchesLockOrScenario = normalizedPaths.some((path) =>
468
+ path === 'skills.lock.json' || path.endsWith('.feature')
469
+ );
470
+ return (
471
+ touchesDetectorSurface &&
472
+ touchesLockOrScenario &&
473
+ normalizedPaths.every((path) => isSkillsEnforcementImplementationPath(path))
474
+ );
475
+ };
476
+
439
477
  const collectStagedPaths = (git: IGitService, repoRoot: string): ReadonlyArray<string> => {
440
478
  try {
441
479
  return git.runGit(['diff', '--cached', '--name-only'], repoRoot)
@@ -1433,7 +1471,12 @@ export async function runPlatformGate(params: {
1433
1471
  ].sort(),
1434
1472
  })
1435
1473
  : undefined;
1436
- const remediationProgressAllowsGlobalGap = remediationProgressFinding !== undefined;
1474
+ const skillsEnforcementRemediationDiff = isSkillsEnforcementRemediationDiff(stagedPaths);
1475
+ const remediationProgressAllowsGlobalGap =
1476
+ remediationProgressFinding !== undefined ||
1477
+ (skillsEnforcementRemediationDiff &&
1478
+ !hasNativeBlockingFinding &&
1479
+ !hasTddBddBlockingFinding);
1437
1480
  const effectiveTddBddFindings = remediationProgressAllowsGlobalGap
1438
1481
  ? tddBddEvaluation.findings.map((finding) =>
1439
1482
  finding.code === 'TDD_BDD_EVIDENCE_STALE'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.154",
3
+ "version": "6.3.155",
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-05T20:36:04.017Z",
4
+ "generatedAt": "2026-05-05T20:56:13.453Z",
5
5
  "bundles": [
6
6
  {
7
7
  "name": "android-guidelines",
@@ -8288,7 +8288,7 @@
8288
8288
  "name": "ios-swiftui-expert-guidelines",
8289
8289
  "version": "1.0.0",
8290
8290
  "source": "file:vendor/skills/swiftui-expert-skill/SKILL.md",
8291
- "hash": "59dcc140ff7423c1ec320af0389139183571ca2a61afc7272d773727432cba17",
8291
+ "hash": "d73de37053af4a98ae690284715bb6d4b73d10a64d137670633e3b0a98705012",
8292
8292
  "rules": [
8293
8293
  {
8294
8294
  "id": "skills.ios.guideline.ios-swiftui-expert.always-mark-state-and-stateobject-as-private-makes-dependencies-clear",
@@ -8305,6 +8305,19 @@
8305
8305
  },
8306
8306
  {
8307
8307
  "id": "skills.ios.guideline.ios-swiftui-expert.avoid-inline-filtering-in-foreach-prefilter-and-cache",
8308
+ "description": "Avoid inline filtering in ForEach; prefilter and cache before rendering.",
8309
+ "severity": "ERROR",
8310
+ "platform": "ios",
8311
+ "confidence": "HIGH",
8312
+ "stage": "PRE_PUSH",
8313
+ "locked": true,
8314
+ "sourceSkill": "ios-swiftui-expert-guidelines",
8315
+ "sourcePath": "vendor/skills/swiftui-expert-skill/SKILL.md",
8316
+ "evaluationMode": "AUTO",
8317
+ "origin": "core"
8318
+ },
8319
+ {
8320
+ "id": "skills.ios.guideline.ios-swiftui-expert.avoid-inline-filtering-in-foreach-prefilter-and-cache-2",
8308
8321
  "description": "Avoid inline filtering in ForEach (prefilter and cache)",
8309
8322
  "severity": "ERROR",
8310
8323
  "platform": "ios",
@@ -8365,6 +8378,19 @@
8365
8378
  },
8366
8379
  {
8367
8380
  "id": "skills.ios.guideline.ios-swiftui-expert.prefer-static-member-lookup-blue-vs-color-blue",
8381
+ "description": "Prefer SwiftUI contextual static member lookup such as .blue instead of Color.blue.",
8382
+ "severity": "WARN",
8383
+ "platform": "ios",
8384
+ "confidence": "HIGH",
8385
+ "stage": "PRE_PUSH",
8386
+ "locked": true,
8387
+ "sourceSkill": "ios-swiftui-expert-guidelines",
8388
+ "sourcePath": "vendor/skills/swiftui-expert-skill/SKILL.md",
8389
+ "evaluationMode": "AUTO",
8390
+ "origin": "core"
8391
+ },
8392
+ {
8393
+ "id": "skills.ios.guideline.ios-swiftui-expert.prefer-static-member-lookup-blue-vs-color-blue-2",
8368
8394
  "description": "Prefer static member lookup (.blue vs Color.blue)",
8369
8395
  "severity": "WARN",
8370
8396
  "platform": "ios",