pumuki 6.3.317 → 6.3.318
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/package.json +1 -1
- package/scripts/framework-menu-system-notifications-macos-dialog-payload.ts +34 -0
- package/scripts/framework-menu-system-notifications-payloads-blocked.ts +28 -0
- package/scripts/framework-menu-system-notifications-remediation.ts +4 -2
- package/scripts/framework-menu-system-notifications-tracking.ts +24 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.318",
|
|
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": {
|
|
@@ -192,6 +192,24 @@ const isGoldenFlowCause = (
|
|
|
192
192
|
(cause.ruleId ?? '').startsWith('generic_tdd_') ||
|
|
193
193
|
(cause.ruleId ?? '').startsWith('generic_red_green_refactor');
|
|
194
194
|
|
|
195
|
+
const isBddScenarioMissingCause = (
|
|
196
|
+
cause: NonNullable<Extract<PumukiCriticalNotificationEvent, { kind: 'gate.blocked' }>['blockingCauses']>[number]
|
|
197
|
+
): boolean => {
|
|
198
|
+
const searchable = `${cause.code} ${cause.ruleId ?? ''} ${cause.message}`.toLowerCase();
|
|
199
|
+
return (
|
|
200
|
+
searchable.includes('tdd_bdd_scenario_file_missing') ||
|
|
201
|
+
searchable.includes('generic_bdd_feature_required') ||
|
|
202
|
+
searchable.includes('missing feature file')
|
|
203
|
+
);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const extractExpectedBddFeatureFile = (cause: NonNullable<Extract<PumukiCriticalNotificationEvent, { kind: 'gate.blocked' }>['blockingCauses']>[number]): string => {
|
|
207
|
+
const directFile = cause.file?.endsWith('.feature') ? cause.file : null;
|
|
208
|
+
const fileField = extractFieldFromCauseMessage(cause.message, 'file');
|
|
209
|
+
const missingFeature = cause.message.match(/missing feature file\s+([^\s.]+\.feature|[^\s]+\.feature)/i)?.[1];
|
|
210
|
+
return directFile ?? fileField ?? missingFeature ?? 'fichero .feature de la slice';
|
|
211
|
+
};
|
|
212
|
+
|
|
195
213
|
const isSkillCause = (
|
|
196
214
|
cause: NonNullable<Extract<PumukiCriticalNotificationEvent, { kind: 'gate.blocked' }>['blockingCauses']>[number]
|
|
197
215
|
): boolean =>
|
|
@@ -274,6 +292,15 @@ const formatBlockingCauseForDialog = (
|
|
|
274
292
|
` Falta: ${truncateNotificationText(formatGoldenFlowMissing(cause.message), 120)}`,
|
|
275
293
|
];
|
|
276
294
|
}
|
|
295
|
+
if (isBddScenarioMissingCause(cause)) {
|
|
296
|
+
const expectedFeatureFile = extractExpectedBddFeatureFile(cause);
|
|
297
|
+
return [
|
|
298
|
+
`${index + 1}. Causa bloqueante: escenario BDD sin fichero .feature`,
|
|
299
|
+
` Fichero esperado: ${truncateNotificationText(expectedFeatureFile, 120)}`,
|
|
300
|
+
' Falla: La slice referencia un escenario BDD, pero no existe su fichero .feature.',
|
|
301
|
+
' Solución: Crea ese .feature con el escenario de aceptación de la slice, o corrige la referencia si el id de tarea no es correcto.',
|
|
302
|
+
];
|
|
303
|
+
}
|
|
277
304
|
|
|
278
305
|
const causeLabel = isSkillCause(cause) ? 'Regla' : 'Causa bloqueante';
|
|
279
306
|
return [
|
|
@@ -295,6 +322,11 @@ const hasOnlyGoldenFlowCauses = (
|
|
|
295
322
|
): boolean =>
|
|
296
323
|
Boolean(causes?.length) && causes!.every(isGoldenFlowCause);
|
|
297
324
|
|
|
325
|
+
const hasOnlyBddScenarioMissingCauses = (
|
|
326
|
+
causes: Extract<PumukiCriticalNotificationEvent, { kind: 'gate.blocked' }>['blockingCauses']
|
|
327
|
+
): boolean =>
|
|
328
|
+
Boolean(causes?.length) && causes!.every(isBddScenarioMissingCause);
|
|
329
|
+
|
|
298
330
|
const resolveGoldenFlowDialogRemediation = (
|
|
299
331
|
causes: Extract<PumukiCriticalNotificationEvent, { kind: 'gate.blocked' }>['blockingCauses']
|
|
300
332
|
): string => {
|
|
@@ -314,6 +346,8 @@ export const buildBlockedDialogPayload = (params: {
|
|
|
314
346
|
? WORKTREE_HYGIENE_REMEDIATION
|
|
315
347
|
: hasOnlyGoldenFlowCauses(params.event.blockingCauses)
|
|
316
348
|
? resolveGoldenFlowDialogRemediation(params.event.blockingCauses)
|
|
349
|
+
: hasOnlyBddScenarioMissingCauses(params.event.blockingCauses)
|
|
350
|
+
? 'Crea el fichero .feature esperado para la slice activa o corrige la referencia de tarea. Después vuelve a intentar el commit.'
|
|
317
351
|
: params.event.blockingCauses && params.event.blockingCauses.length > 0
|
|
318
352
|
? 'Corrige las violaciones listadas y vuelve a intentar el commit.'
|
|
319
353
|
: resolveBlockedRemediation(params.event, causeCode);
|
|
@@ -38,6 +38,23 @@ const isGoldenFlowCause = (cause: BlockedCause): boolean => {
|
|
|
38
38
|
);
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
+
const isBddScenarioMissingCause = (cause: BlockedCause): boolean => {
|
|
42
|
+
const searchable = `${cause.code} ${cause.ruleId ?? ''} ${cause.message}`.toLowerCase();
|
|
43
|
+
return (
|
|
44
|
+
searchable.includes('tdd_bdd_scenario_file_missing') ||
|
|
45
|
+
searchable.includes('generic_bdd_feature_required') ||
|
|
46
|
+
searchable.includes('missing feature file')
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const extractExpectedBddFeatureFile = (cause: BlockedCause): string => {
|
|
51
|
+
const directFile = cause.file?.endsWith('.feature') ? cause.file : null;
|
|
52
|
+
const fieldMatch = cause.message.match(/\bfile=([^\s]+\.feature)\b/i)?.[1];
|
|
53
|
+
const missingFeature = cause.message.match(/missing feature file\s+([^\s.]+\.feature|[^\s]+\.feature)/i)?.[1];
|
|
54
|
+
const raw = directFile ?? fieldMatch ?? missingFeature ?? 'fichero .feature de la slice';
|
|
55
|
+
return raw.split('/').filter(Boolean).pop() ?? raw;
|
|
56
|
+
};
|
|
57
|
+
|
|
41
58
|
const normalizeGoldenFlowMissingToken = (message: string): string => {
|
|
42
59
|
const match = message.match(/missing=\[([^\]]+)\]/i);
|
|
43
60
|
return (match?.[1] ?? '').toUpperCase();
|
|
@@ -165,6 +182,11 @@ export const resolvePrioritizedBlockingCauses = (
|
|
|
165
182
|
if (leftGoldenFlow !== rightGoldenFlow) {
|
|
166
183
|
return leftGoldenFlow ? -1 : 1;
|
|
167
184
|
}
|
|
185
|
+
const leftBddScenarioMissing = isBddScenarioMissingCause(left);
|
|
186
|
+
const rightBddScenarioMissing = isBddScenarioMissingCause(right);
|
|
187
|
+
if (leftBddScenarioMissing !== rightBddScenarioMissing) {
|
|
188
|
+
return leftBddScenarioMissing ? -1 : 1;
|
|
189
|
+
}
|
|
168
190
|
return 0;
|
|
169
191
|
});
|
|
170
192
|
};
|
|
@@ -188,6 +210,9 @@ const buildBlockingCausesSummary = (
|
|
|
188
210
|
if (isGoldenFlowCause(first)) {
|
|
189
211
|
return `Violación del ciclo TDD${overflow}`;
|
|
190
212
|
}
|
|
213
|
+
if (isBddScenarioMissingCause(first)) {
|
|
214
|
+
return `BDD sin fichero .feature: ${extractExpectedBddFeatureFile(first)}${overflow}`;
|
|
215
|
+
}
|
|
191
216
|
return `${prefix}: ${formatCauseRule(first)} · ${formatCauseLocation(first)}${overflow}`;
|
|
192
217
|
};
|
|
193
218
|
|
|
@@ -216,6 +241,9 @@ const buildBlockingCausesRemediation = (
|
|
|
216
241
|
if (isGoldenFlowCause(first)) {
|
|
217
242
|
return formatGoldenFlowBannerRemediation(first);
|
|
218
243
|
}
|
|
244
|
+
if (isBddScenarioMissingCause(first)) {
|
|
245
|
+
return `Falta el fichero ${extractExpectedBddFeatureFile(first)}. Crea ese .feature con el escenario de aceptación de la slice, o corrige la referencia de tarea si no corresponde.`;
|
|
246
|
+
}
|
|
219
247
|
return `${formatCauseFix(first)} Revisa el reporte completo para el resto de causas bloqueantes.`;
|
|
220
248
|
};
|
|
221
249
|
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
truncateNotificationText,
|
|
5
5
|
} from './framework-menu-system-notifications-text';
|
|
6
6
|
import {
|
|
7
|
+
buildNotificationTrackingRemediation,
|
|
7
8
|
extractNotificationTrackingContext,
|
|
8
9
|
TRACKING_BLOCKED_REMEDIATION,
|
|
9
10
|
} from './framework-menu-system-notifications-tracking';
|
|
@@ -219,8 +220,9 @@ export const resolveBlockedRemediation = (
|
|
|
219
220
|
const fromEvent = event.remediation
|
|
220
221
|
? normalizeBlockedRemediation(event.remediation)
|
|
221
222
|
: '';
|
|
222
|
-
|
|
223
|
-
|
|
223
|
+
const trackingContext = extractNotificationTrackingContext(event.causeMessage);
|
|
224
|
+
if (trackingContext) {
|
|
225
|
+
return truncateNotificationText(buildNotificationTrackingRemediation(trackingContext), maxLength);
|
|
224
226
|
}
|
|
225
227
|
if (fromEvent.length > 0) {
|
|
226
228
|
if (
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type NotificationTrackingContext = {
|
|
2
2
|
activeEntry?: string;
|
|
3
3
|
trackingSource?: string;
|
|
4
|
+
line?: string;
|
|
4
5
|
};
|
|
5
6
|
|
|
6
7
|
const TRACKING_CONTEXT_PATTERN = /\b(active_entries=|tracking_source=|TRACKING_CANONICAL_)/u;
|
|
@@ -15,28 +16,46 @@ export const extractNotificationTrackingContext = (
|
|
|
15
16
|
.match(/\bactive_entries=([^,\s]+)/u)?.[1]
|
|
16
17
|
?.replace(/@L\d+$/u, '')
|
|
17
18
|
.trim();
|
|
19
|
+
const line = message.match(/\bline[_=]([0-9]+)\b/u)?.[1]?.trim();
|
|
18
20
|
const trackingSource = message.match(/\btracking_source=([^\s]+)/u)?.[1]?.trim();
|
|
19
21
|
return {
|
|
20
|
-
activeEntry: activeEntry && activeEntry.length > 0
|
|
22
|
+
activeEntry: activeEntry && activeEntry.length > 0 && !/^line_[0-9]+$/u.test(activeEntry)
|
|
23
|
+
? activeEntry
|
|
24
|
+
: undefined,
|
|
21
25
|
trackingSource: trackingSource && trackingSource.length > 0 ? trackingSource : undefined,
|
|
26
|
+
line: line && line.length > 0 ? line : undefined,
|
|
22
27
|
};
|
|
23
28
|
};
|
|
24
29
|
|
|
25
30
|
export const buildNotificationTrackingCauseSummary = (
|
|
26
31
|
context: NotificationTrackingContext
|
|
27
32
|
): string => {
|
|
33
|
+
const lineSuffix = context.line ? `, línea ${context.line}` : '';
|
|
28
34
|
if (context.activeEntry && context.trackingSource) {
|
|
29
|
-
return `Tracking bloqueado: ${context.activeEntry} en ${context.trackingSource}.`;
|
|
35
|
+
return `Tracking bloqueado: ${context.activeEntry} en ${context.trackingSource}${lineSuffix}.`;
|
|
30
36
|
}
|
|
31
37
|
if (context.activeEntry) {
|
|
32
|
-
return `Tracking bloqueado: ${context.activeEntry}.`;
|
|
38
|
+
return `Tracking bloqueado: ${context.activeEntry}${lineSuffix}.`;
|
|
33
39
|
}
|
|
34
40
|
if (context.trackingSource) {
|
|
35
|
-
return `Tracking bloqueado en ${context.trackingSource}.`;
|
|
41
|
+
return `Tracking bloqueado en ${context.trackingSource}${lineSuffix}.`;
|
|
42
|
+
}
|
|
43
|
+
if (context.line) {
|
|
44
|
+
return `Tracking bloqueado en la línea ${context.line}.`;
|
|
36
45
|
}
|
|
37
46
|
return 'El tracking canónico del repo bloquea la governance.';
|
|
38
47
|
};
|
|
39
48
|
|
|
40
49
|
export const TRACKING_BLOCKED_REMEDIATION =
|
|
41
|
-
'Corrige el MD de tracking: deja una única tarea activa válida y vuelve a
|
|
50
|
+
'Corrige el MD de tracking: deja una única tarea activa válida, elimina estados activos duplicados o inválidos y vuelve a intentar el commit.';
|
|
42
51
|
|
|
52
|
+
export const buildNotificationTrackingRemediation = (
|
|
53
|
+
context: NotificationTrackingContext
|
|
54
|
+
): string => {
|
|
55
|
+
const target = context.trackingSource
|
|
56
|
+
? `${context.trackingSource}${context.line ? `, línea ${context.line}` : ''}`
|
|
57
|
+
: context.line
|
|
58
|
+
? `la línea ${context.line} del MD de tracking`
|
|
59
|
+
: 'el MD de tracking';
|
|
60
|
+
return `Abre el MD de tracking ${target}. Deja una única tarea activa válida, elimina estados activos duplicados o inválidos y vuelve a intentar el commit.`;
|
|
61
|
+
};
|