github-proyect-tracking 0.0.0-stage → 0.4.1
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 +21 -0
- package/README.md +456 -2
- package/bin/build.js +42 -0
- package/bin/cli.js +359 -0
- package/package.json +35 -4
- package/skill/SKILL.md +171 -0
- package/skill/references/api.md +163 -0
- package/skill/references/architecture.md +171 -0
- package/skill/scripts/tracking.py +196 -0
- package/skill/scripts/tracking_core/__init__.py +1 -0
- package/skill/scripts/tracking_core/commands.py +846 -0
- package/skill/scripts/tracking_core/common.py +163 -0
- package/skill/scripts/tracking_core/events.py +204 -0
- package/skill/scripts/tracking_core/github.py +315 -0
- package/skill/scripts/tracking_core/state.py +379 -0
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
|
-
#
|
|
1
|
+
# github-proyect-tracking
|
|
2
2
|
|
|
3
|
-
|
|
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/');
|