github-proyect-tracking 0.5.0 → 0.6.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/README.md CHANGED
@@ -6,6 +6,12 @@ de GitHub mediante Issues y comentarios**. Si el proceso se interrumpe (error,
6
6
  caída de sesión, reinicio), el skill reanuda exactamente donde quedó leyendo el
7
7
  último comentario del Issue.
8
8
 
9
+ > **v0.6.0** — el Issue documenta QUÉ y CON QUÉ: `--process-name` nombra
10
+ > el proceso (título `[Process Tracking][N] …`) y `--process-inputs
11
+ > '{"k":"v",…}` publica los parámetros de entrada del proceso en una
12
+ > sección del cuerpo (secretos redactados) — la única forma de saber
13
+ > exactamente qué tarea estaba realizando la ejecución.
14
+ >
9
15
  > **v0.5.0** — el Issue ahora identifica el proceso rastreado:
10
16
  > `init --process-name "Mi Skill"` produce el título
11
17
  > `[Process Tracking][Mi Skill] Execution ID: …` y el cuerpo incluye
@@ -22,7 +28,14 @@ caída de sesión, reinicio), el skill reanuda exactamente donde quedó leyendo
22
28
  `github-proyect-tracking` convierte cada ejecución de un proceso en un Issue de
23
29
  GitHub que funciona como bitácora persistente y auditable:
24
30
 
25
- - **Un Issue por ejecución**, titulado `[Process Tracking] Execution ID: {id}`.
31
+ - **Un Issue por ejecución**, titulado
32
+ `[Process Tracking][{proceso}] Execution ID: {id}` — el nombre del proceso
33
+ (skill padre) se declara con `--process-name` y deja claro **qué** se
34
+ rastrea; sin él se conserva el formato legado
35
+ `[Process Tracking] Execution ID: {id}`.
36
+ - **Parámetros de entrada del proceso** (`--process-inputs '{"k":"v",…}`): se
37
+ publican en la sección "Parámetros de entrada" del cuerpo del Issue — la
38
+ constancia de **con qué** se ejecutó la tarea (secretos redactados).
26
39
  - **Cada transición del proceso es un comentario** con un contrato visible
27
40
  cerrado y sin ambigüedades:
28
41
 
@@ -54,6 +67,7 @@ transición. No hay dependencias de terceros: solo Python 3.8+ (stdlib);
54
67
  | | |
55
68
  |---|---|
56
69
  | Entrada obligatoria | `github_project_url` — `https://github.com/{owner}/{repo}` |
70
+ | Identificación del proceso | `--process-name N` (skill padre; título del Issue) y `--process-inputs JSON` (parámetros de entrada, sección del cuerpo) — ambos opcionales pero recomendados |
57
71
  | Salida de cada comando | Un único objeto JSON + exit code documentado |
58
72
  | Permisos requeridos | PAT classic con scope `repo`, o fine-grained con Issues → Read and write |
59
73
  | Plataformas | Linux, macOS, Windows |
@@ -171,7 +185,7 @@ cp -R github-proyect-tracking/ ~/.hermes/profiles/<perfil>/skills/software-devel
171
185
  ### Ejecutar las pruebas
172
186
 
173
187
  ```bash
174
- python3 tests/test_tracking.py # 157 pruebas: 140 offline + 1 skip opt-in
188
+ python3 tests/test_tracking.py # 172 pruebas: 140 offline + 1 skip opt-in
175
189
  ```
176
190
 
177
191
  ---
@@ -334,9 +348,48 @@ code documentado:
334
348
  ```json
335
349
  { "ok": true, "action": "INIT", "execution_id": "exec-20261028T101500Z-ab12cd34ef56",
336
350
  "issue_number": 42, "issue_url": "https://github.com/o/r/issues/42",
351
+ "process_name": "Mi Skill", "issue_title": "[Process Tracking][Mi Skill] Execution ID: exec-20261028T101500Z-ab12cd34ef56",
352
+ "process_inputs": { "archivo": "datos.csv", "modo": "lint" },
337
353
  "state_file": "~/.github-proyect-tracking/o__r.json", "first_step": "Paso 1" }
338
354
  ```
339
355
 
356
+ `process_name` y `process_inputs` son `null` cuando no se pasaron las
357
+ opciones correspondientes.
358
+
359
+ ### Ejemplo del Issue generado
360
+
361
+ Con `init --url … --process-name "Mi Skill" --process-inputs
362
+ '{"archivo":"datos.csv","modo":"lint"}' --plan '["Paso 1","Paso 2"]'`:
363
+
364
+ ```markdown
365
+ [Process Tracking][Mi Skill] Execution ID: exec-20261030T153448Z-9071da2dae76
366
+ ```
367
+ *(título)* — y el cuerpo:
368
+
369
+ ```markdown
370
+ Proceso rastreado por el skill `github-proyect-tracking`.
371
+
372
+ - **Execution ID:** `exec-20261030T153448Z-9071da2dae76`
373
+ - **Proceso:** Mi Skill
374
+ - **Proyecto:** https://github.com/o/r
375
+ - **Iniciado (UTC):** 2026-10-30T15:34:48+00:00
376
+
377
+ ## Parámetros de entrada
378
+
379
+ - **archivo:** `datos.csv`
380
+ - **modo:** `lint`
381
+
382
+ ## Plan de pasos
383
+
384
+ 1. Paso 1
385
+ 2. Paso 2
386
+
387
+ > El avance se registra como comentarios con el contrato `[STATUS: ...]`.
388
+ > El ÚLTIMO comentario es la fuente de verdad para reanudar.
389
+ ```
390
+
391
+ Ejemplo de error (JSON con `ok: false` + exit code documentado):
392
+
340
393
  ```json
341
394
  { "ok": false, "error": { "code": "RESUME_REQUIRED", "message": "...",
342
395
  "state": { "...": "..." }, "next": ["resume"] } }
@@ -361,7 +414,7 @@ Excepciones documentadas al contrato "un JSON por comando": `--help`/
361
414
 
362
415
  | Comando | Efecto | Efectos secundarios |
363
416
  |---|---|---|
364
- | `init --url URL [--plan JSON \| --plan-file F] [--token T] [--store-token] [--process-name N]` | Crea el Issue y la memoria (plan sanitizado; `N` = skill padre, aparece en título/cuerpo del Issue) | Issue nuevo en GitHub + archivo local |
417
+ | `init --url URL [--plan JSON \| --plan-file F] [--token T] [--store-token] [--process-name N] [--process-inputs JSON]` | Crea el Issue y la memoria (plan sanitizado; `N` = skill padre en título/cuerpo; JSON = parámetros de entrada del proceso, publicados en el Issue con secretos redactados) | Issue nuevo en GitHub + archivo local |
365
418
  | `resume [--url URL]` | Veredicto de reanudación | Actualiza memoria local |
366
419
  | `step-start --name N` | Comenta INICIANDO | Comentario + memoria |
367
420
  | `step-done --name N` | Comenta COMPLETADO | Comentario + memoria |
@@ -414,7 +467,7 @@ rechazan.
414
467
 
415
468
  ### 4.6 Pruebas
416
469
 
417
- La suite descubre **157 pruebas**: **140 offline** y una integración real
470
+ La suite descubre **172 pruebas**: **140 offline** y una integración real
418
471
  omitida por defecto. Las pruebas offline usan stdlib, `FakeGateway`, inyección
419
472
  de fallos, subprocesos CLI e instaladores reales en directorios temporales.
420
473
  Cubren los formatos visibles, máquina de estados, reconciliación con UUID y
@@ -423,7 +476,7 @@ contexto ambiguo, seguridad, rollback y contrato JSON.
423
476
 
424
477
  ```bash
425
478
  python3 tests/test_tracking.py
426
- # Ran 157 tests ... OK (skipped=1)
479
+ # Ran 172 tests ... OK (skipped=1)
427
480
  python3 tools/check_docs.py
428
481
  ```
429
482
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-proyect-tracking",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Instalador npx del skill github-proyect-tracking para Hermes Agent: rastrea el estado de procesos en GitHub Issues con reanudacion.",
5
5
  "bin": {
6
6
  "github-proyect-tracking": "bin/cli.js"
package/skill/SKILL.md CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: github-proyect-tracking
3
3
  description: Track process execution state in GitHub Issues with resume.
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  author: Rommel (rommelmg), Hermes Agent
6
6
  license: MIT
7
7
  platforms: [linux, macos, windows]
@@ -100,6 +100,10 @@ exit 0) y `KeyboardInterrupt` (130).
100
100
  `--process-name "Mi Skill"`: el título del Issue pasa a
101
101
  `[Process Tracking][Mi Skill] Execution ID: …` y el cuerpo incluye
102
102
  `- **Proceso:** Mi Skill` — así el Issue identifica QUÉ se rastrea.
103
+ Para dejar constancia de CON QUÉ se ejecutó, añade `--process-inputs
104
+ '{"archivo":"x.csv","modo":"lint"}'` (objeto clave→valor; se publica
105
+ en la sección "Parámetros de entrada" del Issue, con secretos
106
+ redactados).
103
107
  Si devuelve `RESUME_REQUIRED` (exit 7), NO crees otro Issue: ve al
104
108
  paso 3. Criterio: `execution_id`, `process_name` e `issue_url` en la
105
109
  salida.
@@ -42,7 +42,7 @@ texto y los usa para reconciliar únicamente el intento que originó el POST.
42
42
 
43
43
  ## Comandos
44
44
 
45
- ### `init --url URL [--plan JSON | --plan-file F] [--token T] [--prompt-token] [--store-token] [--process-name N]`
45
+ ### `init --url URL [--plan JSON | --plan-file F] [--token T] [--prompt-token] [--store-token] [--process-name N] [--process-inputs JSON]`
46
46
 
47
47
  - Valida la URL (regex, redactada en errores), resuelve el token, valida
48
48
  `issues:write` ANTES de crear nada.
@@ -59,7 +59,15 @@ texto y los usa para reconciliar únicamente el intento que originó el POST.
59
59
  `- **Proceso:** N`; estado y JSON llevan `process_name`. Sin `N`, se
60
60
  conserva el formato legado `[Process Tracking] Execution ID: …`.
61
61
  Un secreto en `N` se redacta; `[`/`]`/saltos → `VALIDATION_ERROR`.
62
- - Salida de `init`: añade `process_name` e `issue_title`.
62
+ - `--process-inputs JSON`: objeto clave→valor con los parámetros de
63
+ entrada del proceso. Se publican en la sección "Parámetros de entrada"
64
+ del cuerpo del Issue (ordenados por clave, valores entre backticks) y
65
+ se guardan en el estado (`process_inputs`) y en la salida de `init`
66
+ (`process_inputs`). Secretos en claves/valores se REDACTAN; límites:
67
+ 50 pares, valores ≤ 2000 caracteres; claves vacías, JSON inválido o no
68
+ objeto → `VALIDATION_ERROR`. Sin la opción, el estado queda limpio
69
+ (`process_inputs: null`) y el cuerpo no tiene la sección.
70
+ - Salida de `init`: añade `process_name`, `process_inputs` e `issue_title`.
63
71
 
64
72
  Salida: `execution_id`, `issue_number`, `issue_url`, `state_file`, `plan`,
65
73
  `first_step`, `token_source`.
@@ -101,6 +101,11 @@ def build_parser():
101
101
  subparser.add_argument("--process-name", default=None, metavar="N",
102
102
  help="nombre del proceso/skill padre que se rastrea "
103
103
  "(aparece en el título y cuerpo del Issue)")
104
+ subparser.add_argument("--process-inputs", default=None, metavar="JSON",
105
+ help='parámetros de entrada del proceso (objeto '
106
+ "clave→valor, se publican en el Issue; los "
107
+ "secretos se redactan), ej. \'{\"archivo\": "
108
+ '\"datos.csv\"}\'')
104
109
  subparser.add_argument("--prompt-token", action="store_true",
105
110
  help="pedir el token interactivamente (TTY)")
106
111
  subparser.add_argument("--store-token", action="store_true",
@@ -97,6 +97,47 @@ def load_plan(args):
97
97
  return plan
98
98
 
99
99
 
100
+ MAX_INPUTS_COUNT = 50
101
+ MAX_INPUT_VALUE_LEN = 2000
102
+
103
+
104
+ def load_process_inputs(args):
105
+ """Valida y normaliza --process-inputs (objeto JSON clave→valor).
106
+
107
+ - claves y valores se convierten a cadena; claves vacías se rechazan
108
+ - secretos en valores (y claves) se REDACTAN antes de guardar/publicar
109
+ - valores se truncan a MAX_INPUT_VALUE_LEN; máx MAX_INPUTS_COUNT pares
110
+ - devuelve None cuando no se pasó la opción (mantiene el estado limpio)
111
+ """
112
+ raw = getattr(args, "process_inputs", None)
113
+ if raw is None:
114
+ return None
115
+ try:
116
+ data = json.loads(raw)
117
+ except json.JSONDecodeError as error:
118
+ raise SkillError(EXIT_VALIDATION, "VALIDATION_ERROR",
119
+ f"--process-inputs no es JSON válido: {error}")
120
+ if not isinstance(data, dict):
121
+ raise SkillError(EXIT_VALIDATION, "VALIDATION_ERROR",
122
+ "--process-inputs debe ser un objeto JSON "
123
+ "clave→valor, ej. '{\"archivo\": \"x.csv\", "
124
+ "\"modo\": \"lint\"}'")
125
+ if len(data) > MAX_INPUTS_COUNT:
126
+ raise SkillError(EXIT_VALIDATION, "VALIDATION_ERROR",
127
+ f"--process-inputs no puede exceder "
128
+ f"{MAX_INPUTS_COUNT} pares")
129
+ inputs = {}
130
+ for key, value in data.items():
131
+ clean_key, _ = sanitize_text(str(key).strip())
132
+ clean_key = clean_key.strip()
133
+ if not clean_key:
134
+ raise SkillError(EXIT_VALIDATION, "VALIDATION_ERROR",
135
+ "--process-inputs: las claves no pueden ser vacías")
136
+ clean_value, _ = sanitize_text(str(value))
137
+ inputs[clean_key] = clean_value[:MAX_INPUT_VALUE_LEN]
138
+ return inputs or None
139
+
140
+
100
141
  def _remote_snapshot(owner, repo, number, token):
101
142
  issue = github.fetch_issue(owner, repo, number, token)
102
143
  comments = github.fetch_all_comments(owner, repo, number, token)
@@ -127,6 +168,7 @@ def cmd_init(args):
127
168
  path = state_file_for(owner, repo)
128
169
  plan = load_plan(args)
129
170
  process_name = normalize_process_name(getattr(args, "process_name", None))
171
+ process_inputs = load_process_inputs(args)
130
172
 
131
173
  lock = StateLock(path)
132
174
  with lock:
@@ -158,7 +200,8 @@ def cmd_init(args):
158
200
  execution_id = new_execution_id()
159
201
  title = issue_title(execution_id, process_name)
160
202
  body = build_issue_body(args.url.strip(), execution_id, plan,
161
- process_name=process_name)
203
+ process_name=process_name,
204
+ process_inputs=process_inputs)
162
205
  recovered = False
163
206
  create_error = None
164
207
  lock.ensure_owner()
@@ -206,6 +249,7 @@ def cmd_init(args):
206
249
  "active_process_id": number,
207
250
  "execution_id": execution_id,
208
251
  "process_name": process_name,
252
+ "process_inputs": process_inputs or None,
209
253
  "issue_url": data.get("html_url"),
210
254
  "process_state": "RUNNING",
211
255
  "current_step": None,
@@ -229,6 +273,7 @@ def cmd_init(args):
229
273
  "execution_id": execution_id, "issue_number": number,
230
274
  "issue_url": data.get("html_url"), "state_file": str(path),
231
275
  "process_name": process_name, "issue_title": title,
276
+ "process_inputs": process_inputs or None,
232
277
  "plan": plan, "first_step": plan[0] if plan else None,
233
278
  "recovered": recovered,
234
279
  "token_source": source, "token_stored": bool(state["github_token"]),
@@ -7,7 +7,7 @@ import re
7
7
  import uuid
8
8
  from datetime import datetime, timezone
9
9
 
10
- VERSION = "0.5.0"
10
+ VERSION = "0.6.0"
11
11
  API_BASE = "https://api.github.com"
12
12
  USER_AGENT = f"github-proyect-tracking/{VERSION}"
13
13
 
@@ -98,7 +98,22 @@ def events_from_comments(comments):
98
98
  return events
99
99
 
100
100
 
101
- def build_issue_body(url, execution_id, plan, process_name=None):
101
+ def _normalized_inputs(process_inputs):
102
+ """Normaliza los parámetros de entrada a pares (clave, valor) ordenados,
103
+ con ambos convertidos a cadena. Devuelve [] si no hay inputs."""
104
+ if not process_inputs:
105
+ return []
106
+ if not isinstance(process_inputs, dict):
107
+ return []
108
+ entries = []
109
+ for key in sorted(process_inputs, key=str):
110
+ value = process_inputs[key]
111
+ entries.append((str(key).strip(), str(value)))
112
+ return [(k, v) for k, v in entries if k]
113
+
114
+
115
+ def build_issue_body(url, execution_id, plan, process_name=None,
116
+ process_inputs=None):
102
117
  lines = [
103
118
  "Proceso rastreado por el skill `github-proyect-tracking`.",
104
119
  "",
@@ -109,6 +124,16 @@ def build_issue_body(url, execution_id, plan, process_name=None):
109
124
  lines += [
110
125
  f"- **Proyecto:** {url}",
111
126
  f"- **Iniciado (UTC):** {now_iso()}",
127
+ ]
128
+
129
+ # v0.6.0: parámetros de entrada del proceso — la única forma de saber
130
+ # exactamente qué tarea estaba realizando esta ejecución.
131
+ entries = _normalized_inputs(process_inputs)
132
+ if entries:
133
+ lines += ["", "## Parámetros de entrada", ""]
134
+ lines += [f"- **{key}:** `{value}`" for key, value in entries]
135
+
136
+ lines += [
112
137
  "",
113
138
  "## Plan de pasos",
114
139
  ]