pumuki 6.3.79 → 6.3.80

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/VERSION CHANGED
@@ -1 +1 @@
1
- v6.3.79
1
+ v6.3.80
@@ -6,11 +6,11 @@ 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-04-16 (v6.3.79)
9
+ ### 2026-04-17 (v6.3.80)
10
10
 
11
- - **Fix RuralGo (`PUMUKI-INC-078`)**: el parser de tracking canónico reconoce ya boards tabulares con la `🚧` en la primera columna, evitando falsos positivos `TRACKING_CANONICAL_IN_PROGRESS_INVALID` en `status` / `doctor`.
12
- - **Convergencia con guardrails del consumer**: `captureRepoState` y la consola de governance quedan alineados con el shape real de `docs/RURALGO_SEGUIMIENTO.md`, sin exigir reestructurar el tablero del repo consumidor.
13
- - **Rollout recomendado**: publicar `pumuki@6.3.79` y revalidar primero RuralGo (`pumuki status`, `pumuki doctor`) antes de extender el repin a otros consumers si aplica.
11
+ - **Tracking canónico**: la línea viva acepta el formato contractual `[🚧] - tarea` en el plan maestro y en tablas con backticks, tanto en el parser de tracking como en el guardrail shell de una sola task activa.
12
+ - **Notificaciones bloqueantes**: copy y remediaciones de `gate.blocked` salen en español cuando el payload legacy llega en inglés; el banner macOS corta por palabra y conserva una solución breve y accionable.
13
+ - **Rollout recomendado**: publicar `pumuki@6.3.80` y repin ordenado `RuralGo -> SAAS -> Flux`, revalidando después `pumuki status`, `pumuki doctor` y hooks Git en cada consumer.
14
14
 
15
15
  ### 2026-04-16 (v6.3.78)
16
16
 
@@ -15,8 +15,10 @@ const TRACKING_PRIORITY_SOURCE = new Set(['AGENTS.md']);
15
15
  const IN_PROGRESS_PATTERNS = [
16
16
  /^- Estado:\s*🚧/m,
17
17
  /^- 🚧 (\`?P[0-9A-Za-z.-]+\`?)/m,
18
+ /^\`?\[\s*🚧\s*\]\s*-\`?/m,
18
19
  /^\|\s*🚧(?:\s|\|)/m,
19
20
  /^\|[^|\n]+\|\s*🚧(?:\s|\|)/m,
21
+ /^\|[^|\n]+\|\s*\`?\[\s*🚧\s*\]\s*-\`?/m,
20
22
  ] as const;
21
23
 
22
24
  const toRepoRelativePath = (repoRoot: string, absolutePath: string): string => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.79",
3
+ "version": "6.3.80",
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": {
@@ -31,7 +31,7 @@ for FILE in "${FILES[@]}"; do
31
31
  fi
32
32
 
33
33
  if [[ "${FILE}" == "PUMUKI-RESET-MASTER-PLAN.md" ]]; then
34
- ACTIVE_PATTERN='^- Estado: 🚧'
34
+ ACTIVE_PATTERN='(^- Estado:\s*🚧\b)|(^`?\[\s*🚧\s*\]\s*-`?)|(^\|\s*[^|]+\|\s*`?\[\s*🚧\s*\]\s*-`?)'
35
35
  else
36
36
  ACTIVE_PATTERN='^- 🚧 (`?P[0-9A-Za-z.-]+`?)'
37
37
  fi
@@ -18,6 +18,8 @@ const BLOCKED_CAUSE_SUMMARY_BY_CODE: Readonly<Record<string, string>> = {
18
18
  OPENSPEC_MISSING: 'OpenSpec no está instalado en este repositorio.',
19
19
  MCP_ENTERPRISE_RECEIPT_MISSING: 'Falta el recibo enterprise de MCP.',
20
20
  BACKEND_AVOID_EXPLICIT_ANY: 'Se detectó uso de "any" explícito en backend.',
21
+ GIT_ATOMICITY_TOO_MANY_SCOPES: 'Se han cambiado demasiados scopes en el mismo commit.',
22
+ SOLID_HEURISTIC: 'Se detectó una violación estructural en el cambio actual.',
21
23
  };
22
24
 
23
25
  const toKnownSpanishCauseFromMessage = (message: string): string | null => {
@@ -31,6 +33,12 @@ const toKnownSpanishCauseFromMessage = (message: string): string | null => {
31
33
  if (normalized.includes('no upstream tracking reference')) {
32
34
  return BLOCKED_CAUSE_SUMMARY_BY_CODE.PRE_PUSH_UPSTREAM_MISSING;
33
35
  }
36
+ if (normalized.includes('too many scopes changed') || normalized.includes('atomicity budget exceeded')) {
37
+ return BLOCKED_CAUSE_SUMMARY_BY_CODE.GIT_ATOMICITY_TOO_MANY_SCOPES;
38
+ }
39
+ if (normalized.includes('heuristic violation')) {
40
+ return BLOCKED_CAUSE_SUMMARY_BY_CODE.SOLID_HEURISTIC;
41
+ }
34
42
  return null;
35
43
  };
36
44
 
@@ -22,7 +22,7 @@ export const buildGateBlockedPayload = (
22
22
  resolveBlockedCauseSummary(event, causeCode),
23
23
  72
24
24
  );
25
- const remediation = resolveBlockedRemediation(event, causeCode);
25
+ const remediation = resolveBlockedRemediation(event, causeCode, { variant: 'banner' });
26
26
  return {
27
27
  title: '🔴 Pumuki bloqueado',
28
28
  subtitle: `${projectPrefix}${event.stage} · ${causeSummary}`,
@@ -4,55 +4,101 @@ import {
4
4
  truncateNotificationText,
5
5
  } from './framework-menu-system-notifications-text';
6
6
 
7
+ type BlockedRemediationVariant = 'banner' | 'dialog';
8
+
7
9
  const BLOCKED_REMEDIATION_BY_CODE: Readonly<Record<string, string>> = {
8
- EVIDENCE_MISSING: 'Genera evidencia del slice actual y vuelve a validar el gate de esta fase.',
9
- EVIDENCE_INVALID: 'Regenera la evidencia de la iteración y repite la validación en el mismo stage.',
10
+ EVIDENCE_MISSING: 'Genera la evidencia del slice actual y vuelve a validar esta fase.',
11
+ EVIDENCE_INVALID: 'Regenera la evidencia de esta iteración y repite la validación.',
10
12
  EVIDENCE_CHAIN_INVALID: 'Regenera la evidencia para restaurar la cadena de integridad y vuelve a validar.',
11
- EVIDENCE_STALE: 'Ejecuta una auditoría completa de evidencia y vuelve a validar PRE_WRITE/PRE_PUSH. Si persiste, refresca la sesión SDD y reintenta.',
12
- EVIDENCE_BRANCH_MISMATCH: 'Regenera evidencia en esta rama y vuelve a ejecutar la validación para sincronizar branch y snapshot.',
13
- EVIDENCE_REPO_ROOT_MISMATCH: 'Regenera evidencia desde este repositorio y relanza la validación del gate.',
14
- PRE_PUSH_UPSTREAM_MISSING: 'Configura upstream con `git push --set-upstream origin <branch>` y vuelve a ejecutar PRE_PUSH.',
15
- SDD_SESSION_MISSING: 'Abre sesión SDD del change activo y repite la validación de la fase actual.',
16
- SDD_SESSION_INVALID: 'Refresca la sesión SDD (open/refresh) y vuelve a validar en el mismo stage.',
17
- OPENSPEC_MISSING: 'Instala OpenSpec en el repositorio y relanza la validación del gate.',
18
- MCP_ENTERPRISE_RECEIPT_MISSING: 'Genera el receipt enterprise de MCP y vuelve a ejecutar la validación.',
19
- BACKEND_AVOID_EXPLICIT_ANY: 'Reemplaza `any` por tipos concretos en backend y vuelve a lanzar el gate para confirmar el fix.',
13
+ EVIDENCE_STALE: 'Refresca la evidencia y vuelve a validar PRE_WRITE/PRE_PUSH.',
14
+ EVIDENCE_BRANCH_MISMATCH: 'Regenera la evidencia en esta rama y repite la validación.',
15
+ EVIDENCE_REPO_ROOT_MISMATCH: 'Regenera la evidencia desde este repositorio y vuelve a validar.',
16
+ PRE_PUSH_UPSTREAM_MISSING: 'Configura upstream con `git push --set-upstream origin <branch>` y repite PRE_PUSH.',
17
+ SDD_SESSION_MISSING: 'Abre la sesión SDD del change activo y repite la validación.',
18
+ SDD_SESSION_INVALID: 'Refresca la sesión SDD activa y vuelve a validar esta fase.',
19
+ OPENSPEC_MISSING: 'Instala OpenSpec en este repositorio y vuelve a validar el gate.',
20
+ MCP_ENTERPRISE_RECEIPT_MISSING: 'Genera el receipt enterprise de MCP y vuelve a validar.',
21
+ BACKEND_AVOID_EXPLICIT_ANY: 'Sustituye `any` por tipos concretos en backend y relanza el gate.',
22
+ GIT_ATOMICITY_TOO_MANY_SCOPES: 'Divide el cambio por scope o en commits más pequeños y vuelve a ejecutar el gate.',
23
+ SOLID_HEURISTIC: 'Corrige la violación detectada y vuelve a ejecutar el gate.',
24
+ };
25
+
26
+ const BLOCKED_REMEDIATION_MAX_LENGTH_BY_VARIANT: Readonly<Record<BlockedRemediationVariant, number>> = {
27
+ banner: 120,
28
+ dialog: 220,
20
29
  };
21
30
 
22
- const BLOCKED_REMEDIATION_MAX_LENGTH = 220;
31
+ const GENERIC_BLOCKED_REMEDIATION =
32
+ 'Corrige el bloqueo indicado y vuelve a ejecutar el comando.';
33
+
34
+ const normalizeBlockedRemediation = (value: string): string =>
35
+ normalizeNotificationText(value)
36
+ .replace(/^cómo solucionarlo:\s*/i, '')
37
+ .replace(/^remediation:\s*/i, '')
38
+ .replace(/^solution:\s*/i, '')
39
+ .replace(/^fix:\s*/i, '');
40
+
41
+ const resolveFallbackRemediation = (causeCode: string): string =>
42
+ BLOCKED_REMEDIATION_BY_CODE[causeCode] ?? GENERIC_BLOCKED_REMEDIATION;
43
+
44
+ const hasEnglishHints = (message: string): boolean => {
45
+ const normalized = message.toLowerCase();
46
+ return [
47
+ 'avoid explicit any',
48
+ 'set-upstream',
49
+ 'refresh evidence',
50
+ 'fix and retry',
51
+ 'split the change',
52
+ 'smaller commits',
53
+ 're-run',
54
+ 'rerun',
55
+ 'retry',
56
+ 'to continue',
57
+ 'run ',
58
+ ].some((hint) => normalized.includes(hint));
59
+ };
23
60
 
24
- const toKnownSpanishRemediationFromMessage = (message: string): string | null => {
61
+ const toKnownSpanishRemediationFromMessage = (message: string, causeCode: string): string | null => {
25
62
  const normalized = message.toLowerCase();
26
63
  if (normalized.includes('avoid explicit any')) {
27
64
  return BLOCKED_REMEDIATION_BY_CODE.BACKEND_AVOID_EXPLICIT_ANY;
28
65
  }
29
- if (normalized.includes('set-upstream')) {
66
+ if (normalized.includes('set-upstream') || normalized.includes('no upstream tracking reference')) {
30
67
  return BLOCKED_REMEDIATION_BY_CODE.PRE_PUSH_UPSTREAM_MISSING;
31
68
  }
32
- if (normalized.includes('refresh evidence')) {
69
+ if (normalized.includes('refresh evidence') || normalized.includes('evidence is stale')) {
33
70
  return BLOCKED_REMEDIATION_BY_CODE.EVIDENCE_STALE;
34
71
  }
72
+ if (normalized.includes('split the change')) {
73
+ return BLOCKED_REMEDIATION_BY_CODE.GIT_ATOMICITY_TOO_MANY_SCOPES;
74
+ }
75
+ if (normalized.includes('fix and retry') || normalized.includes('retry')) {
76
+ return resolveFallbackRemediation(causeCode);
77
+ }
35
78
  return null;
36
79
  };
37
80
 
38
81
  export const resolveBlockedRemediation = (
39
82
  event: Extract<PumukiCriticalNotificationEvent, { kind: 'gate.blocked' }>,
40
- causeCode: string
83
+ causeCode: string,
84
+ options?: {
85
+ variant?: BlockedRemediationVariant;
86
+ }
41
87
  ): string => {
88
+ const variant = options?.variant ?? 'dialog';
89
+ const maxLength = BLOCKED_REMEDIATION_MAX_LENGTH_BY_VARIANT[variant];
42
90
  const fromEvent = event.remediation
43
- ? normalizeNotificationText(event.remediation)
44
- .replace(/^cómo solucionarlo:\s*/i, '')
45
- .replace(/^remediation:\s*/i, '')
91
+ ? normalizeBlockedRemediation(event.remediation)
46
92
  : '';
47
93
  if (fromEvent.length > 0) {
48
- const translated = toKnownSpanishRemediationFromMessage(fromEvent);
94
+ const translated = toKnownSpanishRemediationFromMessage(fromEvent, causeCode);
49
95
  if (translated) {
50
- return truncateNotificationText(translated, BLOCKED_REMEDIATION_MAX_LENGTH);
96
+ return truncateNotificationText(translated, maxLength);
97
+ }
98
+ if (hasEnglishHints(fromEvent)) {
99
+ return truncateNotificationText(resolveFallbackRemediation(causeCode), maxLength);
51
100
  }
52
- return truncateNotificationText(fromEvent, BLOCKED_REMEDIATION_MAX_LENGTH);
101
+ return truncateNotificationText(fromEvent, maxLength);
53
102
  }
54
- const fallback =
55
- BLOCKED_REMEDIATION_BY_CODE[causeCode]
56
- ?? 'Corrige el bloqueo indicado y vuelve a ejecutar el comando.';
57
- return truncateNotificationText(fallback, BLOCKED_REMEDIATION_MAX_LENGTH);
103
+ return truncateNotificationText(resolveFallbackRemediation(causeCode), maxLength);
58
104
  };
@@ -7,7 +7,13 @@ export const truncateNotificationText = (value: string, maxLength: number): stri
7
7
  if (value.length <= maxLength) {
8
8
  return value;
9
9
  }
10
- return `${value.slice(0, Math.max(1, maxLength - 1)).trimEnd()}…`;
10
+ const limit = Math.max(1, maxLength - 1);
11
+ const trimmed = value.slice(0, limit).trimEnd();
12
+ const boundary = trimmed.lastIndexOf(' ');
13
+ const shortened = boundary >= Math.floor(limit * 0.6)
14
+ ? trimmed.slice(0, boundary).trimEnd()
15
+ : trimmed;
16
+ return `${shortened}…`;
11
17
  };
12
18
 
13
19
  export const resolveProjectLabel = (params: {