pumuki 6.3.70 → 6.3.72

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.
Files changed (45) hide show
  1. package/AGENTS.md +269 -0
  2. package/CHANGELOG.md +666 -0
  3. package/README.md +32 -0
  4. package/docs/README.md +7 -2
  5. package/docs/operations/RELEASE_NOTES.md +15 -0
  6. package/docs/product/CONFIGURATION.md +12 -0
  7. package/docs/product/USAGE.md +24 -3
  8. package/docs/tracking/plan-curso-pumuki-stack-my-architecture.md +111 -0
  9. package/integrations/evidence/buildEvidence.ts +15 -0
  10. package/integrations/evidence/operationalHints.ts +110 -0
  11. package/integrations/evidence/schema.ts +16 -0
  12. package/integrations/evidence/writeEvidence.ts +3 -0
  13. package/integrations/gate/remediationCatalog.ts +40 -0
  14. package/integrations/git/GitService.ts +25 -0
  15. package/integrations/git/filterFactsByPathPrefixes.ts +61 -0
  16. package/integrations/git/runPlatformGate.ts +12 -4
  17. package/integrations/git/runPlatformGateFacts.ts +7 -0
  18. package/integrations/git/stageRunners.ts +82 -28
  19. package/integrations/lifecycle/cli.ts +32 -3
  20. package/integrations/lifecycle/doctor.ts +112 -0
  21. package/integrations/mcp/aiGateCheck.ts +2 -11
  22. package/integrations/mcp/preFlightCheck.ts +2 -1
  23. package/integrations/sdd/openSpecCli.ts +12 -3
  24. package/package.json +4 -1
  25. package/scripts/consumer-menu-matrix-baseline-report-lib.ts +13 -38
  26. package/scripts/framework-menu-consumer-actions-lib.ts +28 -4
  27. package/scripts/framework-menu-consumer-preflight-hints.ts +5 -2
  28. package/scripts/framework-menu-consumer-runtime-actions.ts +86 -6
  29. package/scripts/framework-menu-consumer-runtime-audit.ts +36 -2
  30. package/scripts/framework-menu-consumer-runtime-evidence-classic.ts +140 -0
  31. package/scripts/framework-menu-consumer-runtime-lib.ts +2 -0
  32. package/scripts/framework-menu-consumer-runtime-types.ts +3 -1
  33. package/scripts/framework-menu-evidence-summary-lib.ts +1 -0
  34. package/scripts/framework-menu-evidence-summary-read.ts +57 -5
  35. package/scripts/framework-menu-evidence-summary-severity.ts +3 -1
  36. package/scripts/framework-menu-evidence-summary-types.ts +7 -0
  37. package/scripts/framework-menu-gate-lib.ts +9 -0
  38. package/scripts/framework-menu-layout-data.ts +5 -0
  39. package/scripts/framework-menu-matrix-baseline-lib.ts +15 -14
  40. package/scripts/framework-menu-matrix-canary-lib.ts +22 -1
  41. package/scripts/framework-menu-matrix-evidence-lib.ts +1 -0
  42. package/scripts/framework-menu-matrix-evidence-types.ts +13 -1
  43. package/scripts/framework-menu-matrix-runner-lib.ts +35 -0
  44. package/scripts/framework-menu-system-notifications-macos.ts +4 -0
  45. package/scripts/framework-menu.ts +3 -0
package/README.md CHANGED
@@ -12,6 +12,12 @@ Pumuki gives engineering teams one deterministic execution model across local de
12
12
 
13
13
  `Facts -> Rules -> Gate -> .ai_evidence.json (v2.1)`
14
14
 
15
+ ## Qué NO es Pumuki
16
+
17
+ - **No** es el producto de negocio de tu repositorio (la app, el marketplace, el servicio que entregas a usuarios): es **gobernanza y contrato de entrega** sobre el código.
18
+ - **No** sustituye tests de dominio, contratos de API ni E2E: el gate ayuda a **cumplir políticas y evidencias**; la calidad funcional la define tu equipo con pruebas y criterios de aceptación.
19
+ - **No** garantiza por sí solo “un producto excelente”: sin reglas de equipo, revisión humana y criterios claros, solo obtienes **cumplimiento de lo que configuraste**.
20
+
15
21
  ## Who This README Is For
16
22
 
17
23
  | Profile | Use this path first |
@@ -20,6 +26,32 @@ Pumuki gives engineering teams one deterministic execution model across local de
20
26
  | Framework maintainers (this repo) | [Framework Maintainer Flow](#framework-maintainer-flow-this-repo) |
21
27
  | Platform/architecture owners | [Enterprise Operations Baseline](#enterprise-operations-baseline) |
22
28
 
29
+ ## Rutas de adopción
30
+
31
+ Elige un perfil y profundiza en los enlaces; **no** repite aquí reglas largas (skills, GitFlow, políticas) — están en `AGENTS.md` y en la documentación enlazada.
32
+
33
+ | Perfil | Qué instalar / arrancar | Stages habituales | Opcional típico |
34
+ |--------|-------------------------|-------------------|-----------------|
35
+ | **Mínimo** | `npm install --save-exact pumuki` (en repos Git el `postinstall` puede ejecutar `pumuki install` para hooks y lifecycle). | Hooks Git: **PRE_COMMIT**, **PRE_PUSH**; cadena **PRE_WRITE** cuando el hook lo encadena (según versión y config). | Evidencia [`.ai_evidence.json` v2.1](docs/mcp/ai-evidence-v2.1-contract.md); reglas core embebidas. |
36
+ | **Estándar** | Lo anterior + flujo **OpenSpec/SDD** bajo `openspec/` según tu política. | Lo anterior + validación SDD por stage (`pumuki sdd validate --stage=…`). | Sesiones SDD, cambios versionados bajo `openspec/changes/`. |
37
+ | **Enterprise completo** | `pumuki bootstrap --enterprise` (o equivalente documentado) + `skills.lock.json` / reglas custom / [policy-as-code](docs/product/CONFIGURATION.md) donde aplique. | Lo anterior + **CI** (`pumuki-ci`) y comprobaciones de alineación (`doctor`, parity). | [Skills / MCP](docs/mcp/mcp-servers-overview.md), `pumuki doctor --parity`, notificaciones, [hard mode](docs/product/CONFIGURATION.md). |
38
+
39
+ Referencias canónicas (profundizar aquí): [Instalación](docs/product/INSTALLATION.md), [Uso y gates](docs/product/USAGE.md), [Configuración](docs/product/CONFIGURATION.md), [AGENTS.md](AGENTS.md) (contrato agentes/skills/GitFlow en repos que lo adopten), [índice de documentación](docs/README.md).
40
+
41
+ Formación opcional (curso **Pumuki** dentro del hub *Stack My Architecture*): [https://stack-my-architecture-hub.vercel.app/pumuki/](https://stack-my-architecture-hub.vercel.app/pumuki/) · seguimiento de la iniciativa en [docs/tracking/plan-curso-pumuki-stack-my-architecture.md](docs/tracking/plan-curso-pumuki-stack-my-architecture.md).
42
+
43
+ ## Comandos esenciales
44
+
45
+ Cinco entradas que cubren el 80 % del día a día en un consumidor; el detalle está en los enlaces.
46
+
47
+ 1. **`npx pumuki doctor --json`** — Versión efectiva, drift, lifecycle, parity y avisos (p. ej. `pathExecutionHazard`). Detalle: [API_REFERENCE](docs/product/API_REFERENCE.md), [USAGE](docs/product/USAGE.md).
48
+ 2. **`npx pumuki status --json`** — Estado resumido del menú/lifecycle y alineación de versión. Detalle: [USAGE](docs/product/USAGE.md).
49
+ 3. **`npx pumuki install`** (o deja que el `postinstall` lo ejecute en Git) — Hooks y lifecycle en el repo. Detalle: [INSTALLATION](docs/product/INSTALLATION.md).
50
+ 4. **Gates locales** — `npx pumuki-pre-write`, `npx pumuki-pre-commit` (y `pumuki-pre-push` cuando toque push). Detalle: [USAGE](docs/product/USAGE.md), [Troubleshooting (USAGE)](docs/product/USAGE.md#troubleshooting).
51
+ 5. **SDD por stage (enterprise)** — `npx pumuki sdd validate --stage=PRE_COMMIT` (u otro stage). Detalle: [USAGE](docs/product/USAGE.md), [INSTALLATION](docs/product/INSTALLATION.md#troubleshooting) si falla el bootstrap.
52
+
53
+ Si algo bloquea o el mensaje no es claro: [Troubleshooting](#troubleshooting) (más abajo en este README), [USAGE § Troubleshooting](docs/product/USAGE.md#troubleshooting) y [GitHub Issues](https://github.com/SwiftEnProfundidad/ast-intelligence-hooks/issues).
54
+
23
55
  ## 5-Minute Quick Start (Consumer)
24
56
 
25
57
  Prerequisites:
package/docs/README.md CHANGED
@@ -4,6 +4,8 @@ Mapa corto y humano de la documentación oficial de Pumuki.
4
4
 
5
5
  ## Si buscas algo concreto
6
6
 
7
+ - Quiero límites del producto, perfil de adopción y comandos mínimos (sin leer todo el README largo):
8
+ - `README.md` (secciones **Qué NO es Pumuki**, **Rutas de adopción**, **Comandos esenciales**)
7
9
  - Quiero instalar y arrancar Pumuki en un repo consumidor:
8
10
  - `README.md`
9
11
  - `docs/product/INSTALLATION.md`
@@ -35,6 +37,8 @@ Mapa corto y humano de la documentación oficial de Pumuki.
35
37
 
36
38
  - Quiero saber en qué estamos ahora:
37
39
  - `docs/tracking/plan-activo-de-trabajo.md`
40
+ - Quiero el seguimiento del curso Stack My Architecture (Pumuki), iniciativa formativa aparte del espejo operativo:
41
+ - `docs/tracking/plan-curso-pumuki-stack-my-architecture.md`
38
42
 
39
43
  ## Estructura oficial
40
44
 
@@ -62,8 +66,9 @@ Mapa corto y humano de la documentación oficial de Pumuki.
62
66
 
63
67
  - `docs/tracking/`
64
68
  - Seguimiento permitido y solo el imprescindible.
65
- - Unica fuente de verdad: `docs/tracking/plan-activo-de-trabajo.md`
66
- - Regla hard: solo puede existir una tarea `🚧` en el plan activo.
69
+ - Espejo operativo de producto y consumidores: `docs/tracking/plan-activo-de-trabajo.md` (unica fuente de verdad para ese ambito).
70
+ - Curso Pumuki (Stack My Architecture): diseño pedagógico + seguimiento de entrega en `docs/tracking/plan-curso-pumuki-stack-my-architecture.md` (no sustituye al plan activo).
71
+ - Regla hard: solo puede existir una tarea `🚧` en cada documento de seguimiento que lo use.
67
72
 
68
73
  ## Fuera de `docs/`
69
74
 
@@ -6,6 +6,21 @@ 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-11 (v6.3.72)
10
+
11
+ - **Tarball npm**: `package.json` → `files` incluye `AGENTS.md`, `CHANGELOG.md` y `docs/tracking/plan-curso-pumuki-stack-my-architecture.md` para lectura canónica vía npm / jsDelivr sin depender solo del repo Git.
12
+ - **`gate.blocked` (macOS)**: banner de Notification Center **y** modal por defecto (evita cero notificaciones si el modal no llega a mostrarse desde un hook); dedupe opcional: `PUMUKI_MACOS_GATE_BLOCKED_BANNER_DEDUPE=1`.
13
+ - **Menú / matriz consumer**: opciones motor `11–14`, matriz baseline alineada, vista classic opcional, etc. (ver `CHANGELOG.md`).
14
+ - **Rollout**: `pumuki@6.3.72`; `pumuki doctor --json` + repin en consumidores (p. ej. RuralGO).
15
+
16
+ ### 2026-04-06 (v6.3.71)
17
+
18
+ - **Evidencia v2.1**: bloque `operational_hints` (`requires_second_pass`, resumen operativo, desglose por severidad de reglas). Alineado con PRE_COMMIT solo-docs + evidencia trackeada (INC-069) cuando no se re-stagea el JSON automáticamente.
19
+ - **Monorepo**: `PUMUKI_GATE_SCOPE_PATH_PREFIXES` acota el primer alcance de hechos por prefijos de ruta.
20
+ - **Paridad CI/local**: `pumuki doctor --parity` y fichero opcional `.pumuki/ci-parity-expected.json` (fallo con exit 1 si hay drift respecto al esperado).
21
+ - **MCP y hooks**: mismas pistas de remediación por código de violación vía catálogo compartido.
22
+ - **Rollout**: `pumuki@6.3.71`; repin en consumidores cuando se publique npm; validar hooks y `doctor --parity` si fijáis expectativas de CI.
23
+
9
24
  ### 2026-04-06 (v6.3.70)
10
25
 
11
26
  - **Consumidores con pre-commit en pre-push**: con `.ai_evidence.json` **versionado**, `PRE_PUSH` en **ALLOW/WARN** omite persistir en disco para no ensuciar el árbol tras el gate; variable `PUMUKI_PRE_PUSH_ALWAYS_WRITE_TRACKED_EVIDENCE=1` si necesitas el snapshot `PRE_PUSH` en fichero trackeado (puede exigir flujo de commit explícito).
@@ -317,6 +317,18 @@ Environment variables:
317
317
  - `PUMUKI_PREWRITE_WORKTREE_WARN_THRESHOLD` (default: `12`)
318
318
  - `PUMUKI_PREWRITE_WORKTREE_BLOCK_THRESHOLD` (default: `24`)
319
319
 
320
+ ## Alcance del gate por prefijos (monorepos)
321
+
322
+ - `PUMUKI_GATE_SCOPE_PATH_PREFIXES`: prefijos de ruta separados por **coma** o **punto y coma** (p. ej. `apps/backend,apps/web-app`). Se normalizan barras invertidas a `/`. El **primer** conjunto de hechos del alcance del stage (p. ej. staged o rango de commits) se filtra a rutas bajo esos prefijos. Hechos sin ruta de archivo reconocible (p. ej. algunas dependencias) se conservan para no romper reglas transversales.
323
+
324
+ ## Paridad local vs CI (`pumuki doctor`)
325
+
326
+ - Fichero opcional **`.pumuki/ci-parity-expected.json`** (commit en el repo consumer): JSON mínimo con los campos que quieras fijar, p. ej. `pumuki_package_version`, `pre_commit_policy_hash`, `pre_commit_policy_bundle`. Con `pumuki doctor --parity` (y opcionalmente `--json`), Pumuki calcula el perfil actual y, si existe el fichero esperado, rellena `parity_comparison`; cualquier desajuste hace **exit code 1**.
327
+
328
+ ## Evidencia en PRE_COMMIT con índice solo documentación
329
+
330
+ - `PUMUKI_PRE_COMMIT_ALWAYS_RESTAGE_TRACKED_EVIDENCE` (`1|true|yes`): fuerza el `git add` automático de `.ai_evidence.json` después de un `PRE_COMMIT` exitoso aunque el índice solo contenga rutas `*.md` / `*.mdx` (además de la propia evidencia). Por defecto, en ese alcance Pumuki **no** ensarta la evidencia en el commit: el snapshot se refresca en disco y puedes decidir si lo incluyes (`git add -- .ai_evidence.json`). Cubre el caso de commits puramente documentales sin mezclar un diff grande de evidencia (p. ej. informes upstream tipo PUMUKI-INC-069).
331
+
320
332
  ## Evidencia en PRE_PUSH con `.ai_evidence.json` trackeado
321
333
 
322
334
  - `PUMUKI_PRE_PUSH_ALWAYS_WRITE_TRACKED_EVIDENCE` (`1|true|yes`): fuerza la escritura del snapshot en `PRE_PUSH` aunque `.ai_evidence.json` esté versionado. Por defecto (sin esta variable), si el fichero está en el índice de git y el outcome no es `BLOCK`, Pumuki **no** muta el archivo para no romper hooks encadenados (p. ej. `pre-commit` ejecutado desde `pre-push`).
@@ -14,6 +14,11 @@ Production operations baseline (SLA/SLO, incident response and alerting):
14
14
  Visual walkthrough for menu Option 1 captures:
15
15
  - `docs/operations/framework-menu-consumer-walkthrough.md`
16
16
 
17
+ Stack My Architecture (optional training hub; includes the dedicated Pumuki course):
18
+
19
+ - Public static site: `https://stack-my-architecture-hub.vercel.app/pumuki/`
20
+ - Initiative tracking in this repo: `docs/tracking/plan-curso-pumuki-stack-my-architecture.md`
21
+
17
22
  ## Prerequisites
18
23
 
19
24
  - Node.js `>=18`
@@ -72,6 +77,8 @@ Platform activation:
72
77
 
73
78
  Pumuki enforces OpenSpec policy/session before allowing normal gate execution.
74
79
 
80
+ OpenSpec CLI resolution is **repo-local only**: Pumuki runs `openspec` from **`node_modules/.bin`** in the repository under check. A binary named `openspec` elsewhere on your **`PATH`** (global install, another tool) is **not** used. Add **`@fission-ai/openspec`** to the consumer project (for example via `pumuki install` / `bootstrap`) so laptops and CI behave the same; otherwise you may see **`OPENSPEC_MISSING`** even when `openspec --version` succeeds in an interactive shell.
81
+
75
82
  Minimal daily flow:
76
83
 
77
84
  ```bash
@@ -123,7 +130,10 @@ Use `A` to switch to `Advanced` mode (full options), and `C` to return to `Consu
123
130
  Advanced mode options include short inline contextual help.
124
131
  Consumer mode is now a minimal read-only shell:
125
132
 
126
- - `1/2/3/4` are the canonical read-only gate flows
133
+ - `1/2/3/4` are the canonical gate flows with **consumer preflight** before evaluation (labels state scope and PRE_COMMIT vs PRE_PUSH).
134
+ - `11/12/13/14` run the **engine** with **no preflight**: staged only, unstaged only (index→working tree + untracked), full working tree under **PRE_COMMIT**, or **all tracked files** (full repo). They write `.ai_evidence.json` on **PRE_COMMIT** engine runs like other menu audits.
135
+ - After each successful evidence read, the menu prints a **second panel** (“Classic evidence view”) with **ANSI-colored** enterprise/legacy severity counts, optional **platform** rows from `snapshot.platforms`, and a longer ranked violation list. Disable with `PUMUKI_MENU_VINTAGE_REPORT=0`.
136
+ - Options **2** and **4** (PRE_PUSH): if outcome is **PASS** or **WARN**, a short hint explains that a **tracked** `.ai_evidence.json` may **not** be rewritten on disk; use `PUMUKI_PRE_PUSH_ALWAYS_WRITE_TRACKED_EVIDENCE=1` for local debugging.
127
137
  - `8` exports the same evidence snapshot in markdown form
128
138
  - `5/6/7/9` remain available only as `Legacy Read-Only Diagnostics`
129
139
 
@@ -242,7 +252,7 @@ Stage mapping:
242
252
  If a scope is empty, the menu prints an explicit operational hint (`Scope vacío`), so `PASS` with zero findings is distinguishable from a clean repository scan.
243
253
 
244
254
  System notifications (macOS) can be enabled from advanced menu option `31` (persisted in `.pumuki/system-notifications.json`).
245
- On non-macOS platforms, the same payloads are written to **stderr** by default (visible in the terminal) because there is no native banner API. Set `PUMUKI_DISABLE_STDERR_NOTIFICATIONS=1` to silence that path (delivery reports `unsupported-platform` on those OSes). On macOS, set `PUMUKI_NOTIFICATION_STDERR_MIRROR=1` to duplicate **any** notification payload to stderr in addition to the system notification. For **`gate.blocked`** specifically, stderr mirroring is **on by default** when the macOS path reports success (so a failed push/commit still prints a `[pumuki]` block in the terminal even if the banner does not appear); disable only that default with `PUMUKI_DISABLE_GATE_BLOCKED_STDERR_MIRROR=1`. The **blocked modal** (Swift floating / AppleScript) with **Desactivar / Silenciar 30 min / Mantener activas** is **on by default** whenever notifications are enabled and `blockedDialogEnabled` is omitted in `.pumuki/system-notifications.json`. Turn it off with `"blockedDialogEnabled": false` or `PUMUKI_MACOS_BLOCKED_DIALOG=0`. Ensure the terminal app is allowed to show notifications in **System Settings → Notifications**.
255
+ On non-macOS platforms, the same payloads are written to **stderr** by default (visible in the terminal) because there is no native banner API. Set `PUMUKI_DISABLE_STDERR_NOTIFICATIONS=1` to silence that path (delivery reports `unsupported-platform` on those OSes). On macOS, set `PUMUKI_NOTIFICATION_STDERR_MIRROR=1` to duplicate **any** notification payload to stderr in addition to the system notification. For **`gate.blocked`** specifically, stderr mirroring is **on by default** when the macOS path reports success (so a failed push/commit still prints a `[pumuki]` block in the terminal even if the banner does not appear); disable only that default with `PUMUKI_DISABLE_GATE_BLOCKED_STDERR_MIRROR=1`. The **blocked modal** (Swift floating / AppleScript) with **Desactivar / Silenciar 30 min / Mantener activas** is **on by default** whenever notifications are enabled and `blockedDialogEnabled` is omitted in `.pumuki/system-notifications.json`. Turn it off with `"blockedDialogEnabled": false` or `PUMUKI_MACOS_BLOCKED_DIALOG=0`. **`gate.blocked`** now emits **both** the Notification Center banner (`osascript`) **and** the interactive modal by default, so consumers (e.g. hooks in other repos) still get a visible banner if the modal cannot attach to a GUI session. To restore the previous “modal only” behaviour and suppress the duplicate banner, set `PUMUKI_MACOS_GATE_BLOCKED_BANNER_DEDUPE=1`. If you see **no** notifications at all: confirm `PUMUKI_DISABLE_SYSTEM_NOTIFICATIONS` is unset, delete or fix `.pumuki/system-notifications.json` (absent file defaults to enabled), and ensure the terminal app is allowed to show notifications in **System Settings → Notifications**.
246
256
  Blocked notifications now use a native Swift floating modal (bottom-right) by default, with AppleScript fallback.
247
257
  Override mode with `PUMUKI_MACOS_BLOCKED_DIALOG_MODE=auto|swift-floating|applescript`.
248
258
  Custom skills import is available in advanced menu option `33` (writes `/.pumuki/custom-rules.json`).
@@ -287,6 +297,8 @@ npx --yes pumuki install --with-mcp --agent=codex
287
297
  npx --yes pumuki doctor
288
298
  # include deterministic adapter/mcp wiring health checks
289
299
  npx --yes pumuki doctor --deep --json
300
+ # parity profile vs .pumuki/ci-parity-expected.json (CI/local alignment)
301
+ npx --yes pumuki doctor --parity --json
290
302
 
291
303
  # show lifecycle status
292
304
  npx --yes pumuki status
@@ -563,6 +575,7 @@ npx --yes tsx@4.21.0 scripts/reconcile-consumer-backlog-issues.ts \
563
575
 
564
576
  OpenSpec integration behavior:
565
577
  - `pumuki bootstrap --enterprise --agent=<name>` orquesta `install + adapter wiring + doctor --deep` en un solo paso.
578
+ - SDD/OpenSpec enforcement invokes the CLI only from **`{repo}/node_modules/.bin/openspec`** (no fallback to a generic `openspec` on `PATH`).
566
579
  - `pumuki install` auto-bootstraps OpenSpec (`@fission-ai/openspec`) when missing/incompatible and scaffolds `openspec/` project baseline when absent.
567
580
  - `pumuki install --with-mcp` adds adapter/MCP wiring bootstrap and prints MCP health summary on completion.
568
581
  - `pumuki update --latest` migrates legacy `openspec` package to `@fission-ai/openspec` before hook reinstall.
@@ -671,7 +684,9 @@ npm run toolkit:clean-artifacts -- --dry-run
671
684
 
672
685
  - Reads staged changes with `git diff --cached --name-status`.
673
686
  - Builds facts from staged content.
687
+ - Opcional: `PUMUKI_GATE_SCOPE_PATH_PREFIXES` acota el primer alcance de hechos a prefijos del monorepo (ver `docs/product/CONFIGURATION.md`).
674
688
  - Requires valid SDD/OpenSpec status (session + active change + validation).
689
+ - Si `.ai_evidence.json` está **versionado** y el hook refresca el snapshot en disco tras un gate **no** bloqueante, por defecto Pumuki vuelve a hacer `git add` de ese fichero **salvo** que lo único en el índice (ignorando `.ai_evidence.json` / `.AI_EVIDENCE.json`) sean rutas de documentación (`*.md`, `*.mdx`). En ese caso la evidencia se actualiza en disco pero **no** se ensarta en el commit: puedes añadirla manualmente (`git add -- .ai_evidence.json`) o activar el comportamiento anterior con `PUMUKI_PRE_COMMIT_ALWAYS_RESTAGE_TRACKED_EVIDENCE=1` (ver `docs/product/CONFIGURATION.md`). En stderr (si no va en modo silencioso) verás un aviso `[pumuki][evidence-sync]`.
675
690
 
676
691
  ### PRE_PUSH
677
692
 
@@ -711,11 +726,15 @@ Cada ejecución del gate escribe evidencia determinista en:
711
726
 
712
727
  - `.ai_evidence.json`
713
728
 
714
- Excepción: en `PRE_PUSH`, si el fichero está trackeado y el outcome no es `BLOCK`, la escritura al path anterior se omite (ver sección PRE_PUSH arriba). La telemetría interna del gate sigue generándose; solo se evita mutar el árbol de trabajo.
729
+ Excepciones:
730
+
731
+ - En `PRE_PUSH`, si el fichero está trackeado y el outcome no es `BLOCK`, la escritura al path anterior se omite (ver sección PRE_PUSH arriba). La telemetría interna del gate sigue generándose; solo se evita mutar el árbol de trabajo.
732
+ - En `PRE_COMMIT`, si el fichero está trackeado y el índice es solo documentación (`*.md` / `*.mdx`), el fichero puede actualizarse en disco sin auto-`git add` (ver sección PRE_COMMIT arriba).
715
733
 
716
734
  Schema and behavior:
717
735
 
718
736
  - `version: "2.1"` is the source of truth
737
+ - `operational_hints` (opcional pero recomendado en runs recientes): `requires_second_pass` (boolean), `second_pass_reason` (string o null), `human_summary_lines` (1–4 líneas legibles), `rule_execution_breakdown` (conteos evaluated / blocking / warn / info / skipped out-of-scope). Útil para tooling y para entender el resultado sin abrir todo el snapshot.
719
738
  - `snapshot` + `ledger`
720
739
  - `platforms` and `rulesets` tracking
721
740
  - `snapshot.sdd_metrics` tracks stage-level SDD enforcement metadata
@@ -798,6 +817,8 @@ npx --yes pumuki sdd status
798
817
  npx --yes pumuki sdd validate --stage=PRE_COMMIT
799
818
  ```
800
819
 
820
+ If the gate reports **`OPENSPEC_MISSING`** but `openspec --version` works in the terminal, you are likely using a **global** OpenSpec install. Pumuki does not use that path; install the package in the repo (`npm install`, `pumuki install`, or `pumuki bootstrap`) so `node_modules/.bin/openspec` exists.
821
+
801
822
  Open or refresh session if needed:
802
823
 
803
824
  ```bash
@@ -0,0 +1,111 @@
1
+ # Diseño pedagógico y seguimiento — Curso Pumuki (Stack My Architecture)
2
+
3
+ Este documento define **qué debe aprender una persona**, **en qué orden**, **cómo se comprueba** y **qué confusiones hay que romper**. El seguimiento de repos y build va **al final**; no sustituye el diseño.
4
+
5
+ No sustituye el espejo de producto en [`plan-activo-de-trabajo.md`](./plan-activo-de-trabajo.md).
6
+
7
+ ## Leyenda operativa
8
+
9
+ - ✅ Entregado en repo curso + coherente con USAGE/INSTALLATION
10
+ - 🚧 Única tarea activa (máx. 1)
11
+ - ⏳ Pendiente
12
+ - ⛔ Bloqueado
13
+
14
+ ---
15
+
16
+ ## 1. Para quién es y qué problema resuelve
17
+
18
+ **Quién:** desarrollador o tech lead que debe **vivir** con hooks, CI y (a veces) agentes; no auditor de slides.
19
+
20
+ **Problema:** en la práctica mezcla *SDD*, *gate*, *MCP*, *evidencia* y *menú*; cuando algo bloquea no sabe **qué capa** falló ni **qué comando** la inspecciona.
21
+
22
+ **Éxito del curso:** tras cerrarlo, en un repo real puede (1) nombrar el **stage** y el **scope** de un fallo, (2) leer **`.ai_evidence.json`** y relacionarlo con stderr, (3) distinguir **enforcement** (hooks/CI) de **lectura/agente** (MCP), (4) ejecutar el **flujo SDD mínimo** sin confundirlo con “arreglar reglas”, (5) instalar, actualizar o **retirar** Pumuki sin dejar hooks huérfanos.
23
+
24
+ Si eso no se cumple, el curso falla aunque el HTML sea bonito.
25
+
26
+ ---
27
+
28
+ ## 2. Modelo mental único (todo el curso orbita aquí)
29
+
30
+ Una sola frase que el alumno debe poder repetir:
31
+
32
+ **Hechos del diff/código → reglas efectivas → decisión del gate por stage → registro en evidencia v2.1.**
33
+
34
+ Todo lo demás (menú, MCP, notificaciones, `doctor`) es **acceso** a ese mismo pipeline o **comodidad**; no es un segundo producto.
35
+
36
+ ---
37
+
38
+ ## 3. Confusiones que el material debe desmontar de forma explícita
39
+
40
+ Cada unidad del curso debe tener al menos un párrafo “**no confundas**” o un mini escenario:
41
+
42
+ | Confusión típica | Verdad operativa |
43
+ |------------------|------------------|
44
+ | “Sin MCP no hay Pumuki” | Los **hooks y CI** son el enforcement; MCP es **opcional** para IDE/agente. |
45
+ | “SDD es lo mismo que el gate” | SDD/OpenSpec gobierna el **cambio**; el gate evalúa **código + política + evidencia** en un **scope** concreto. |
46
+ | “`openspec` en el PATH basta” | Pumuki usa OpenSpec **repo-local** (`node_modules/.bin`). |
47
+ | “PRE_COMMIT limpio ⇒ push seguro” | **PRE_PUSH** mira otro **scope** (`upstream..HEAD`). |
48
+ | “El menú es la fuente de verdad” | El menú **orquesta** diagnósticos; la fuente de verdad es **policy + stage + evidencia**. |
49
+ | “Borrar el paquete limpia el repo” | `npm uninstall pumuki` **no** quita hooks/lifecycle; hace falta **`pumuki remove`** / flujo documentado. |
50
+
51
+ Si el Markdown del curso no nombra estas líneas, el plan pedagógico sigue vacío aunque haya tablas de check.
52
+
53
+ ---
54
+
55
+ ## 4. Secuencia didáctica (orden de primera lectura)
56
+
57
+ El repo del curso incluye la **columna vertebral en prosa** `00-preparacion/03-recorrido-cero-a-cien-pumuki.md` (0→100 % sin saltos) y el **proyecto guiado** `02-modulos/13-proyecto-guiado-de-la-a-la-z.md` (fases A–M con criterios de hecho). Esta tabla U0–U10 sigue siendo el contrato de **outcomes**; esas piezas son el **relato** y el **laboratorio** que los materializan.
58
+
59
+ El **mapa completo** (`00-mapa-completo-del-producto.md`) es **referencia** para quien ya perdió el hilo; la **primera pasada** debe seguir esta secuencia para no cargar conceptos antes de tiempo.
60
+
61
+ | Orden | Unidad | Objetivo observable (el alumno demuestra…) | Actividad mínima en lab | Criterio de dominio |
62
+ |-------|--------|---------------------------------------------|-------------------------|----------------------|
63
+ | U0 | Preparación + versión + lab | Que existe documentación canónica y un repo donde ensayar | Abrir USAGE e INSTALLATION; fijar versión mínima `pumuki` | Explica en una frase dónde miente un “funciona en mi máquina” sin `doctor` |
64
+ | U1 | Contrato + stages + cobertura | Qué pregunta **cada** stage y qué es `unevaluated_rule_ids` | Un `pre-commit` / `pre-push` y leer evidencia | Predice scope antes de ejecutar y acierta al comparar con JSON |
65
+ | U2 | Instalación y primer verde | Postinstall, `bootstrap` vs `install`, `pathExecutionHazard` | `doctor --json` antes/después de un cambio reversible | Usa `alignmentCommand` o workaround sin improvisar |
66
+ | U3 | Ciclo de vida completo | Diferencia `npm uninstall`, `pumuki uninstall`, `pumuki remove`, `update --latest` | Simular retirada en rama de prueba | Lista qué queda en disco tras cada comando |
67
+ | U4 | Evidencia | Dónde se escribe, cuándo **no** se reescribe (PRE_PUSH trackeado), restage PRE_COMMIT | Diff de `.ai_evidence.json` entre stages | Explica un caso “hook modificó archivo” sin pánico |
68
+ | U5 | Menú interactivo | Consumer vs Advanced; 1–4/8/9; matrix; variables UI | Correr `pumuki-framework` y una opción de cada familia | Enlaza opción de menú con **stage** y **scope** equivalentes |
69
+ | U6 | MCP | Evidence vs enterprise; HTTP vs stdio; recibo PRE_WRITE | Levantar o inspeccionar config en `.pumuki/adapter.json` | Explica por qué el gate puede pasar con MCP caído |
70
+ | U7 | SDD/OpenSpec | Flujo diario: `sdd status`, `session`, `validate`; catálogo de códigos | Abrir sesión de cambio y validar un stage | Clasifica un JSON de bloqueo en “falta OpenSpec” vs “falta sesión” |
71
+ | U8 | Notificaciones y watch | macOS vs stderr; `system-notifications.json`; anti-spam de `watch` | Una sesión `watch --once` o documentada | Elige variable correcta para silenciar o espejar |
72
+ | U9 | Perfiles, Governance, monorepo | Cuándo bajar de enterprise; prefijos de scope; parity | (Según perfil del equipo) | Justifica un perfil sin autoboicot |
73
+ | U10 | Cierre | Checklist operativa propia del equipo | Checklist escrita a partir del curso | Puede enseñar el modelo mental §2 a otra persona en 3 minutos |
74
+
75
+ **Regla:** ninguna fila U1–U10 puede considerarse “hecha” solo con un enlace a USAGE; debe existir **narrativa + comando + actividad + criterio** en el Markdown del curso.
76
+
77
+ ---
78
+
79
+ ## 5. Qué el curso no debe intentar (límites)
80
+
81
+ - Sustituir el curso **SDD** (OpenSpec, Kanban del cambio) ni el **Governance** (AGENTS, cultura): solo **enganchar** Pumuki a ellos.
82
+ - Sustituir tests de producto, revisión humana ni criterios de negocio.
83
+ - Prometer “cero lectura de USAGE”: USAGE sigue siendo norma; el curso debe **enseñar** lo mismo con **menor fricción**, no duplicar mal.
84
+
85
+ ---
86
+
87
+ ## 6. Estado de entrega en el repo (operativo, subordinado al §4)
88
+
89
+ | Unidad §4 | Estado en `stack-my-architecture-pumuki` |
90
+ |-----------|-------------------------------------------|
91
+ | U0–U2 | ✅ Base + módulo 2; ciclo de vida **completo** en **08** |
92
+ | U3 | ✅ `02-modulos/08-ciclo-de-vida-install-uninstall-actualizacion.md` |
93
+ | U4 | ✅ `02-modulos/09-evidencia-por-stage-y-ai-evidence-json.md` |
94
+ | U5 | ✅ `02-modulos/10-menu-interactivo-matrix-y-preflight.md` |
95
+ | U6 | ✅ `02-modulos/11-mcp-enforcement-vs-lectura-agente.md` |
96
+ | U7 | ✅ Módulo **04** ampliado (§4.6–4.8 + criterio dominio) |
97
+ | U8 | ✅ `02-modulos/12-notificaciones-macos-stderr-y-watch.md` |
98
+ | U9–U10 | ✅ Revisar checklist **07** frente a criterios §4 en próxima iteración |
99
+
100
+ | Task | Estado |
101
+ |------|--------|
102
+ | Implementar U3–U8 en `.md` + `FILE_ORDER` + validación | ✅ |
103
+ | CHANGELOG curso + rebuild HTML + sync hub local | ✅ (incl. CSS lectura **0.3.1**) |
104
+ | Push hub + deploy Vercel (ver curso en prod) | 🚧 |
105
+
106
+ ---
107
+
108
+ ## 7. Auditoría técnica (no confundir con evaluación del alumno)
109
+
110
+ - `python3 scripts/validate-course-structure.py` y `check-links.py` en repo curso.
111
+ - Publicación hub según tu script; URL `/pumuki/` 200.
@@ -7,6 +7,7 @@ import type {
7
7
  ConsolidationSuppressedFinding,
8
8
  CompatibilityViolation,
9
9
  EvidenceLines,
10
+ EvidenceOperationalHints,
10
11
  HumanIntentState,
11
12
  LedgerEntry,
12
13
  PlatformState,
@@ -23,6 +24,7 @@ import { buildSnapshotPlatformSummaries } from './platformSummary';
23
24
  import { resolveHumanIntent } from './humanIntent';
24
25
  import { normalizeSnapshotEvaluationMetrics } from './evaluationMetrics';
25
26
  import { normalizeSnapshotRulesCoverage } from './rulesCoverage';
27
+ import { buildEvidenceOperationalHints } from './operationalHints';
26
28
 
27
29
  type BuildFindingInput = Finding & {
28
30
  file?: string;
@@ -50,6 +52,9 @@ export type BuildEvidenceParams = {
50
52
  };
51
53
  sddMetrics?: SddMetrics;
52
54
  repoState?: RepoState;
55
+ operationalHintsExtra?: Partial<
56
+ Pick<EvidenceOperationalHints, 'requires_second_pass' | 'second_pass_reason'>
57
+ >;
53
58
  };
54
59
 
55
60
  const normalizeLines = (lines?: EvidenceLines): EvidenceLines | undefined => {
@@ -766,9 +771,19 @@ export function buildEvidence(params: BuildEvidenceParams): AiEvidenceV2_1 {
766
771
  previousEvidence: params.previousEvidence,
767
772
  });
768
773
 
774
+ const operational_hints = buildEvidenceOperationalHints({
775
+ stage: params.stage,
776
+ outcome,
777
+ findings: normalizedFindings,
778
+ rulesCoverage: normalizedRulesCoverage,
779
+ evaluationMetrics: normalizedEvaluationMetrics,
780
+ extra: params.operationalHintsExtra,
781
+ });
782
+
769
783
  return {
770
784
  version: '2.1',
771
785
  timestamp: now,
786
+ operational_hints,
772
787
  snapshot: {
773
788
  stage: params.stage,
774
789
  audit_mode: params.auditMode ?? 'gate',
@@ -0,0 +1,110 @@
1
+ import type { GateOutcome } from '../../core/gate/GateOutcome';
2
+ import type { GateStage } from '../../core/gate/GateStage';
3
+ import { resolveRemediationHintOrDefault } from '../gate/remediationCatalog';
4
+ import type {
5
+ EvidenceOperationalHints,
6
+ EvidenceRuleExecutionBreakdown,
7
+ SnapshotEvaluationMetrics,
8
+ SnapshotFinding,
9
+ SnapshotRulesCoverage,
10
+ } from './schema';
11
+
12
+ const truncate = (value: string, max: number): string => {
13
+ const t = value.trim();
14
+ if (t.length <= max) {
15
+ return t;
16
+ }
17
+ return `${t.slice(0, max - 1)}…`;
18
+ };
19
+
20
+ const buildRuleExecutionBreakdown = (params: {
21
+ findings: ReadonlyArray<SnapshotFinding>;
22
+ rulesCoverage?: SnapshotRulesCoverage;
23
+ evaluationMetrics?: SnapshotEvaluationMetrics;
24
+ }): EvidenceRuleExecutionBreakdown => {
25
+ const matched_blocking_count = params.findings.filter(
26
+ (f) =>
27
+ f.severity === 'ERROR' ||
28
+ f.severity === 'CRITICAL' ||
29
+ f.blocking === true
30
+ ).length;
31
+ const matched_warn_count = params.findings.filter((f) => f.severity === 'WARN').length;
32
+ const matched_info_count = params.findings.filter((f) => f.severity === 'INFO').length;
33
+ const evaluated_count =
34
+ params.rulesCoverage?.counts?.evaluated ?? params.evaluationMetrics?.evaluated_rule_ids.length ?? 0;
35
+ const active = params.rulesCoverage?.counts?.active ?? 0;
36
+ const evaluatedFromCoverage = params.rulesCoverage?.counts?.evaluated;
37
+ const skipped_out_of_scope_count =
38
+ typeof params.rulesCoverage?.counts?.unevaluated === 'number'
39
+ ? params.rulesCoverage.counts.unevaluated
40
+ : active > 0 && typeof evaluatedFromCoverage === 'number'
41
+ ? Math.max(0, active - evaluatedFromCoverage)
42
+ : 0;
43
+
44
+ return {
45
+ evaluated_count: Math.max(0, evaluated_count),
46
+ matched_blocking_count,
47
+ matched_warn_count,
48
+ matched_info_count,
49
+ skipped_out_of_scope_count: Math.max(0, skipped_out_of_scope_count),
50
+ };
51
+ };
52
+
53
+ const buildHumanSummaryLines = (params: {
54
+ stage: GateStage;
55
+ outcome: GateOutcome;
56
+ findings: ReadonlyArray<SnapshotFinding>;
57
+ }): string[] => {
58
+ const lines: string[] = [`${params.stage}: outcome=${params.outcome}.`];
59
+ if (params.outcome === 'BLOCK') {
60
+ const blocking =
61
+ params.findings.find((f) => f.severity === 'CRITICAL' || f.severity === 'ERROR') ??
62
+ params.findings.find((f) => f.blocking === true) ??
63
+ params.findings[0];
64
+ if (blocking) {
65
+ lines.push(`${blocking.code}: ${truncate(blocking.message, 140)}`);
66
+ lines.push(resolveRemediationHintOrDefault(blocking.code));
67
+ }
68
+ return lines.slice(0, 3);
69
+ }
70
+ const warn = params.findings.find((f) => f.severity === 'WARN');
71
+ if (warn) {
72
+ lines.push(`WARN ${warn.code}: ${truncate(warn.message, 120)}`);
73
+ }
74
+ return lines.slice(0, 3);
75
+ };
76
+
77
+ export const buildEvidenceOperationalHints = (params: {
78
+ stage: GateStage;
79
+ outcome: GateOutcome;
80
+ findings: ReadonlyArray<SnapshotFinding>;
81
+ rulesCoverage?: SnapshotRulesCoverage;
82
+ evaluationMetrics?: SnapshotEvaluationMetrics;
83
+ extra?: Partial<Pick<EvidenceOperationalHints, 'requires_second_pass' | 'second_pass_reason'>>;
84
+ }): EvidenceOperationalHints => {
85
+ const breakdown = buildRuleExecutionBreakdown({
86
+ findings: params.findings,
87
+ rulesCoverage: params.rulesCoverage,
88
+ evaluationMetrics: params.evaluationMetrics,
89
+ });
90
+ let human_summary_lines = buildHumanSummaryLines({
91
+ stage: params.stage,
92
+ outcome: params.outcome,
93
+ findings: params.findings,
94
+ });
95
+ if (params.extra?.requires_second_pass === true) {
96
+ human_summary_lines = [
97
+ ...human_summary_lines,
98
+ 'La evidencia trackeada se actualizó en disco pero no entró en el índice; si debe ir en este commit: git add -- .ai_evidence.json',
99
+ ];
100
+ }
101
+ return {
102
+ requires_second_pass: params.extra?.requires_second_pass ?? false,
103
+ second_pass_reason:
104
+ params.extra?.requires_second_pass === true
105
+ ? (params.extra.second_pass_reason ?? null)
106
+ : null,
107
+ human_summary_lines: human_summary_lines.slice(0, 4),
108
+ rule_execution_breakdown: breakdown,
109
+ };
110
+ };
@@ -206,10 +206,26 @@ export type EvidenceChain = {
206
206
  sequence: number;
207
207
  };
208
208
 
209
+ export type EvidenceRuleExecutionBreakdown = {
210
+ evaluated_count: number;
211
+ matched_blocking_count: number;
212
+ matched_warn_count: number;
213
+ matched_info_count: number;
214
+ skipped_out_of_scope_count: number;
215
+ };
216
+
217
+ export type EvidenceOperationalHints = {
218
+ requires_second_pass: boolean;
219
+ second_pass_reason: string | null;
220
+ human_summary_lines: string[];
221
+ rule_execution_breakdown?: EvidenceRuleExecutionBreakdown;
222
+ };
223
+
209
224
  export type AiEvidenceV2_1 = {
210
225
  version: '2.1';
211
226
  timestamp: string;
212
227
  evidence_chain?: EvidenceChain;
228
+ operational_hints?: EvidenceOperationalHints;
213
229
  snapshot: Snapshot;
214
230
  ledger: LedgerEntry[];
215
231
  platforms: Record<string, PlatformState>;
@@ -342,6 +342,9 @@ const toStableEvidence = (
342
342
  return {
343
343
  version: '2.1',
344
344
  timestamp: evidence.timestamp,
345
+ ...(typeof evidence.operational_hints !== 'undefined'
346
+ ? { operational_hints: evidence.operational_hints }
347
+ : {}),
345
348
  snapshot: {
346
349
  stage: evidence.snapshot.stage,
347
350
  audit_mode: normalizedAuditMode,
@@ -0,0 +1,40 @@
1
+ export const DEFAULT_GATE_REMEDIATION =
2
+ 'Corrige la causa del bloqueo y vuelve a ejecutar el gate.';
3
+
4
+ export const REMEDIATION_HINT_BY_CODE: Readonly<Record<string, string>> = {
5
+ EVIDENCE_MISSING: 'Regenera .ai_evidence.json ejecutando una auditoría.',
6
+ EVIDENCE_INVALID: 'Corrige/regenera .ai_evidence.json y vuelve a ejecutar el gate.',
7
+ EVIDENCE_CHAIN_INVALID: 'Regenera evidencia para restaurar la cadena criptográfica.',
8
+ EVIDENCE_STAGE_SYNC_FAILED:
9
+ 'Sincroniza la evidencia trackeada y reintenta: git add -- .ai_evidence.json && git commit --amend --no-edit',
10
+ EVIDENCE_STALE: 'Refresca evidencia antes de continuar.',
11
+ EVIDENCE_REPO_ROOT_MISMATCH: 'Regenera evidencia desde este mismo repositorio.',
12
+ EVIDENCE_BRANCH_MISMATCH: 'Regenera evidencia en la rama actual y reintenta.',
13
+ EVIDENCE_RULES_COVERAGE_MISSING: 'Ejecuta auditoría completa para recalcular rules_coverage.',
14
+ EVIDENCE_RULES_COVERAGE_INCOMPLETE: 'Asegura coverage_ratio=1 y unevaluated=0.',
15
+ ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES_HIGH:
16
+ 'Reconcilia policy/skills y reintenta PRE_COMMIT: npx --yes --package pumuki@latest pumuki policy reconcile --strict --json && npx --yes --package pumuki@latest pumuki-pre-commit',
17
+ EVIDENCE_ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES:
18
+ 'Reconcilia policy/skills y revalida PRE_WRITE: npx --yes --package pumuki@latest pumuki policy reconcile --strict --json && npx --yes --package pumuki@latest pumuki sdd validate --stage=PRE_WRITE --json',
19
+ GITFLOW_PROTECTED_BRANCH: 'Trabaja en feature/* y evita ramas protegidas.',
20
+ EVIDENCE_PREWRITE_WORKTREE_OVER_LIMIT:
21
+ 'Reduce archivos staged/unstaged por debajo del umbral (o ajusta PUMUKI_PREWRITE_WORKTREE_*); divide el trabajo en commits más pequeños.',
22
+ EVIDENCE_PREWRITE_WORKTREE_WARN:
23
+ 'El worktree supera el umbral de aviso; reduce alcance antes del siguiente commit/push.',
24
+ PRE_PUSH_UPSTREAM_MISSING: 'Ejecuta: git push --set-upstream origin <branch>',
25
+ PRE_PUSH_UPSTREAM_MISALIGNED:
26
+ 'Alinea upstream con la rama actual: git branch --unset-upstream && git push --set-upstream origin <branch>',
27
+ MANIFEST_MUTATION_DETECTED:
28
+ 'Los hooks/gates no deben modificar manifests. Revisa wiring y ejecuta upgrade explícito solo cuando aplique (por ejemplo: pumuki update --latest).',
29
+ };
30
+
31
+ export const resolveRemediationHintForViolationCode = (code: string): string | undefined => {
32
+ const trimmed = code.trim();
33
+ if (trimmed.length === 0) {
34
+ return undefined;
35
+ }
36
+ return REMEDIATION_HINT_BY_CODE[trimmed];
37
+ };
38
+
39
+ export const resolveRemediationHintOrDefault = (code: string): string =>
40
+ resolveRemediationHintForViolationCode(code) ?? DEFAULT_GATE_REMEDIATION;
@@ -9,6 +9,7 @@ export { parseNameStatus } from './gitDiffUtils';
9
9
  export interface IGitService {
10
10
  runGit(args: ReadonlyArray<string>, cwd?: string): string;
11
11
  getStagedFacts(extensions: ReadonlyArray<string>): ReadonlyArray<Fact>;
12
+ getUnstagedFacts(extensions: ReadonlyArray<string>): ReadonlyArray<Fact>;
12
13
  getRepoFacts(extensions: ReadonlyArray<string>): ReadonlyArray<Fact>;
13
14
  getRepoAndStagedFacts(extensions: ReadonlyArray<string>): ReadonlyArray<Fact>;
14
15
  getStagedAndUnstagedFacts(extensions: ReadonlyArray<string>): ReadonlyArray<Fact>;
@@ -44,6 +45,30 @@ export class GitService implements IGitService {
44
45
  );
45
46
  }
46
47
 
48
+ getUnstagedFacts(extensions: ReadonlyArray<string>): ReadonlyArray<Fact> {
49
+ const nameStatus = this.runGit(['diff', '--name-status']);
50
+ const changes = parseNameStatus(nameStatus).filter((change) =>
51
+ hasAllowedExtension(change.path, extensions)
52
+ );
53
+ const untrackedPaths = this.runGit(['ls-files', '--others', '--exclude-standard'])
54
+ .split('\n')
55
+ .map((line) => line.trim())
56
+ .filter((line) => line.length > 0)
57
+ .filter((path) => hasAllowedExtension(path, extensions));
58
+ const unstagedPaths = new Set(changes.map((change) => change.path));
59
+ const untrackedChanges = untrackedPaths
60
+ .filter((path) => !unstagedPaths.has(path))
61
+ .map((path) => ({
62
+ path,
63
+ changeType: 'added' as const,
64
+ }));
65
+ const mergedChanges = [...changes, ...untrackedChanges];
66
+ const repoRoot = this.resolveRepoRoot();
67
+ return buildFactsFromChanges(mergedChanges, 'git:unstaged', (filePath) =>
68
+ this.readWorkingTreeFile(repoRoot, filePath)
69
+ );
70
+ }
71
+
47
72
  getRepoFacts(extensions: ReadonlyArray<string>): ReadonlyArray<Fact> {
48
73
  const trackedFiles = this.runGit(['ls-files'])
49
74
  .split('\n')