github-proyect-tracking 0.0.0-stage → 0.4.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kronops
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,457 @@
1
- # Temporary Holding Version
1
+ # github-proyect-tracking
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Skill para agentes de IA (Hermes Agent) que **rastrea y persiste el estado de
4
+ ejecución de un proceso estructurado, vinculándolo directamente a un proyecto
5
+ de GitHub mediante Issues y comentarios**. Si el proceso se interrumpe (error,
6
+ caída de sesión, reinicio), el skill reanuda exactamente donde quedó leyendo el
7
+ último comentario del Issue.
8
+
9
+ > **v0.4.0** — correcciones de `Review_V3.md`: resultados terminales derivados
10
+ > del Issue, recuperación explícita tras `ERROR`, reconciliación por UUID ante
11
+ > respuestas perdidas o 5xx, heartbeat durante toda la operación, esquema local
12
+ > v2 con migración e instaladores transaccionales. El CLI se separó en módulos
13
+ > sin cambiar su punto de entrada ni sus formatos visibles.
14
+
15
+ ---
16
+
17
+ ## 1. Descripción general
18
+
19
+ `github-proyect-tracking` convierte cada ejecución de un proceso en un Issue de
20
+ GitHub que funciona como bitácora persistente y auditable:
21
+
22
+ - **Un Issue por ejecución**, titulado `[Process Tracking] Execution ID: {id}`.
23
+ - **Cada transición del proceso es un comentario** con un contrato visible
24
+ cerrado y sin ambigüedades:
25
+
26
+ ```text
27
+ [STATUS: INICIANDO] - Paso: {nombre}
28
+ [STATUS: ERROR] - Paso: {nombre} - Detalles: {error o stacktrace}
29
+ [STATUS: COMPLETADO] - Paso: {nombre}
30
+ [STATUS: FINALIZADO] - El proceso concluyó exitosamente.
31
+ [STATUS: CANCELADO] - El proceso fue cerrado anticipadamente (--force).
32
+ ```
33
+
34
+ Los comentarios nuevos añaden después de ese texto una línea HTML invisible
35
+ con `execution_id`, `sequence` y `transition_id` (UUID). Estos metadatos
36
+ identifican el intento exacto al reconciliar un POST; no alteran el contrato
37
+ visible ni la interpretación de comentarios antiguos sin metadatos.
38
+
39
+ - **Memoria interna** por repositorio (`~/.github-proyect-tracking/{owner}__{repo}.json`)
40
+ con `active_process_id` (número de Issue; nulo si no hay proceso) y
41
+ `github_token` (opcional).
42
+ - **Reanudación determinista**: el último comentario del Issue es la fuente de
43
+ verdad; el parser reconstruye la cronología y dicta la acción siguiente
44
+ (re-ejecutar el paso con error, continuar con el siguiente, finalizar…).
45
+
46
+ El skill **instrumenta procesos, no los ejecuta**: el agente (o el script
47
+ externo que orquesta) hace el trabajo real de cada paso y reporta cada
48
+ transición. No hay dependencias de terceros: solo Python 3.8+ (stdlib);
49
+ `gh` CLI es opcional (fuente automática de token).
50
+
51
+ | | |
52
+ |---|---|
53
+ | Entrada obligatoria | `github_project_url` — `https://github.com/{owner}/{repo}` |
54
+ | Salida de cada comando | Un único objeto JSON + exit code documentado |
55
+ | Permisos requeridos | PAT classic con scope `repo`, o fine-grained con Issues → Read and write |
56
+ | Plataformas | Linux, macOS, Windows |
57
+ | Licencia | MIT |
58
+
59
+ ---
60
+
61
+ ## 2. Instalación
62
+
63
+ ### Requisitos previos
64
+
65
+ - Python 3.8 o superior (`python3` en PATH).
66
+ - Un token de GitHub con permisos de escritura sobre Issues del repo destino.
67
+ Fuentes que el skill resuelve automáticamente, en orden:
68
+ `--token` → `GITHUB_TRACKING_TOKEN` → `GITHUB_TOKEN` → `GH_TOKEN` →
69
+ `gh auth token` → archivo de estado local.
70
+ - (Opcional) `gh` CLI autenticado — sirve como fuente de token.
71
+
72
+ ### Instalación estándar
73
+
74
+ Desde la raíz de este repositorio:
75
+
76
+ ```bash
77
+ bash install.sh
78
+ ```
79
+
80
+ El instalador:
81
+
82
+ 1. Copia `github-proyect-tracking/` a
83
+ `{perfil-de-hermes}/skills/software-development/github-proyect-tracking/`
84
+ (detecta el perfil activo; se puede forzar con `--profile NOMBRE` o
85
+ apuntar a cualquier ruta con `--dest RUTA`; `--user` instala en
86
+ `~/.hermes/skills` del perfil por defecto).
87
+ 2. Respalda cualquier versión previa con una ruta única y la restaura si el
88
+ reemplazo o la verificación falla.
89
+ 3. Valida el frontmatter del SKILL.md y ejecuta
90
+ `tracking.py --version` antes y después del reemplazo.
91
+ 4. Imprime la prueba rápida recomendada.
92
+
93
+ El staging y el rename ocurren en el mismo filesystem. Retirar el directorio
94
+ anterior y renombrar el nuevo no es una única operación atómica; esa breve
95
+ ventana queda cubierta por rollback ante errores, pero otro proceso no debe
96
+ usar el skill durante la instalación.
97
+
98
+ > El skill aparece en el catálogo de **sesiones nuevas** de Hermes (el
99
+ > catálogo se carga al iniciar sesión; no en la sesión en curso).
100
+
101
+ ### Verificación post-instalación
102
+
103
+ ```bash
104
+ python3 ~/.hermes/profiles/<perfil>/skills/software-development/github-proyect-tracking/scripts/tracking.py \
105
+ doctor --url https://github.com/{owner}/{repo}
106
+ ```
107
+
108
+ `doctor` verifica: versión de Python, directorio de estado escribible, token
109
+ resuelto, conectividad con la API de GitHub, `gh` CLI y permisos de escritura
110
+ sobre Issues del repo indicado. Siempre termina con exit 0 (es un reporte).
111
+
112
+ ### Instalación vía npx (sin clonar)
113
+
114
+ Si el paquete está publicado en npm (o tienes el tarball localmente), no
115
+ necesitas clonar el repositorio:
116
+
117
+ ```bash
118
+ # instalar (mismo destino y validaciones que install.sh)
119
+ npx --yes github-proyect-tracking
120
+ npx --yes github-proyect-tracking install --profile kronops # o --dest RUTA / --user
121
+
122
+ # diagnóstico sin instalar (python + token + API de GitHub)
123
+ npx --yes github-proyect-tracking doctor --url https://github.com/{owner}/{repo}
124
+
125
+ # ejecutar cualquier comando del skill sin instalarlo
126
+ npx --yes github-proyect-tracking run status --remote
127
+ npx --yes github-proyect-tracking uninstall [--purge-state]
128
+ ```
129
+
130
+ - El instalador npx (`npm/bin/cli.js`, Node ≥ 18, **cero dependencias npm**)
131
+ replica la resolución de destino de `install.sh`: `HERMES_HOME` → perfil
132
+ activo (`HERMES_PROFILE` o la línea `profile:` del `AGENT.md` del
133
+ directorio actual) → perfil `default`. Ambos instaladores son
134
+ intercambiables.
135
+ - `install` valida el frontmatter del SKILL.md, ejecuta
136
+ `tracking.py --version` como verificación real y respalda la copia previa.
137
+ - `doctor` y `run` usan la copia instalada si existe; si no, la empaquetada.
138
+
139
+ **Publicar en npm** (mantenedor del paquete):
140
+
141
+ ```bash
142
+ cd npm
143
+ npm login
144
+ npm publish # prepack empaqueta skill/ + README + LICENSE automáticamente
145
+ ```
146
+
147
+ `npm pack` genera el tarball verificable de forma idéntica
148
+ (`npx --yes ./npm/github-proyect-tracking-0.4.0.tgz install`).
149
+
150
+ ### Desinstalación
151
+
152
+ ```bash
153
+ bash uninstall.sh # quita el skill del perfil
154
+ bash uninstall.sh --purge-state # además borra la memoria interna local
155
+ npx --yes github-proyect-tracking uninstall # vía npx
156
+ npx --yes github-proyect-tracking uninstall --purge-state
157
+ ```
158
+
159
+ `--purge-state` elimina `~/.github-proyect-tracking/`; los Issues creados en
160
+ GitHub **no** se tocan (son parte del historial del repositorio remoto).
161
+
162
+ ### Instalación manual
163
+
164
+ ```bash
165
+ cp -R github-proyect-tracking/ ~/.hermes/profiles/<perfil>/skills/software-development/
166
+ ```
167
+
168
+ ### Ejecutar las pruebas
169
+
170
+ ```bash
171
+ python3 tests/test_tracking.py # 141 pruebas: 140 offline + 1 skip opt-in
172
+ ```
173
+
174
+ ---
175
+
176
+ ## 3. Arquitectura del skill
177
+
178
+ ### 3.1 Modelo general
179
+
180
+ ```
181
+ ┌──────────────┐ 1 comando = 1 transición ┌─────────────────────────────────┐
182
+ │ Agente (tú) │ ───────────────────────────▶ │ scripts/tracking.py (stdlib) │
183
+ │ │ ◀─────────────────────────── │ · valida, llama a la API, │
184
+ └──────────────┘ JSON + exit code │ guarda memoria y responde │
185
+ │ └───────────────┬─────────────────┘
186
+ │ ejecuta el trabajo real del proceso │
187
+ ▼ (builds, tests, despliegues…) ▼
188
+ ┌────────────────────────┐ comentarios ┌────────────────────────────────┐
189
+ │ Memoria interna local │ [STATUS: …] │ GitHub REST API v3 │
190
+ │ ~/.github-proyect- │ ◀──────────────── │ POST /repos/{o}/{r}/issues/… │
191
+ │ tracking/ │ Issue #N │ (fuente de verdad al reanudar) │
192
+ │ {owner}__{repo}.json │ └────────────────────────────────┘
193
+ └────────────────────────┘
194
+ ```
195
+
196
+ **Doble libro de contabilidad:**
197
+
198
+ - **Remoto (autoritativo):** el Issue con su cronología de comentarios. Todo
199
+ evento es *append-only* y sobrevive a cualquier pérdida local.
200
+ - **Local (rápido):** el archivo de estado evita buscar "¿qué proceso estaba
201
+ corriendo?" en cada comando. Escritura atómica (`tmp` + `rename`), permisos
202
+ `0600` (puede contener el token).
203
+
204
+ ### 3.2 Máquina de estados del proceso
205
+
206
+ ```
207
+ init (URL válida + token issues:write)
208
+ (sin proceso) ───────────────────────────────────────▶ RUNNING
209
+ │
210
+ ┌───── step-start / step-done (bucle por paso) ───┤
211
+ ▼ │
212
+ RUNNING ── step-error ──▶ ERROR ── resume(RERUN_STEP)─┘
213
+ │
214
+ ├─ finalize: FINALIZADO + Issue cerrado ──────────▶ FINALIZED
215
+ └─ finalize --force: CANCELADO + Issue cerrado ───▶ CANCELADO
216
+ (active_process_id := null)
217
+ ```
218
+
219
+ | Estado | Significado | `active_process_id` |
220
+ |---|---|---|
221
+ | `RUNNING` | Issue abierto; último evento INICIANDO/COMPLETADO | número de Issue |
222
+ | `ERROR` | Último evento ERROR — el proceso está detenido | número de Issue |
223
+ | `FINALIZED` | Issue cerrado con FINALIZADO; memoria limpia | `null` |
224
+ | `CANCELADO` | Cierre anticipado con CANCELADO; memoria limpia | `null` |
225
+ | `ABORTED` | Issue cerrado sin evento terminal válido | `null` |
226
+ | `CLEARED` | Memoria descartada localmente (`clear` soft) | `null` |
227
+
228
+ Se considera **proceso inconcluso** aquel con `active_process_id` no nulo y
229
+ estado `RUNNING` o `ERROR`. `init` sobre un proceso inconcluso **falla con
230
+ exit 7** (`RESUME_REQUIRED`) a propósito: obliga a reanudar y evita Issues
231
+ huérfanos duplicados.
232
+
233
+ ### 3.3 Flujo de trabajo (fases de la definición)
234
+
235
+ **A. Inicialización / Reanudación**
236
+
237
+ 1. `init --url URL [--plan JSON]`:
238
+ - Valida la URL con regex (formato `https://github.com/{owner}/{repo}`).
239
+ - Resuelve el token y **valida permisos de escritura antes de crear nada**.
240
+ - Genera `exec-{UTC}-{12 hex}`, crea el Issue con título canónico y
241
+ cuerpo estándar (metadatos + plan numerado), guarda
242
+ `active_process_id` en memoria.
243
+ 2. Si ya había proceso inconcluso → exit 7 → ejecutar `resume`.
244
+ 3. `resume`: trae el Issue y **todos** sus comentarios (paginado),
245
+ reconstruye la cronología de eventos y emite un veredicto accionable
246
+ (tabla §3.4). Registra `last_resume`; después de un ERROR solo esa
247
+ reanudación explícita autoriza reintentar el mismo paso.
248
+
249
+ **B. Ejecución de pasos** — por cada paso, en orden estricto:
250
+
251
+ 1. `step-start --name N` **antes** de trabajar →
252
+ `[STATUS: INICIANDO] - Paso: N`.
253
+ 2. Si el paso falla → `step-error --name N --details …` →
254
+ `[STATUS: ERROR] - Paso: N - Detalles: …` — **el proceso se detiene aquí**.
255
+ 3. Si el paso triunfa → `step-done --name N` →
256
+ `[STATUS: COMPLETADO] - Paso: N`.
257
+
258
+ **C. Finalización**
259
+
260
+ 1. `finalize` comenta `[STATUS: FINALIZADO] - El proceso concluyó
261
+ exitosamente.` si el plan está completo. Sin plan exige al menos un
262
+ COMPLETADO: después del último paso permite éxito; sin ningún trabajo exige
263
+ `--force`. Con pasos pendientes, `--force` registra CANCELADO.
264
+ 2. Cierra el Issue (PATCH `state=closed`) y **verifica re-leyendo** el
265
+ estado real.
266
+ 3. Limpia `active_process_id` (estado `FINALIZED` o `CANCELADO`) y registra
267
+ el `outcome` en el historial (últimos 20). Reintentar conserva el evento
268
+ terminal remoto y no duplica el comentario.
269
+
270
+ ### 3.4 Parser de reanudación y veredictos
271
+
272
+ Los comentarios se parsean con una regex multilinea sobre el contrato visible;
273
+ la línea HTML de metadatos se retira antes de aplicar la regex. `Detalles`
274
+ admite stacktraces multilínea. La cronología se ordena por
275
+ `(created_at, comment_id)` → **el último elemento es el último evento**.
276
+
277
+ | Último evento del Issue | `verdict.action` / `outcome` | El agente debe… |
278
+ |---|---|---|
279
+ | FINALIZADO, Issue cerrado | `NONE` / `FINALIZADO` | Nada (memoria se auto-limpia) |
280
+ | CANCELADO, Issue cerrado | `NONE` / `CANCELADO` | Nada; conservar la cancelación |
281
+ | FINALIZADO, Issue abierto | `FINISH_CLEANUP` / `FINALIZADO` | Ejecutar `finalize` |
282
+ | CANCELADO, Issue abierto | `FINISH_CLEANUP` / `CANCELADO` | Ejecutar `finalize`; no cambiar el resultado |
283
+ | ERROR | `RERUN_STEP` (+ `failed_step`, `error_details`) | Re-ejecutar ese paso y continuar |
284
+ | INICIANDO | `RERUN_STEP` | Re-ejecutar el paso (quedó a medias) |
285
+ | COMPLETADO, quedan pasos | `NEXT_STEP` (+ `remaining_steps`) | Ejecutar `resume_from_step` |
286
+ | COMPLETADO, plan agotado | `ALL_STEPS_DONE` | Ejecutar `finalize` |
287
+ | Sin comentarios | `START_FIRST` | Empezar por el paso 1 del plan |
288
+ | Issue cerrado sin FINALIZADO/CANCELADO | `ABORTED_EXTERNALLY` | Preguntar al usuario |
289
+ | Sin plan explícito | `NEXT_STEP_AGENT_DEFINED` | Decidir el siguiente paso / finalizar tras COMPLETADO |
290
+
291
+ ### 3.5 Estructura del repositorio
292
+
293
+ ```
294
+ KGithubTracking/
295
+ ├── README.md / AGENT.md / CONTRIBUTING.md / CHANGELOG.md
296
+ ├── install.sh / uninstall.sh / lib/common.sh
297
+ ├── npm/ ← paquete npx (Node ≥ 18, sin deps)
298
+ │ ├── package.json
299
+ │ ├── bin/cli.js / bin/build.js
300
+ │ └── skill/ ← copia generada (ignorada por git)
301
+ ├── tests/
302
+ │ ├── test_tracking.py ← carga toda la suite
303
+ │ ├── test_more.py / test_v2.py / test_v3.py
304
+ │ ├── test_installers.py ← argumentos y rollback reales
305
+ │ └── test_github_integration.py← integración real opt-in
306
+ ├── tools/check_docs.py ← coherencia documental (CI)
307
+ └── github-proyect-tracking/ ← skill instalable
308
+ ├── SKILL.md
309
+ ├── scripts/
310
+ │ ├── tracking.py ← punto de entrada compatible
311
+ │ └── tracking_core/
312
+ │ ├── common.py ← versión, constantes y validación
313
+ │ ├── events.py ← contrato, metadatos y planner
314
+ │ ├── state.py ← esquema v2, migración y locks
315
+ │ ├── github.py ← token, HTTP y reconciliación
316
+ │ └── commands.py ← comandos y guardas
317
+ └── references/
318
+ ├── architecture.md
319
+ └── api.md
320
+ ```
321
+
322
+ ---
323
+
324
+ ## 4. Documentación de funcionamiento
325
+
326
+ ### 4.1 Contrato de respuesta del CLI
327
+
328
+ Cada comando imprime **exactamente un JSON** a stdout y termina con un exit
329
+ code documentado:
330
+
331
+ ```json
332
+ { "ok": true, "action": "INIT", "execution_id": "exec-20261028T101500Z-ab12cd34ef56",
333
+ "issue_number": 42, "issue_url": "https://github.com/o/r/issues/42",
334
+ "state_file": "~/.github-proyect-tracking/o__r.json", "first_step": "Paso 1" }
335
+ ```
336
+
337
+ ```json
338
+ { "ok": false, "error": { "code": "RESUME_REQUIRED", "message": "...",
339
+ "state": { "...": "..." }, "next": ["resume"] } }
340
+ ```
341
+
342
+ | Exit | Códigos típicos | Causa / acción |
343
+ |---|---|---|
344
+ | 0 | — | Éxito |
345
+ | 1 | `NETWORK_ERROR`, `RATE_LIMITED`, `STATE_CORRUPT`, `COMMENTS_TOO_MANY`, `STATE_LOCKED`… | Reintentar / diagnosticar |
346
+ | 2 | `VALIDATION_ERROR`, `NO_STEP_IN_FLIGHT`, `STEP_OUT_OF_ORDER`, `STEP_NOT_IN_PLAN`, `STEP_ALREADY_DONE`, `STEP_IN_FLIGHT`, `CONTEXT_AMBIGUOUS`, `PLAN_AMBIGUOUS` | Argumentos o transición inválida |
347
+ | 3 | `INVALID_TOKEN`, `INSUFFICIENT_SCOPE`, `NO_REPO_ACCESS`, `REPO_NOT_FOUND`, `ISSUES_WRITE_FAILED` | El token no puede escribir Issues |
348
+ | 4 | `NO_TOKEN` | No hay token: pedirlo al usuario |
349
+ | 5 | `NO_STATE`, `NO_ACTIVE_PROCESS` | No hay proceso activo: `init` primero |
350
+ | 6 | `ISSUE_NOT_FOUND`, `ISSUE_AMBIGUOUS` | El Issue no existe o es ambiguo |
351
+ | 7 | `RESUME_REQUIRED` (proceso inconcluso, estado `ERROR`, `adopt` sobre activo) | Ejecutar `resume` |
352
+ | 8 | `FINALIZE_BLOCKED`, `ABORTED_EXTERNALLY`, `ISSUE_CLOSED_EXTERNALLY`, `ALREADY_FINALIZED` | Conflicto con el estado remoto |
353
+
354
+ Excepciones documentadas al contrato "un JSON por comando": `--help`/
355
+ `--version` (salida humana, exit 0) y `KeyboardInterrupt` (exit 130).
356
+
357
+ ### 4.2 Referencia de comandos
358
+
359
+ | Comando | Efecto | Efectos secundarios |
360
+ |---|---|---|
361
+ | `init --url URL [--plan JSON \| --plan-file F] [--token T] [--store-token]` | Crea el Issue y la memoria (plan sanitizado) | Issue nuevo en GitHub + archivo local |
362
+ | `resume [--url URL]` | Veredicto de reanudación | Actualiza memoria local |
363
+ | `step-start --name N` | Comenta INICIANDO | Comentario + memoria |
364
+ | `step-done --name N` | Comenta COMPLETADO | Comentario + memoria |
365
+ | `step-error --name N (--details D \| --details-file F)` | Comenta ERROR y detiene | Comentario + memoria (`ERROR`) |
366
+ | `finalize [--force]` | FINALIZADO (o CANCELADO con `--force`) + cierre + limpieza | Comentario + Issue cerrado |
367
+ | `status [--remote]` | Estado local (y remoto) | Solo lectura |
368
+ | `adopt --url URL --execution-id ID [--force]` | Recupera memoria desde un Issue | Archivo local nuevo |
369
+ | `clear [--hard]` | Limpia la memoria | Archivo local (soft/hard) |
370
+ | `doctor [--url URL]` | Diagnóstico (crea el dir. de estado y un archivo de prueba como efecto declarado) | Efecto de escritura intencional y documentado |
371
+
372
+ `--url` es opcional cuando hay exactamente UN proceso activo (ese es el
373
+ contexto); con varios procesos activos el comando exige `--url`
374
+ (`CONTEXT_AMBIGUOUS` lista los activos). `--plan` es opcional pero
375
+ recomendado — hace la reanudación 100% determinista; los nombres se
376
+ normalizan (validar+strip+sanitizar) y los duplicados tras normalizar se
377
+ rechazan.
378
+
379
+ ### 4.3 Variables de entorno
380
+
381
+ | Variable | Uso |
382
+ |---|---|
383
+ | `GITHUB_TRACKING_TOKEN` | Token preferido (máxima prioridad de las fuentes automáticas) |
384
+ | `GITHUB_TOKEN` / `GH_TOKEN` | Fallbacks estándar de la comunidad |
385
+ | `GITHUB_TRACKING_STATE` | Directorio alternativo para los archivos de estado |
386
+
387
+ ### 4.4 Seguridad del token
388
+
389
+ - Nunca se imprime en stdout (`status` lo enmascara como `<stored>`).
390
+ - Se persiste en el archivo de estado **solo** con `--store-token`
391
+ (opt-in explícito desde v0.2.0; `--token` solo ya NO guarda; el flag
392
+ `--no-store-token` quedó obsoleto y se acepta por compatibilidad); el
393
+ archivo queda con permisos `0600`.
394
+ - Vía preferida: variables de entorno o `gh auth token`.
395
+ - Nunca debe viajar en el cuerpo de un Issue o comentario (solo van
396
+ mensajes de error/stacktrace del proceso, sin credenciales).
397
+
398
+ ### 4.5 Robustez implementada
399
+
400
+ | Requisito | Implementación |
401
+ |---|---|
402
+ | URL y credenciales | `REPO_URL_RE`; redacción completa de userinfo/query/fragment |
403
+ | Permisos antes de `init` | Scopes y permisos efectivos; rol de solo lectura demostrado falla; `triage` permite gestionar Issues |
404
+ | Reanudación | Regex del contrato visible, cronología completa y veredictos con `outcome` |
405
+ | Memoria | Esquema v2 con validación/invariantes y migración automática v1→v2 al guardar; escritura atómica, 0600 |
406
+ | Exclusión | Lock con dueño y heartbeat automático durante red; `ensure_owner()` antes de efectos remotos y escrituras |
407
+ | `ERROR` | `resume` registra autorización y solo permite reintentar el paso fallido |
408
+ | Red | Respuesta perdida o 500/502/503/504 en POST → reconciliación por UUID y un reintento |
409
+ | Recuperación | `adopt` usa token almacenado y conserva ERROR/CANCELADO/ABORTED; `init` recupera por título paginado |
410
+ | Contexto | Varios activos o varios estados para `clear` → `CONTEXT_AMBIGUOUS` |
411
+
412
+ ### 4.6 Pruebas
413
+
414
+ La suite descubre **141 pruebas**: **140 offline** y una integración real
415
+ omitida por defecto. Las pruebas offline usan stdlib, `FakeGateway`, inyección
416
+ de fallos, subprocesos CLI e instaladores reales en directorios temporales.
417
+ Cubren los formatos visibles, máquina de estados, reconciliación con UUID y
418
+ 5xx, cancelaciones, procesos sin plan, heartbeat, migración v1→v2, esquema,
419
+ contexto ambiguo, seguridad, rollback y contrato JSON.
420
+
421
+ ```bash
422
+ python3 tests/test_tracking.py
423
+ # Ran 141 tests ... OK (skipped=1)
424
+ python3 tools/check_docs.py
425
+ ```
426
+
427
+ La integración real es opt-in, crea un Issue y 105 notas para ejercitar
428
+ paginación, pérdida/recuperación con `adopt` y cierre. Requiere autorización
429
+ explícita y un repositorio desechable:
430
+
431
+ ```bash
432
+ GITHUB_TRACKING_INTEGRATION=1 \
433
+ GITHUB_TRACKING_INTEGRATION_URL=https://github.com/owner/repo-desechable \
434
+ GITHUB_TRACKING_TOKEN=... python3 tests/test_github_integration.py
435
+ ```
436
+
437
+ También acepta `GITHUB_TOKEN` o `GH_TOKEN`. No se ejecuta con la suite normal.
438
+
439
+ ### 4.7 Límites operativos conocidos
440
+
441
+ - **Concurrencia:** el lock protege escrituras locales y mantiene heartbeat;
442
+ no hay transacciones distribuidas. Otro actor con acceso puede intervenir
443
+ manualmente en el Issue.
444
+ - **Comentarios de terceros:** el contrato visible puede imitarse manualmente
445
+ y se interpreta como evento. Los metadatos UUID solo autentican la
446
+ reconciliación del intento, no convierten comentarios en eventos firmados.
447
+ - **Paginación:** Issues con >10 000 comentarios → `COMMENTS_TOO_MANY`.
448
+ - **Token en archivo:** opt-in (`--store-token`), 0600 y enmascarado; prefiere
449
+ variables de entorno o `gh`.
450
+ - **Idempotencia:** `finalize` no duplica su evento terminal. Cada invocación
451
+ nueva de paso tiene otro UUID; la reconciliación automática solo deduplica
452
+ los reintentos internos de esa invocación. Consulta `status --remote` antes
453
+ de repetir manualmente un comando cuyo resultado sea incierto.
454
+ - **CANCELADO vs FINALIZADO:** ambos resultados se preservan en reintentos,
455
+ `resume`, `adopt`, estado e historial.
456
+ - **Múltiples repos:** usa `--url` cuando haya varios procesos activos o varios
457
+ estados candidatos para `clear`.
package/bin/build.js ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Empaqueta el skill dentro de npm/ para su distribución (prepack):
4
+ * github-proyect-tracking/ → npm/skill/ (sin __pycache__/bytecode)
5
+ * README.md, LICENSE → npm/
6
+ * Se ejecuta automáticamente con `npm pack` / `npm publish` (script prepack).
7
+ */
8
+ 'use strict';
9
+
10
+ const fs = require('node:fs');
11
+ const path = require('node:path');
12
+
13
+ const root = path.join(__dirname, '..', '..');
14
+ const src = path.join(root, 'github-proyect-tracking');
15
+ const here = path.join(__dirname, '..');
16
+ const dst = path.join(here, 'skill');
17
+
18
+ if (!fs.existsSync(path.join(src, 'SKILL.md'))) {
19
+ console.error(`ERROR: no se encontró ${src}/SKILL.md`);
20
+ process.exit(1);
21
+ }
22
+
23
+ fs.rmSync(dst, { recursive: true, force: true });
24
+ fs.cpSync(src, dst, { recursive: true });
25
+
26
+ (function clean(dir) {
27
+ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
28
+ const p = path.join(dir, e.name);
29
+ if (e.isDirectory()) {
30
+ if (e.name === '__pycache__') fs.rmSync(p, { recursive: true, force: true });
31
+ else clean(p);
32
+ } else if (/\.py[co]$/.test(e.name)) {
33
+ fs.rmSync(p, { force: true });
34
+ }
35
+ }
36
+ })(dst);
37
+
38
+ fs.copyFileSync(path.join(root, 'README.md'), path.join(here, 'README.md'));
39
+ fs.copyFileSync(path.join(root, 'LICENSE'), path.join(here, 'LICENSE'));
40
+
41
+ console.log(`skill → ${dst}`);
42
+ console.log('README.md + LICENSE → npm/');