@dforce2055/dai 0.10.0 → 0.12.0

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 (38) hide show
  1. package/CHANGELOG.md +168 -0
  2. package/README.md +12 -2
  3. package/VERSION +1 -1
  4. package/cli/dai.mjs +492 -36
  5. package/cli/lib/bootstrap.mjs +5 -2
  6. package/cli/lib/branch-scope.mjs +200 -0
  7. package/cli/lib/forge-api.mjs +57 -0
  8. package/cli/lib/pm-adapter.mjs +16 -2
  9. package/cli/lib/pm-clickup.mjs +16 -1
  10. package/cli/lib/pm-jira.mjs +21 -1
  11. package/cli/lib/pr.mjs +21 -1
  12. package/cli/lib/us-format.mjs +147 -0
  13. package/docs/EJEMPLO-END-TO-END.md +26 -3
  14. package/docs/METODOLOGIA.md +5 -0
  15. package/docs/PROBAR.md +12 -1
  16. package/docs/SCRUM-CON-IA.md +2 -2
  17. package/docs/adr/0018-alcance-de-stamp-y-gate-de-ci.md +183 -0
  18. package/docs/adr/README.md +1 -0
  19. package/docs/detalle/01-refinamiento.md +14 -0
  20. package/docs/detalle/07-merge-trazabilidad.md +13 -2
  21. package/docs/glosario.md +1 -1
  22. package/docs/guias/dev.md +21 -0
  23. package/docs/guias/po.md +40 -1
  24. package/docs/public/tutoriales/funcional-1-skills-usuario.png +0 -0
  25. package/docs/public/tutoriales/funcional-2-copilot-signin.png +0 -0
  26. package/docs/public/tutoriales/funcional-3-carpeta-configurada.png +0 -0
  27. package/docs/public/tutoriales/funcional-4-doctor.png +0 -0
  28. package/docs/public/tutoriales/funcional-5-publish-parent.png +0 -0
  29. package/docs/tutoriales/index.md +9 -0
  30. package/docs/tutoriales/setup-dev.md +516 -0
  31. package/docs/tutoriales/setup-funcional.md +480 -0
  32. package/governance/ci-rules.md +47 -8
  33. package/package.json +1 -1
  34. package/skills/dai-review/SKILL.md +1 -1
  35. package/skills/grill-epic/SKILL.md +2 -2
  36. package/skills/grill-intent/SKILL.md +1 -1
  37. package/skills/grill-user-story/SKILL.md +58 -10
  38. package/templates/ci-dai-gate.yml +50 -0
package/CHANGELOG.md CHANGED
@@ -3,6 +3,172 @@
3
3
  Formato basado en [Keep a Changelog](https://keepachangelog.com/). Versionado semver
4
4
  (ver `VERSION`).
5
5
 
6
+ ## [0.12.0] — 2026-08-13
7
+
8
+ **La PR deja de robarle la US a otro. `dai pr` resolvía el link recorriendo todo el repo y
9
+ quedándose con el último `implements.yaml`; ahora lo resuelve la rama, que es la que sabe la
10
+ respuesta — y cuando no puede saberlo, pregunta en vez de elegir en silencio. Más el tutorial
11
+ de setup del dev, la contraparte del que ya tenía el funcional.**
12
+
13
+ ### Arreglado
14
+ - **`dai pr` armaba la PR con la US equivocada** (issues [#31](https://github.com/dforce2055/dai/issues/31),
15
+ [#32](https://github.com/dforce2055/dai/issues/32), [#33](https://github.com/dforce2055/dai/issues/33)).
16
+ Recorría **todos** los `implements.yaml` del repo —archivados incluidos, porque era el
17
+ único comando que no pasaba `{ includeArchived: false }`— y el `break` cortaba solo el
18
+ bucle interno, así que ganaba el **último** en orden de lectura. La rama, que la nombra el
19
+ propio `dai link-us`, no entraba en la decisión.
20
+ El síntoma es silencioso y por eso duele: la PR sale con el título, el link y el
21
+ `dai check ✅` de **otra** US. En repos reales convivieron dos PRs con el mismo título y
22
+ contenidos que no tenían nada que ver, y una PR de archivado apareció rotulada con la
23
+ historia de un compañero. Es exactamente el modo de falla que la constitución quiere
24
+ evitar — el link QUÉ↔CÓMO queda mal y nadie se entera, porque **nadie lee el
25
+ `implements.yaml` en la lista de PRs: leen el título**.
26
+ Ahora decide `prScope` (`cli/lib/branch-scope.mjs`), hermana de `stampScope`: la rama
27
+ nombra una US viva → esa; una sola US viva → esa; varias candidatas → **pregunta** con TTY
28
+ y **falla** sin TTY, listándolas.
29
+ - **Una rama exenta ya no hereda la US del repo.** Un `chore/`/`docs/`/`release/` que no
30
+ nombra ninguna US genera la PR **sin** US —título del último commit y la sección
31
+ *Implementa* diciendo que no hay historia— en lugar de colgarle la de otro o dejar el
32
+ placeholder `ABC-###` del template.
33
+ - **`dai done` anunciaba `US cerrada:` con todas las US del repo**, archivadas incluidas.
34
+ Cerrar una rama informaba el cierre de medio sprint. Mismo defecto de clase, en un mensaje.
35
+ - **Ctrl+D en las preguntas de `dai pr`** cancela en vez de cortar con `Aborted with Ctrl+D`.
36
+ Todas ellas preceden a una acción hacia afuera (push + PR): ahí abortar es lo seguro.
37
+
38
+ ### Agregado
39
+ - **`dai pr --us <ID>`** — el escape hatch explícito, y la única salida cuando hay ambigüedad
40
+ y no hay TTY (un pipeline). Acepta también un change ya archivado.
41
+ - **El preview dice de dónde salió la US**: `US: ABC-482 — la branch '…' nombra ABC-482`.
42
+ Cuando el título está mal, es lo único que lo delata.
43
+ - **[Tutorial de setup para desarrolladores (Windows)](docs/tutoriales/setup-dev.md)** — el
44
+ otro lado del que ya existía para el funcional: Node, dai, git + SSH + `glab`, las skills
45
+ en Copilot, OpenSpec, el `.env.dai` contra Jira, y el ciclo completo sobre una US real
46
+ (`link-us` → `check` → `mr` → `stamp` → `done`). El troubleshooting sale de lo que pasó de
47
+ verdad en Windows corporativo: el push HTTPS que necesita completar el credential manager,
48
+ el proxy con su propio certificado, el gate de CI.
49
+
50
+ ### Versionado
51
+
52
+ **Minor → 0.12.0.** `dai pr` **cambia de comportamiento**: donde antes elegía una US en
53
+ silencio, ahora pregunta (o falla), y una rama exenta genera la PR sin US. En el papel es
54
+ incompatible; en la práctica el comportamiento viejo era el bug de los issues #31/#32/#33.
55
+ Suma la flag `--us`, aditiva. El contrato del modelo (`ac_hash`, schema de `implements.yaml`)
56
+ queda intacto.
57
+
58
+ ### Interno
59
+ - **319 tests** (+12 desde 0.11.0): `prScope` ×10 —la rama manda sobre el orden de
60
+ directorio, los archivados no compiten, una `chore/` no hereda, la ambigüedad no se
61
+ resuelve sola— y el cuerpo/título de una PR sin US ×2.
62
+ - `requiresLink()` devuelve además `kind` (`always` / `exempt` / `untyped`): es lo que separa
63
+ "exenta por tipo" de "la rama no dice nada", y lo que `dai pr` necesitaba para no heredar
64
+ la US de otro sin romper el gate de CI.
65
+
66
+ ## [0.11.0] — 2026-07-22
67
+
68
+ **Ronda de fixes reportados usándola, más el eslabón que faltaba: editar el QUÉ. `dai stamp`
69
+ deja de estampar de más, el gate de governance pasa de regla escrita a comando ejecutable,
70
+ `dai edit-us` trae la US del tracker y valida el formato antes de devolverla, y los mensajes
71
+ de error dejan de mandar el diagnóstico para el lado equivocado.**
72
+
73
+ ### Agregado
74
+ - **`dai check --ci`** — el gate de [`governance/ci-rules.md`](governance/ci-rules.md),
75
+ ejecutable ([ADR-0018](docs/adr/0018-alcance-de-stamp-y-gate-de-ci.md), issue #26). El
76
+ documento prometía "sin `implements.yaml` el CI bloquea" y no existía el comando que lo
77
+ hiciera: era una regla escrita que nadie aplicaba. Ahora lee el nombre de la rama y
78
+ aplica `branch-naming.md` — `feature/` siempre exige US, `chore/`/`docs/`/`ci/`/`release/`
79
+ y compañía quedan **exentas**, `fix/` exige solo si el nombre trae un ID. Salidas:
80
+ `0` pasa · `1` falta el link · `2` el QUÉ cambió. Detecta sola la rama en CI
81
+ (`GITHUB_HEAD_REF` y equivalentes: en una PR, `HEAD` es un merge commit detached).
82
+ Con `--no-network` valida el link sin pegarle al tracker.
83
+ - **`templates/ci-dai-gate.yml`** — workflow listo para copiar a `.github/workflows/`.
84
+ Fuera de GitHub Actions el contrato es el mismo: un comando y su código de salida.
85
+ - **`dai edit-us <ID>` y `dai update-us <ID>`** — editar el QUÉ deja de ser copiar y pegar
86
+ (issue #23, [ADR-0018](docs/adr/0018-alcance-de-stamp-y-gate-de-ci.md)). Dos puertas a un
87
+ solo camino: `edit-us` **baja la US del tracker**, te la abre en tu `$EDITOR` y la sube
88
+ (para el PO); `update-us` empuja un `.md` que ya escribiste (para el dev que refinó la US
89
+ implementándola). Las dos dan el mismo preview y la misma confirmación, porque comparten
90
+ el mismo tramo de escritura.
91
+ - **Valida el formato antes de guardar** contra el molde canónico
92
+ (`templates/formato-us.md`): frenan las tres cosas sin las cuales no hay `ac_hash`
93
+ —sin título, sin sección de criterios, sección vacía— y **avisan** las demás (un
94
+ criterio que no es Gherkin completo, uno que se mete en el CÓMO, un título
95
+ kilométrico). `--strict` sube los avisos a errores.
96
+ - **Un formato inválido no tira lo escrito**: te devuelve al editor con los errores a la
97
+ vista, las veces que haga falta.
98
+ - **Propone subir el `spec_version`** cuando el `ac_hash` se movió, y espera un sí o un
99
+ no: `s` = cambio material (los repos con la versión vieja se marcan **atrasados**),
100
+ `n` = cambio editorial (no se marca nadie). dai sabe *que* cambió, no *si importa* —
101
+ eso lo sabe el PO. `--bump` / `--no-bump` para el modo no interactivo.
102
+ - **Re-estampa el `ac_hash`** del `implements.yaml`, que si no `dai check` te marcaría
103
+ atrasado por tu propia edición. `--dry-run` muestra todo el preview sin escribir.
104
+
105
+ ### Arreglado
106
+ - **`dai stamp` estampaba TODAS las US del repo, archivadas incluidas** (issue #22). Cerrar
107
+ una historia dejaba un comentario de cobertura en los tickets de las otras tres del
108
+ sprint — y un comentario en un tracker no se deshace. Ahora el alcance sale de la rama
109
+ ([ADR-0018](docs/adr/0018-alcance-de-stamp-y-gate-de-ci.md)): si la rama nombra la US,
110
+ esa; si hay una sola viva, esa; si hay varias y no puede saber cuál, **pregunta** en vez
111
+ de estampar de más (y sin TTY falla pidiendo el ID, en vez de decidir por vos). Los
112
+ changes archivados salen del default. `dai stamp <ID>` es explícito y `dai stamp --all`
113
+ recupera el comportamiento anterior.
114
+ - **El error del forge decía `¿token? ¿ref correcta?` para todo** (issue #24). Sin token,
115
+ token vencido, token sin scope y PR inexistente caían en la misma frase, y eso costó una
116
+ sesión entera de diagnóstico equivocado. Ahora se nombran por separado: **no hay
117
+ `GITHUB_TOKEN`/`GITLAB_TOKEN`** (se detecta antes de salir a la red, y no se afirma que
118
+ esté vencido algo que no existe) · **401** el token existe pero no sirve, con el `curl`
119
+ para verificarlo · **403** válido pero sin permiso, o rate limit · **404** nombra las
120
+ **dos** causas, porque en un repo privado GitHub devuelve 404 y no 403 a propósito. El
121
+ diagnóstico ahora cubre `forge pr`, `forge comment` y `forge review`, que antes se
122
+ tragaban el error real.
123
+ - **`.dai/reviews/` en el `.gitignore` de los repos ya inicializados** (issue #25). La
124
+ regla estaba desde 0.10.0, pero solo la aplicaban `dai init`/`dai sync`, y el
125
+ `review.json` lo escribe la skill: un repo scaffoldeado con una dai vieja se comía
126
+ borradores a medio editar en un commit. Ahora `dai forge review` lo agrega al consumir
127
+ un borrador que está bajo `.dai/reviews/`. Además la reconciliación compara **normalizado**
128
+ (`.dai/reviews`, `/.dai/reviews/` y `.dai/reviews/` son la misma regla), así no duplica
129
+ la línea a quien ya la había puesto a mano.
130
+ - Un `Ctrl+D` en un prompt de confirmación se trata como **cancelar** en vez de crashear
131
+ con `Aborted with Ctrl+D`.
132
+
133
+ ### Interno
134
+ - **`cli/test/package-hygiene.test.mjs`** — chequea lo que hasta ahora era un `git grep` a
135
+ mano antes de pushear: que ningún fuente versionado tenga bytes NUL (uno se coló y git
136
+ pasó a tratar el archivo como binario, con el diff dejando de ser revisable), que todo
137
+ sea UTF-8 válido, que no viajen identificadores de repos de terceros —la convención de
138
+ los ejemplos es ACME— y que `files[]` no publique el sitio.
139
+
140
+ ### Versionado
141
+
142
+ **Minor → 0.11.0.** `dai stamp` sin argumentos **cambia de comportamiento**: antes estampaba
143
+ todas las US del repo, ahora una. En el papel es incompatible; en la práctica el
144
+ comportamiento viejo era el bug que reporta el issue #22, y quien lo quiera tiene `--all`.
145
+ El contrato del modelo (`ac_hash`, schema de `implements.yaml`) queda intacto, y todo lo
146
+ demás es aditivo.
147
+
148
+ ### Cambiado
149
+ - `governance/ci-rules.md` describe lo que la máquina realmente hace: el comando que
150
+ ejecuta cada regla, la tabla de ramas exentas, y por qué el gate no bloquea sin
151
+ credenciales del tracker.
152
+ - **Los adaptadores de PM devuelven `raw`** (el markdown completo de la US) además del
153
+ parseo. Es lo que `edit-us` abre; antes solo salían título + hash, que alcanza para
154
+ detectar drift pero no para editar.
155
+ - **`/grill-user-story` distingue crear de refinar.** Si la US es nueva la crea como
156
+ siempre (MCP o `dai publish`); si **ya tiene key**, ahora la actualiza con
157
+ `dai edit-us <ID> --us <md> --no-editor` en vez de pisar el ticket por MCP. Así el
158
+ camino de la skill pasa por los mismos controles que el manual: validación de formato
159
+ antes de escribir, preview, y la pregunta del `spec_version` — que la skill tiene
160
+ instrucción explícita de trasladarle al PO, no de responder con `--yes`.
161
+ - **Las skills dicen bien dónde está la config: `.env.dai` *o* `.env`.** Varias mandaban a
162
+ leer solo uno de los dos, y quien tenía todo en el `.env` del equipo veía a la skill
163
+ concluir que no había tracker configurado. dai **carga los dos** —`.env.dai` gana si una
164
+ clave está en ambos, y un repo que nunca creó `.env.dai` sigue funcionando igual
165
+ (ADR-0017)—; ahora las skills lo dicen así. Sin cambios de comportamiento en el CLI:
166
+ el loader ya hacía esto.
167
+ - Documentación al día con los comandos nuevos: la [guía del PO](docs/guias/po.md) estrena
168
+ una sección "Cuando el QUÉ cambia", más `docs/guias/dev.md`, `docs/PROBAR.md`,
169
+ `docs/EJEMPLO-END-TO-END.md`, `docs/SCRUM-CON-IA.md`, `docs/METODOLOGIA.md`,
170
+ `docs/glosario.md`, `docs/detalle/01-refinamiento.md` y `docs/detalle/07-merge-trazabilidad.md`.
171
+
6
172
  ## [0.10.0] — 2026-07-18
7
173
 
8
174
  **La config de dai deja de vivir en el `.env` del equipo y pasa a un `.env.dai` propio (no
@@ -445,6 +611,8 @@ ClickUp y Jira Cloud.
445
611
  - Tests de las rutas de red (jira/clickup/forge) con `fetch` mockeado. Sin links rotos;
446
612
  `files` de npm sin tests ni secretos.
447
613
 
614
+ [0.12.0]: https://github.com/dforce2055/dai/releases/tag/v0.12.0
615
+ [0.11.0]: https://github.com/dforce2055/dai/releases/tag/v0.11.0
448
616
  [0.10.0]: https://github.com/dforce2055/dai/releases/tag/v0.10.0
449
617
  [0.9.0]: https://github.com/dforce2055/dai/releases/tag/v0.9.0
450
618
  [0.8.2]: https://github.com/dforce2055/dai/releases/tag/v0.8.2
package/README.md CHANGED
@@ -112,6 +112,14 @@ dai pr --assignee <compañero> # crea la PR precargada y se la asigna a un com
112
112
  dai stamp # al mergear: estampa la cobertura en el tracker
113
113
  ```
114
114
 
115
+ > ¿Cambió el QUÉ? `dai edit-us <ID>` baja la US del tracker, te la abre en tu editor, valida
116
+ > el formato y te pregunta si el cambio es material (sube `spec_version`) o editorial. Si
117
+ > ya tenés el `.md` escrito —lo refinaste implementando— `dai update-us <ID>` lo empuja y
118
+ > re-estampa tu `ac_hash`, que si no `dai check` te marca atrasado por tu propia edición.
119
+ > Y si quieres que el link deje de depender de la memoria del equipo, `dai check --ci` es
120
+ > el gate ejecutable de [`governance/ci-rules.md`](governance/ci-rules.md): copia
121
+ > [`templates/ci-dai-gate.yml`](templates/ci-dai-gate.yml) a `.github/workflows/`.
122
+
115
123
  > **Para que `dai pr` cree la PR/MR** necesitas el CLI del forge instalado y autenticado, una
116
124
  > sola vez por máquina:
117
125
  > - **GitHub** → [`gh`](https://cli.github.com) · `gh auth login`
@@ -159,13 +167,15 @@ flowchart TD
159
167
  | 4 | **Linkear la US** | `dai link-us <ID>` → branch + `openspec/changes/<id>/implements.yaml` | dev |
160
168
  | 5 | **Verificar / listar** | `dai check` (¿al día?) · `dai ls` (qué implementa el repo) | dev |
161
169
  | 6 | **Resincronizar** *(si el PO editó la US)* | `dai link-us <ID> --resync` | dev |
170
+ | 6b | **Editar la US** | `dai edit-us <ID>` la baja del tracker, la abrís en tu editor, valida el formato y la devuelve · `dai update-us <ID>` empuja un `.md` que ya escribiste. Las dos preguntan si subir el `spec_version` | PO / dev |
162
171
  | 7 | **Diseñar el CÓMO** | en el asistente: `/opsx:explore` → `/opsx:propose` → design + tasks | dev + IA |
163
172
  | 8 | **Implementar** | `/opsx:apply` → implementa la US con TDD y genera los commits | dev + IA |
164
173
  | 9 | **Code review propio** | revisas tu implementación (correctitud + calidad) antes de la PR | dev |
165
174
  | 10 | **Smoke test** | pides al agente un smoke local del flujo | dev + IA |
166
175
  | 11 | **Crear la PR** | `dai pr` → pregunta la branch base, arma el texto, lo muestra, confirma, pushea y crea la PR/MR | dev |
167
176
  | 12 | **Review de un partner** | skill `/dai-review <PR>` deja un **review inline** (resumen + un comentario por línea, low/medium/high); te muestra el preview y **espera tu OK** antes de postear; un humano aprueba | partner |
168
- | 13 | **Merge + estampar** | al mergear: `dai stamp` → cobertura inversa en el tracker | dev / CI |
177
+ | 13 | **Merge + estampar** | al mergear: `dai stamp` → cobertura inversa en el tracker (deduce la US de la rama; en CI pasa el ID) | dev / CI |
178
+ | 13b | **Gate de trazabilidad** *(opcional)* | `dai check --ci` en el CI → bloquea una rama de producto sin link; `chore/`/`docs/` quedan exentas ([template](templates/ci-dai-gate.yml)) | CI |
169
179
  | 14 | **Cerrar la US** | `dai done` → vuelve a la base, actualiza y borra la branch local (si está mergeada) | dev |
170
180
 
171
181
  > **Paso 3b (publicar):** el MCP crea el issue interactivamente; `dai publish` necesita el
@@ -191,7 +201,7 @@ flowchart TD
191
201
  | `dai link-us <ID> --resync` | re-estampa el `ac_hash` contra la US viva (tras un ⚠️ de check) |
192
202
  | `dai check` | compara tu código vs la US viva → ✅ al día / ⚠️ atrasado (exit code = gate de PR) |
193
203
  | `dai ls [--json]` | lista las US que implementa el repo + su link al tracker |
194
- | `dai pr [--assignee u] [--base b] [--draft] [--yes]` · alias **`dai mr`** | crea TU PR/MR precargada: pregunta la branch base (default `main`), muestra el texto y confirma antes de publicar. Detecta el forge (GitHub→PR con `gh` · GitLab→MR con `glab`); `mr` es el mismo comando, más natural en GitLab |
204
+ | `dai pr [--assignee u] [--base b] [--draft] [--yes] [--us ID] [--title t]` · alias **`dai mr`** | crea TU PR/MR precargada: pregunta la branch base (default `main`), muestra el texto y confirma antes de publicar. Detecta el forge (GitHub→PR con `gh` · GitLab→MR con `glab`); `mr` es el mismo comando, más natural en GitLab. **La US la resuelve la branch** (la nombra `dai link-us`): si hay varias vivas y ninguna coincide, **pregunta** en vez de elegir por vos (sin TTY falla pidiendo `--us <ID>`), y una branch `chore/`/`docs/` sale **sin US** en lugar de heredar la de otro |
195
205
  | `dai stamp` | estampa la cobertura inversa en el tracker (branch + commit-ancla) |
196
206
  | `dai done [--base main] [--force]` | cierra la US: vuelve a la base, `fetch --prune` + `pull`, y borra la branch local **si está mergeada** (chequeo estricto; `--force` la borra igual). Redes: no estar en la base, sin cambios sueltos, sin commits sin pushear |
197
207
  | `dai archive [<change>] [--skip-specs]` | **funde los delta specs del change en las specs canónicas** (`openspec/specs/`) y lo archiva. Lo corre el **aprobador** de la PR (gate de aprobación, [ADR-0011](docs/adr/0011-archive-gate-de-aprobacion.md)); detecta el change activo o le pasás el nombre. Envuelve `openspec archive` |
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.0
1
+ 0.12.0