stackhelx 1.0.0__tar.gz → 1.1.0__tar.gz

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 (63) hide show
  1. {stackhelx-1.0.0 → stackhelx-1.1.0}/CHANGELOG.md +24 -5
  2. {stackhelx-1.0.0 → stackhelx-1.1.0}/PKG-INFO +13 -6
  3. {stackhelx-1.0.0 → stackhelx-1.1.0}/README.md +12 -5
  4. {stackhelx-1.0.0 → stackhelx-1.1.0}/docs/comandos.md +5 -2
  5. {stackhelx-1.0.0 → stackhelx-1.1.0}/docs/plan-expansion.md +29 -29
  6. {stackhelx-1.0.0 → stackhelx-1.1.0}/docs/plan-lenguajes.md +3 -3
  7. {stackhelx-1.0.0 → stackhelx-1.1.0}/stack.example.yaml +3 -3
  8. stackhelx-1.1.0/stackhelx/__init__.py +2 -0
  9. {stackhelx-1.0.0 → stackhelx-1.1.0}/stackhelx/cli.py +15 -1
  10. {stackhelx-1.0.0 → stackhelx-1.1.0}/stackhelx/mcp.py +1 -1
  11. {stackhelx-1.0.0 → stackhelx-1.1.0}/stackhelx/ports.py +4 -4
  12. {stackhelx-1.0.0 → stackhelx-1.1.0}/stackhelx/registry.py +2 -2
  13. {stackhelx-1.0.0 → stackhelx-1.1.0}/stackhelx/runner.py +7 -5
  14. {stackhelx-1.0.0 → stackhelx-1.1.0}/stackhelx/scripts.py +1 -1
  15. {stackhelx-1.0.0 → stackhelx-1.1.0}/stackhelx/server.py +6 -6
  16. {stackhelx-1.0.0 → stackhelx-1.1.0}/stackhelx/tunnel.py +1 -1
  17. {stackhelx-1.0.0 → stackhelx-1.1.0}/stackhelx/web/app.css +102 -0
  18. {stackhelx-1.0.0 → stackhelx-1.1.0}/stackhelx/web/app.js +101 -1
  19. {stackhelx-1.0.0 → stackhelx-1.1.0}/stackhelx/web/index.html +30 -1
  20. {stackhelx-1.0.0 → stackhelx-1.1.0}/tests/test_cli.py +37 -3
  21. {stackhelx-1.0.0 → stackhelx-1.1.0}/tests/test_detect.py +2 -2
  22. {stackhelx-1.0.0 → stackhelx-1.1.0}/tests/test_mcp.py +3 -3
  23. {stackhelx-1.0.0 → stackhelx-1.1.0}/tests/test_runner.py +41 -3
  24. {stackhelx-1.0.0 → stackhelx-1.1.0}/tests/test_scripts.py +1 -1
  25. {stackhelx-1.0.0 → stackhelx-1.1.0}/tests/test_server.py +37 -4
  26. {stackhelx-1.0.0 → stackhelx-1.1.0}/tests/test_tunnel.py +7 -7
  27. stackhelx-1.0.0/stackhelx/__init__.py +0 -2
  28. {stackhelx-1.0.0 → stackhelx-1.1.0}/.github/workflows/ci.yml +0 -0
  29. {stackhelx-1.0.0 → stackhelx-1.1.0}/.github/workflows/release.yml +0 -0
  30. {stackhelx-1.0.0 → stackhelx-1.1.0}/.gitignore +0 -0
  31. {stackhelx-1.0.0 → stackhelx-1.1.0}/AGENTS.md +0 -0
  32. {stackhelx-1.0.0 → stackhelx-1.1.0}/CLAUDE.md +0 -0
  33. {stackhelx-1.0.0 → stackhelx-1.1.0}/LICENSE +0 -0
  34. {stackhelx-1.0.0 → stackhelx-1.1.0}/docs/deteccion.md +0 -0
  35. {stackhelx-1.0.0 → stackhelx-1.1.0}/docs/interfaz.md +0 -0
  36. {stackhelx-1.0.0 → stackhelx-1.1.0}/docs/pendientes.md +0 -0
  37. {stackhelx-1.0.0 → stackhelx-1.1.0}/docs/plan-siguiente.md +0 -0
  38. {stackhelx-1.0.0 → stackhelx-1.1.0}/docs/stack-yaml.md +0 -0
  39. {stackhelx-1.0.0 → stackhelx-1.1.0}/docs/superpowers/plans/2026-08-08-partir-readme.md +0 -0
  40. {stackhelx-1.0.0 → stackhelx-1.1.0}/docs/superpowers/plans/2026-08-08-release-1-0-1.md +0 -0
  41. {stackhelx-1.0.0 → stackhelx-1.1.0}/docs/superpowers/plans/2026-08-18-campo-url.md +0 -0
  42. {stackhelx-1.0.0 → stackhelx-1.1.0}/docs/superpowers/specs/2026-07-29-autodeteccion-design.md +0 -0
  43. {stackhelx-1.0.0 → stackhelx-1.1.0}/pyproject.toml +0 -0
  44. {stackhelx-1.0.0 → stackhelx-1.1.0}/scripts/check_docs.py +0 -0
  45. {stackhelx-1.0.0 → stackhelx-1.1.0}/scripts/check_metadata.py +0 -0
  46. {stackhelx-1.0.0 → stackhelx-1.1.0}/stackhelx/__main__.py +0 -0
  47. {stackhelx-1.0.0 → stackhelx-1.1.0}/stackhelx/browse.py +0 -0
  48. {stackhelx-1.0.0 → stackhelx-1.1.0}/stackhelx/config.py +0 -0
  49. {stackhelx-1.0.0 → stackhelx-1.1.0}/stackhelx/detect.py +0 -0
  50. {stackhelx-1.0.0 → stackhelx-1.1.0}/stackhelx/docker.py +0 -0
  51. {stackhelx-1.0.0 → stackhelx-1.1.0}/stackhelx/doctor.py +0 -0
  52. {stackhelx-1.0.0 → stackhelx-1.1.0}/stackhelx/guardrails.py +0 -0
  53. {stackhelx-1.0.0 → stackhelx-1.1.0}/stackhelx/history.py +0 -0
  54. {stackhelx-1.0.0 → stackhelx-1.1.0}/stackhelx/web/tokens.css +0 -0
  55. {stackhelx-1.0.0 → stackhelx-1.1.0}/tests/conftest.py +0 -0
  56. {stackhelx-1.0.0 → stackhelx-1.1.0}/tests/test_browse.py +0 -0
  57. {stackhelx-1.0.0 → stackhelx-1.1.0}/tests/test_config.py +0 -0
  58. {stackhelx-1.0.0 → stackhelx-1.1.0}/tests/test_conftest.py +0 -0
  59. {stackhelx-1.0.0 → stackhelx-1.1.0}/tests/test_guardrails.py +0 -0
  60. {stackhelx-1.0.0 → stackhelx-1.1.0}/tests/test_history.py +0 -0
  61. {stackhelx-1.0.0 → stackhelx-1.1.0}/tests/test_ports.py +0 -0
  62. {stackhelx-1.0.0 → stackhelx-1.1.0}/tests/test_registry.py +0 -0
  63. {stackhelx-1.0.0 → stackhelx-1.1.0}/uv.lock +0 -0
@@ -4,6 +4,26 @@ Formato de [Keep a Changelog](https://keepachangelog.com/es/1.1.0/).
4
4
  Versionado semántico: la superficie pública son los comandos del CLI, el
5
5
  esquema de `stack.yaml` y las rutas de la API local.
6
6
 
7
+ ## [1.1.0] - 2026-09-18
8
+
9
+ ### Añadido y Mejorado
10
+
11
+ - **Onboarding de Agentes MCP:** Modal web interactivo con panel desplegable colapsable para configurar clientes de IA (Claude Desktop, Cursor, Antigravity, VS Code), con generación de snippets JSON (`shx` / `uvx`), descarga directa de `stackhelx-mcp.json` y copia de System Prompt para agentes.
12
+ - **Banderas CLI para MCP:** Se incorporan las opciones `shx mcp --config` (imprime bloque JSON de configuración) y `shx mcp --prompt` (imprime instrucciones recomendadas para agentes).
13
+ - **Telemetría y Métricas en Tiempo Real:** Tooltip explicativo (`title` y `aria-label`) con cursor `help` sobre el badge de métricas de servicio (`.service__metrics`). Desglosa el uso de CPU multi-core (equivalencia de núcleos activos) y aclara que la memoria es física residente (RSS).
14
+ - **Documentación del alias `shx`:** `README.md` y `docs/comandos.md` documentan formalmente la instalación y uso del comando abreviado `shx` 1:1 junto con `stackhelx`.
15
+ - **Diseño sin emojis:** Estilo visual puro y tipográfico, conforme a los principios de diseño sobrio y accesible.
16
+
17
+ ## [1.0.1] - 2026-09-13
18
+
19
+ ### Seguridad y Limpieza
20
+
21
+ - **Purga de mensajes de seguridad y excepciones (`ports.py`):** Los mensajes de `KillRefused` ante intento de cerrar el PID propio o el de la terminal padre ahora identifican explícitamente a `StackHelx`.
22
+ - **Precedencia de entorno global (`runner.py`):** `build_env()` lee `~/.stackhelx/env.global` y mantiene fallback transparente a `~/.portmaster/env.global` si existe, con pruebas de precedencia añadidas a la suite.
23
+ - **Títulos y logs de servidor (`server.py`):** Título de FastAPI fijado a `StackHelx`, logger migrado a `stackhelx.server`, y mensajes de guarda en `/api/share` actualizados para rechazar túneles contra la consola de `StackHelx`.
24
+ - **Plantilla de configuración (`stack.example.yaml`):** Actualizada para documentar `stackhelx init` y la nueva identidad de StackHelx.
25
+ - **Suite de pruebas:** 474 pruebas en verde (+1 test de precedencia de entorno global).
26
+
7
27
  ## [1.0.0] - 2026-09-13
8
28
 
9
29
  ### Rebrand y Nuevo Comienzo
@@ -548,8 +568,7 @@ Primera versión publicada. Lo que sigue es el alcance completo, no un diff.
548
568
  [1.3.0]: https://github.com/TicoraX/PortMaster/releases/tag/v1.3.0
549
569
  [1.2.0]: https://github.com/TicoraX/PortMaster/releases/tag/v1.2.0
550
570
  [1.1.1]: https://github.com/TicoraX/PortMaster/releases/tag/v1.1.1
551
- [1.1.0]: https://github.com/TicoraX/PortMaster/releases/tag/v1.1.0
552
- [1.0.3]: https://github.com/TicoraX/PortMaster/releases/tag/v1.0.3
553
- [1.0.2]: https://github.com/TicoraX/PortMaster/releases/tag/v1.0.2
554
- [1.0.1]: https://github.com/TicoraX/PortMaster/releases/tag/v1.0.1
555
- [1.0.0]: https://github.com/TicoraX/PortMaster/releases/tag/v1.0.0
571
+ [1.1.0]: https://github.com/TicoraX/StackHelx/releases/tag/v1.1.0
572
+ [1.0.1]: https://github.com/TicoraX/StackHelx/releases/tag/v1.0.1
573
+ [1.0.0]: https://github.com/TicoraX/StackHelx/releases/tag/v1.0.0
574
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: stackhelx
3
- Version: 1.0.0
3
+ Version: 1.1.0
4
4
  Summary: Orquestador de entornos de desarrollo locales: puertos, Docker, backend y frontend con un solo comando
5
5
  Project-URL: Homepage, https://github.com/TicoraX/StackHelx
6
6
  Project-URL: Repository, https://github.com/TicoraX/StackHelx
@@ -59,7 +59,7 @@ Description-Content-Type: text/markdown
59
59
 
60
60
  # StackHelx
61
61
 
62
- [![pypi](https://img.shields.io/pypi/v/stackhelx)](https://pypi.org/project/stackhelx/)
62
+ [![pypi](https://img.shields.io/pypi/v/stackhelx?logo=pypi&logoColor=white)](https://pypi.org/project/stackhelx/)
63
63
  [![tests](https://github.com/TicoraX/StackHelx/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/TicoraX/StackHelx/actions/workflows/ci.yml)
64
64
  [![license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
65
65
  [![python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](pyproject.toml)
@@ -76,11 +76,15 @@ uv tool install stackhelx
76
76
  pipx install stackhelx
77
77
  ```
78
78
 
79
+ La instalación registra dos ejecutables idénticos en el sistema: el comando principal **`stackhelx`** y su versión abreviada **`shx`**.
80
+
79
81
  Requiere Python 3.10 o superior. Funciona en Windows, macOS y Linux.
80
82
 
81
83
  ## Comandos
82
84
 
83
- | Comando | Qué hace |
85
+ Todos los comandos pueden ejecutarse como `stackhelx <comando>` o de forma abreviada con su alias oficial **`shx <comando>`** (ej. `shx up`, `shx down`, `shx doctor`, `shx ports`):
86
+
87
+ | Comando (`stackhelx` / `shx`) | Qué hace |
84
88
  |---|---|
85
89
  | `stackhelx up` | Levanta el stack entero: libera puertos, arranca en orden y sigue los logs |
86
90
  | `stackhelx down` | Baja lo que sobrevive a la terminal, o sea contenedores |
@@ -113,10 +117,13 @@ Cada uno con `--help`.
113
117
  ## Arrancar un stack
114
118
 
115
119
  ```bash
120
+ shx up
121
+ # o
116
122
  stackhelx up
117
- stackhelx up --profile backend # solo un subconjunto
118
- stackhelx up --no-free # no tocar los puertos ocupados
119
- stackhelx up --env-file .env.qa # carga ese .env antes de arrancar
123
+
124
+ shx up --profile backend # solo un subconjunto
125
+ shx up --no-free # no tocar los puertos ocupados
126
+ shx up --env-file .env.qa # carga ese .env antes de arrancar
120
127
  ```
121
128
 
122
129
  `--env-file` no reemplaza al `env_file:` de `stack.yaml`, se suma: carga el
@@ -1,6 +1,6 @@
1
1
  # StackHelx
2
2
 
3
- [![pypi](https://img.shields.io/pypi/v/stackhelx)](https://pypi.org/project/stackhelx/)
3
+ [![pypi](https://img.shields.io/pypi/v/stackhelx?logo=pypi&logoColor=white)](https://pypi.org/project/stackhelx/)
4
4
  [![tests](https://github.com/TicoraX/StackHelx/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/TicoraX/StackHelx/actions/workflows/ci.yml)
5
5
  [![license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6
6
  [![python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](pyproject.toml)
@@ -17,11 +17,15 @@ uv tool install stackhelx
17
17
  pipx install stackhelx
18
18
  ```
19
19
 
20
+ La instalación registra dos ejecutables idénticos en el sistema: el comando principal **`stackhelx`** y su versión abreviada **`shx`**.
21
+
20
22
  Requiere Python 3.10 o superior. Funciona en Windows, macOS y Linux.
21
23
 
22
24
  ## Comandos
23
25
 
24
- | Comando | Qué hace |
26
+ Todos los comandos pueden ejecutarse como `stackhelx <comando>` o de forma abreviada con su alias oficial **`shx <comando>`** (ej. `shx up`, `shx down`, `shx doctor`, `shx ports`):
27
+
28
+ | Comando (`stackhelx` / `shx`) | Qué hace |
25
29
  |---|---|
26
30
  | `stackhelx up` | Levanta el stack entero: libera puertos, arranca en orden y sigue los logs |
27
31
  | `stackhelx down` | Baja lo que sobrevive a la terminal, o sea contenedores |
@@ -54,10 +58,13 @@ Cada uno con `--help`.
54
58
  ## Arrancar un stack
55
59
 
56
60
  ```bash
61
+ shx up
62
+ # o
57
63
  stackhelx up
58
- stackhelx up --profile backend # solo un subconjunto
59
- stackhelx up --no-free # no tocar los puertos ocupados
60
- stackhelx up --env-file .env.qa # carga ese .env antes de arrancar
64
+
65
+ shx up --profile backend # solo un subconjunto
66
+ shx up --no-free # no tocar los puertos ocupados
67
+ shx up --env-file .env.qa # carga ese .env antes de arrancar
61
68
  ```
62
69
 
63
70
  `--env-file` no reemplaza al `env_file:` de `stack.yaml`, se suma: carga el
@@ -1,13 +1,16 @@
1
1
  # Otros comandos
2
2
 
3
+ > **Nota:** Todos los comandos del sistema pueden ejecutarse indistintamente con `stackhelx <cmd>` o mediante el alias corto oficial **`shx <cmd>`** (por ejemplo: `shx down`, `shx doctor`, `shx switch`, `shx run`, `shx share`).
4
+
3
5
  `up`, `serve`, `ports` y `free` están en el [README](../README.md#comandos).
4
6
  Acá están los cuatro que quedan, con qué revisa cada uno y por qué.
5
7
 
6
8
  ## Bajar lo que sobrevive a la terminal
7
9
 
8
10
  ```bash
9
- stackhelx down
10
- stackhelx down --profile backend
11
+ shx down
12
+ # o stackhelx down
13
+ shx down --profile backend
11
14
  ```
12
15
 
13
16
  `Ctrl-C` sobre un `stackhelx up` apaga a sus hijos, pero un
@@ -1,9 +1,9 @@
1
- # Plan de Expansión Estratégica: PortMaster 2.0 (Ecosistema Completo de Desarrollo)
1
+ # Plan de Expansión Estratégica: StackHelx (Ecosistema Completo de Desarrollo)
2
2
 
3
3
  Fecha: 14 de agosto de 2026.
4
4
  Rama: `feature/expansion-plan`
5
5
 
6
- Este documento define la evolución de PortMaster desde un orquestador de *stacks* locales hacia una **plataforma integral de productividad, diagnóstico, automatización y control del entorno de desarrollo local**.
6
+ Este documento define la evolución de StackHelx desde un orquestador de *stacks* locales hacia una **plataforma integral de productividad, diagnóstico, automatización y control del entorno de desarrollo local**.
7
7
 
8
8
  ---
9
9
 
@@ -11,17 +11,17 @@ Este documento define la evolución de PortMaster desde un orquestador de *stack
11
11
 
12
12
  ```
13
13
  ┌─────────────────────────────────────────────────────────────────────────────┐
14
- │ PORTMASTER ECOSYSTEM │
14
+ │ STACKHELX ECOSYSTEM │
15
15
  ├───────────────────────────────┬───────────────────────────────┬─────────────┤
16
16
  │ 1. ORQUESTACIÓN DE STACK │ 2. RUNNER DE TAREAS │ 3. TÚNELES │
17
17
  │ - Topological sort & health │ - Scripts y pipelines locales │ - ngrok │
18
18
  │ - env_file & pre_start hooks │ - Inyección de variables │ - cloudflare│
19
- │ - Monorrepos (pnpm/turbo/uv) │ - portmaster run <tarea> │ - share │
19
+ │ - Monorrepos (pnpm/turbo/uv) │ - stackhelx run <tarea> │ - share │
20
20
  ├───────────────────────────────┼───────────────────────────────┼─────────────┤
21
21
  │ 4. MULTI-PROYECTO COMPUESTO │ 5. HIGIENE Y MANTENIMIENTO │ 6. MCP & AI │
22
22
  │ - Inclusión inter-repositorios│ - Docker prune inteligente │ - Tools MCP │
23
23
  │ - Grupos de servicios │ - Detección de zombies │ - AI Agent │
24
- │ - Matriz de colisiones │ - portmaster clean │ Diagnosis │
24
+ │ - Matriz de colisiones │ - stackhelx clean │ Diagnosis │
25
25
  └───────────────────────────────┴───────────────────────────────┴─────────────┘
26
26
  ```
27
27
 
@@ -30,15 +30,15 @@ Este documento define la evolución de PortMaster desde un orquestador de *stack
30
30
  ## 2. Especificación de Fases y Módulos
31
31
 
32
32
  ### Fase 1: Variables de Entorno y Hooks del Stack
33
- * **Módulo**: [`portmaster/config.py`](../portmaster/config.py), [`portmaster/runner.py`](../portmaster/runner.py)
33
+ * **Módulo**: [`stackhelx/config.py`](../stackhelx/config.py), [`stackhelx/runner.py`](../stackhelx/runner.py)
34
34
  * **`env_file`**: Carga de `.env`, `.env.local` o lista ordenada sin dependencias externas.
35
35
  * **Hooks de Ciclo de Vida**:
36
36
  * `pre_start`: Ejecución síncrona preparatoria (ej. migraciones, build).
37
37
  * `post_start`: Ejecución tras confirmación de salud (ej. `seed`, notificación).
38
- * **Bóveda Global (`~/.portmaster/env.global`)**: Herencia automática de variables comunes entre proyectos.
38
+ * **Bóveda Global (`~/.stackhelx/env.global`)**: Herencia automática de variables comunes entre proyectos.
39
39
 
40
- ### Fase 2: Runner de Tareas y Scripts de Proyecto (`portmaster run`)
41
- * **Módulo**: [`portmaster/scripts.py`](../portmaster/scripts.py), [`portmaster/cli.py`](../portmaster/cli.py)
40
+ ### Fase 2: Runner de Tareas y Scripts de Proyecto (`stackhelx run`)
41
+ * **Módulo**: [`stackhelx/scripts.py`](../stackhelx/scripts.py), [`stackhelx/cli.py`](../stackhelx/cli.py)
42
42
  * **Declaración en `stack.yaml`**:
43
43
  ```yaml
44
44
  scripts:
@@ -47,15 +47,15 @@ Este documento define la evolución de PortMaster desde un orquestador de *stack
47
47
  migrate: alembic upgrade head
48
48
  check: [lint, test] # Pipeline secuencial
49
49
  ```
50
- * **Comando CLI**: `portmaster run <script>` ejecuta en la raíz del proyecto, con el contexto de variables del stack.
50
+ * **Comando CLI**: `stackhelx run <script>` ejecuta en la raíz del proyecto, con el contexto de variables del stack.
51
51
 
52
- ### Fase 3: Exposición Segura y Compartir (`portmaster share`)
53
- * **Módulo**: [`portmaster/tunnel.py`](../portmaster/tunnel.py), [`portmaster/cli.py`](../portmaster/cli.py)
52
+ ### Fase 3: Exposición Segura y Compartir (`stackhelx share`)
53
+ * **Módulo**: [`stackhelx/tunnel.py`](../stackhelx/tunnel.py), [`stackhelx/cli.py`](../stackhelx/cli.py)
54
54
  * **Integración de Túneles**: Detección y manejo de binarios locales (`cloudflared`, `ngrok`, `tailscale`).
55
- * **Comando CLI & Web**: `portmaster share web` genera URL pública temporal, QR en consola y botón directo en la UI web.
55
+ * **Comando CLI & Web**: `stackhelx share web` genera URL pública temporal, QR en consola y botón directo en la UI web.
56
56
 
57
57
  ### Fase 4: Orquestación Multi-Proyecto y Dependencias Inter-Repositorios
58
- * **Módulo**: [`portmaster/registry.py`](../portmaster/registry.py), [`portmaster/runner.py`](../portmaster/runner.py)
58
+ * **Módulo**: [`stackhelx/registry.py`](../stackhelx/registry.py), [`stackhelx/runner.py`](../stackhelx/runner.py)
59
59
  * **Proyectos Compuestos (`includes`)**:
60
60
  ```yaml
61
61
  # frontend/stack.yaml
@@ -63,28 +63,28 @@ Este documento define la evolución de PortMaster desde un orquestador de *stack
63
63
  includes:
64
64
  - ../backend-api # Levanta backend automáticamente si no está corriendo
65
65
  ```
66
- * **Grupos de Proyectos**: `portmaster group up <nombre_grupo>` y matriz preventiva de colisión de puertos.
66
+ * **Grupos de Proyectos**: `stackhelx group up <nombre_grupo>` y matriz preventiva de colisión de puertos.
67
67
 
68
- ### Fase 5: Higiene del Sistema y Docker (`portmaster clean`)
69
- * **Módulo**: [`portmaster/docker.py`](../portmaster/docker.py), [`portmaster/cli.py`](../portmaster/cli.py)
68
+ ### Fase 5: Higiene del Sistema y Docker (`stackhelx clean`)
69
+ * **Módulo**: [`stackhelx/docker.py`](../stackhelx/docker.py), [`stackhelx/cli.py`](../stackhelx/cli.py)
70
70
  * **Limpieza Asistida**:
71
- * `portmaster clean` (con `--volumes` opcional): Remueve contenedores parados, imágenes huérfanas, build-cache y redes no usadas.
72
- * `portmaster doctor`: Diagnóstico integral del entorno local y detección de problemas.
71
+ * `stackhelx clean` (con `--volumes` opcional): Remueve contenedores parados, imágenes huérfanas, build-cache y redes no usadas.
72
+ * `stackhelx doctor`: Diagnóstico integral del entorno local y detección de problemas.
73
73
 
74
74
  ### Fase 6: Servidor MCP e Integración con Agentes de IA
75
- * **Módulo**: [`portmaster/mcp.py`](../portmaster/mcp.py)
75
+ * **Módulo**: [`stackhelx/mcp.py`](../stackhelx/mcp.py)
76
76
  * **Herramientas MCP Expuestas**:
77
- * `portmaster_status`: Estado de salud, puertos y procesos.
78
- * `portmaster_restart`: Reinicio atómico de un servicio específico.
79
- * `portmaster_logs`: Consulta de logs con filtrado para depuración autónoma.
80
- * `portmaster_free_port`: Liberación de puertos conflictivos.
81
- * `portmaster_clean`: Limpieza segura de recursos Docker.
82
- * `portmaster_doctor`: Diagnóstico integral del sistema.
83
- * `portmaster_history`: Historial de arranques de proyectos.
84
- * `portmaster_init`: Inicialización asistida de `stack.yaml`.
77
+ * `stackhelx_status`: Estado de salud, puertos y procesos.
78
+ * `stackhelx_restart`: Reinicio atómico de un servicio específico.
79
+ * `stackhelx_logs`: Consulta de logs con filtrado para depuración autónoma.
80
+ * `stackhelx_free_port`: Liberación de puertos conflictivos.
81
+ * `stackhelx_clean`: Limpieza segura de recursos Docker.
82
+ * `stackhelx_doctor`: Diagnóstico integral del sistema.
83
+ * `stackhelx_history`: Historial de arranques de proyectos.
84
+ * `stackhelx_init`: Inicialización asistida de `stack.yaml`.
85
85
 
86
86
  ### Fase 7: Detección Avanzada (Monorrepos, `uv`, Frameworks) y UI Web Polish
87
- * **Módulo**: [`portmaster/detect.py`](../portmaster/detect.py), [`portmaster/web/`](../portmaster/web)
87
+ * **Módulo**: [`stackhelx/detect.py`](../stackhelx/detect.py), [`stackhelx/web/`](../stackhelx/web)
88
88
  * Detección de `pnpm-workspace.yaml`, `turbo.json`, `uv.lock`, Astro (`4321`), Vite (`5173`).
89
89
  * UI Web: Pausa y búsqueda en streaming de logs, editor de `stack.yaml` y disparador de tareas `run`.
90
90
 
@@ -15,7 +15,7 @@
15
15
 
16
16
  Fecha: 7 de septiembre de 2026. Rama `main`, 432 tests en verde.
17
17
 
18
- PortMaster detecta hoy compose, Python, Node, Deno, Go, Rust, Ruby, PHP y .NET.
18
+ StackHelx detecta hoy compose, Python, Node, Deno, Go, Rust, Ruby, PHP y .NET.
19
19
  Una carpeta con un proyecto de otro lenguaje no devuelve nada y obliga a escribir
20
20
  el `stack.yaml` a mano. Este plan cierra los tres huecos que tienen usuarios
21
21
  reales de desarrollo local multi-servicio: la JVM (Java y Kotlin), Elixir y Bun
@@ -298,8 +298,8 @@ primero). Y se verifica revirtiendo, como todo lo demás acá.
298
298
 
299
299
  Cada PR toca:
300
300
 
301
- - `portmaster/detect.py`: las dos funciones, sus constantes, y el nombre en la tupla de `detect()`.
302
- - `portmaster/browse.py`: el marcador nuevo en `MARKERS` (`pom.xml`, `build.gradle`, `mix.exs`, `bunfig.toml`), que es lo que pinta el badge en el explorador de carpetas.
301
+ - `stackhelx/detect.py`: las dos funciones, sus constantes, y el nombre en la tupla de `detect()`.
302
+ - `stackhelx/browse.py`: el marcador nuevo en `MARKERS` (`pom.xml`, `build.gradle`, `mix.exs`, `bunfig.toml`), que es lo que pinta el badge en el explorador de carpetas.
303
303
  - `tests/test_detect.py`: los casos de arriba, negativo incluido.
304
304
  - `docs/deteccion.md`, `README.md` y `CHANGELOG.md`. De `CLAUDE.md`: lo que se escribe en la doc se comprueba corriendo el comando, no recordándolo.
305
305
 
@@ -1,7 +1,7 @@
1
- # Esquema de configuracion de PortMaster.
1
+ # Esquema de configuracion de StackHelx.
2
2
  #
3
- # El archivo es opcional: sin uno, PortMaster detecta los servicios del proyecto.
4
- # `portmaster init` escribe lo detectado aca para editarlo a mano.
3
+ # El archivo es opcional: sin uno, StackHelx detecta los servicios del proyecto.
4
+ # `stackhelx init` (o `shx init`) escribe lo detectado aca para editarlo a mano.
5
5
 
6
6
  name: mi-proyecto
7
7
 
@@ -0,0 +1,2 @@
1
+ __version__ = "1.1.0"
2
+
@@ -892,19 +892,33 @@ def mcp_cmd(
892
892
  "-c",
893
893
  help="Muestra el bloque de configuracion JSON para Claude Desktop, Cursor o Antigravity.",
894
894
  ),
895
+ show_prompt: bool = typer.Option(
896
+ False,
897
+ "--prompt",
898
+ "-p",
899
+ help="Muestra las instrucciones recomendadas para el agente de IA.",
900
+ ),
895
901
  ) -> None:
896
902
  """Inicia el servidor Model Context Protocol (MCP) sobre stdio para agentes de IA."""
897
903
  if show_config:
898
904
  cfg = {
899
905
  "mcpServers": {
900
906
  "stackhelx": {
901
- "command": "stackhelx",
907
+ "command": "shx",
902
908
  "args": ["mcp"],
903
909
  }
904
910
  }
905
911
  }
906
912
  console.print(json.dumps(cfg, indent=2))
907
913
  return
914
+ if show_prompt:
915
+ console.print(
916
+ "Tienes a tu disposición las herramientas MCP de StackHelx (`stackhelx_*`). "
917
+ "Úsalas para inspeccionar el estado de puertos (`stackhelx_ports`), diagnosticar problemas de entorno (`stackhelx_doctor`), "
918
+ "liberar puertos en conflicto (`stackhelx_free`), levantar el stack del proyecto (`stackhelx_up`), "
919
+ "apagarlo (`stackhelx_down`), ejecutar scripts declarados (`stackhelx_run`) y compartir servicios vía túneles HTTPS (`stackhelx_share`)."
920
+ )
921
+ return
908
922
  mcp.serve_stdio()
909
923
 
910
924
 
@@ -420,7 +420,7 @@ def _reservar_stdout():
420
420
  """Deja el descriptor 1 solo para el protocolo y manda lo demas a stderr.
421
421
 
422
422
  Sobre stdio el JSON-RPC comparte el descriptor 1 con todo lo que imprima el
423
- proceso. `portmaster_run` lanza los comandos del usuario heredando ese
423
+ proceso. `stackhelx_run` (o `portmaster_run`) lanza los comandos del usuario heredando ese
424
424
  descriptor, asi que un `echo` adentro de un script se metia entre dos
425
425
  respuestas y el cliente perdia la sesion.
426
426
 
@@ -39,7 +39,7 @@ TERMINATE_TIMEOUT = 5
39
39
 
40
40
 
41
41
  class KillRefused(Exception):
42
- """El proceso existe pero PortMaster se niega a matarlo."""
42
+ """El proceso existe pero StackHelx se niega a matarlo."""
43
43
 
44
44
 
45
45
  @dataclass(frozen=True)
@@ -356,7 +356,7 @@ def kill(
356
356
  no existe, y psutil.AccessDenied si faltan permisos.
357
357
  """
358
358
  # psutil.Process(None) es el proceso actual: sin esto, un scan que no vio al
359
- # dueno del puerto termina en PortMaster matandose a si mismo.
359
+ # dueno del puerto termina en StackHelx matandose a si mismo.
360
360
  if pid is None:
361
361
  raise KillRefused("no hay PID que cerrar")
362
362
  if pid in PROTECTED_PIDS:
@@ -364,9 +364,9 @@ def kill(
364
364
 
365
365
  me = psutil.Process()
366
366
  if pid == me.pid:
367
- raise KillRefused("ese PID es PortMaster")
367
+ raise KillRefused("ese PID es StackHelx")
368
368
  if pid in {ancestor.pid for ancestor in me.parents()}:
369
- raise KillRefused(f"PID {pid} es un proceso padre de PortMaster (tu terminal)")
369
+ raise KillRefused(f"PID {pid} es un proceso padre de StackHelx (tu terminal)")
370
370
 
371
371
  proc = psutil.Process(pid)
372
372
  if create_time is not None and proc.create_time() != create_time:
@@ -116,7 +116,7 @@ _docker_cache: tuple[float, bool] | None = None
116
116
  def declared_ports(max_age: float = 0.0) -> dict[int, list[Path]]:
117
117
  """Puerto declarado -> proyectos registrados que lo piden.
118
118
 
119
- Es el unico dato que PortMaster tiene y las herramientas de un proyecto solo
119
+ Es el unico dato que StackHelx tiene y las herramientas de un proyecto solo
120
120
  no pueden tener: cada compose se conoce a si mismo y ninguno sabe del de al
121
121
  lado. Sin esto, que dos proyectos peleen por el 3000 se descubre cuando el
122
122
  segundo no arranca.
@@ -251,7 +251,7 @@ def _save(items: list[Path]) -> None:
251
251
  def token() -> str:
252
252
  """Token de la API local.
253
253
 
254
- Prioriza PORTMASTER_TOKEN. Si no esta, usa uno generado en el directorio del
254
+ Prioriza STACKHELX_TOKEN (con fallback a PORTMASTER_TOKEN). Si no esta, usa uno generado en el directorio del
255
255
  usuario. Ver la desviacion documentada en CLAUDE.md: una herramienta que se
256
256
  instala con pipx no puede traer un .env, y un token generado con permisos
257
257
  0600 fuera del repo es mas seguro que uno que el usuario copia a mano.
@@ -32,13 +32,15 @@ from .config import Service, Stack
32
32
  def build_env(service: Service, extra_env: dict[str, str] | None = None) -> dict[str, str]:
33
33
  """Construye el entorno de ejecucion con precedencia clara:
34
34
  1. os.environ
35
- 2. ~/.portmaster/env.global (si existe)
35
+ 2. ~/.stackhelx/env.global (o legacy ~/.portmaster/env.global si existe)
36
36
  3. service.env_file (en orden)
37
37
  4. service.env (declarado explicito)
38
38
  5. extra_env (e.g. desde --env-file en CLI)
39
39
  """
40
40
  env = dict(os.environ)
41
- global_env = Path.home() / ".portmaster" / "env.global"
41
+ global_env = Path.home() / ".stackhelx" / "env.global"
42
+ if not global_env.is_file():
43
+ global_env = Path.home() / ".portmaster" / "env.global"
42
44
  if global_env.is_file():
43
45
  env.update(config.parse_env_file(global_env))
44
46
  for env_path in service.env_file:
@@ -346,7 +348,7 @@ class Runner:
346
348
  if estrenados:
347
349
  # Un solo respiro para todo el lote, no uno por proceso: sin esto,
348
350
  # la primera lectura de un servicio recien arrancado seria 0.0 y
349
- # `portmaster stats`, que hace una sola consulta, nunca mediria nada.
351
+ # `stackhelx stats`, que hace una sola consulta, nunca mediria nada.
350
352
  time.sleep(CPU_MUESTRA)
351
353
 
352
354
  vivos = {proc.pid for arbol in arboles.values() for proc in arbol}
@@ -692,7 +694,7 @@ def dependency_graph(stack: Stack, profile: str | None = None) -> dict:
692
694
  def run_stop(service: Service, extra_env: dict[str, str] | None = None) -> subprocess.CompletedProcess | None:
693
695
  """Corre el `stop:` de un servicio. None si no termino a tiempo.
694
696
 
695
- Vive afuera del `Runner` porque `portmaster down` tiene que poder apagar un
697
+ Vive afuera del `Runner` porque `stackhelx down` tiene que poder apagar un
696
698
  stack que arranco otro proceso: los contenedores de un `docker compose up -d`
697
699
  sobreviven a la terminal que los levanto, y ahi no hay ningun `Proc` vivo del
698
700
  que colgarse.
@@ -755,7 +757,7 @@ def _port_hint(proc: Proc) -> str:
755
757
  quien = dueno.name or f"pid {dueno.pid}"
756
758
  return (
757
759
  f"; el puerto {declarado} ya lo tenia {quien} (pid {dueno.pid}):"
758
- f" liberalo con `portmaster free {declarado}` y reintenta"
760
+ f" liberalo con `stackhelx free {declarado}` y reintenta"
759
761
  )
760
762
  return ""
761
763
 
@@ -33,7 +33,7 @@ def _entrecomillar(args: Sequence[str]) -> str:
33
33
 
34
34
  Iban con un `" ".join`, y estos comandos corren con `shell=True`: un
35
35
  argumento con un separador ejecutaba lo que viniera despues. Desde el CLI lo
36
- escribe el usuario, pero `portmaster_run` los recibe de un agente de IA.
36
+ escribe el usuario, pero `stackhelx_run` (o `portmaster_run`) los recibe de un agente de IA.
37
37
 
38
38
  Por plataforma, porque el shell no es el mismo. `shlex.join` entrecomilla al
39
39
  estilo POSIX y `cmd.exe` no entiende las comillas simples: un `&` adentro le
@@ -54,7 +54,7 @@ from . import (
54
54
  # `browse_module` porque el endpoint de /api/browse ya se llama browse.
55
55
  from . import browse as browse_module
56
56
 
57
- log = logging.getLogger("portmaster.server")
57
+ log = logging.getLogger("stackhelx.server")
58
58
 
59
59
  WEB = Path(__file__).parent / "web"
60
60
  LOG_LINES = 500
@@ -265,7 +265,7 @@ class Session:
265
265
  self.stopped_by_user = False
266
266
  self.state = "starting"
267
267
  self.sink.write(
268
- f"\n[portmaster] Conmutando al perfil '{new_profile or 'default'}'...\n"
268
+ f"\n[stackhelx] Conmutando al perfil '{new_profile or 'default'}'...\n"
269
269
  )
270
270
  console = Console(
271
271
  file=self.sink, force_terminal=False, no_color=True, width=160, soft_wrap=True
@@ -695,7 +695,7 @@ def _cerrar_tuneles() -> None:
695
695
  async def _ciclo_de_vida(app: FastAPI):
696
696
  """Al apagar, cierra los tuneles.
697
697
 
698
- Sin esto `portmaster serve` terminaba y el cliente de tuneles seguia vivo,
698
+ Sin esto `stackhelx serve` terminaba y el cliente de tuneles seguia vivo,
699
699
  con el puerto expuesto a internet, sin nada en pantalla que lo dijera y sin
700
700
  forma de cerrarlo que no fuera matarlo a mano.
701
701
  """
@@ -707,7 +707,7 @@ def create_app(token: str | None = None) -> FastAPI:
707
707
  _load_sessions_state()
708
708
  token = token or registry.token()
709
709
  app = FastAPI(
710
- title="PortMaster",
710
+ title="StackHelx",
711
711
  docs_url=None,
712
712
  redoc_url=None,
713
713
  openapi_url=None,
@@ -1520,7 +1520,7 @@ def create_app(token: str | None = None) -> FastAPI:
1520
1520
  log.info("puerto rechazado: %s", exc)
1521
1521
  raise HTTPException(400, str(exc))
1522
1522
 
1523
- # PortMaster no se publica a si mismo. Detras de este puerto esta la API
1523
+ # StackHelx no se publica a si mismo. Detras de este puerto esta la API
1524
1524
  # que arranca stack.yaml, o sea ejecucion de comandos: exponerla deja al
1525
1525
  # token como unica puerta contra internet entero. La validacion de Host
1526
1526
  # del middleware ya rechaza al cliente de tuneles, asi que hoy el efecto
@@ -1534,7 +1534,7 @@ def create_app(token: str | None = None) -> FastAPI:
1534
1534
  if propio is not None and port == propio:
1535
1535
  raise HTTPException(
1536
1536
  400,
1537
- f"el puerto {port} es el de PortMaster. Publicarlo expone la API "
1537
+ f"el puerto {port} es el de StackHelx. Publicarlo expone la API "
1538
1538
  "que ejecuta los comandos de tu stack.yaml, no tu proyecto.",
1539
1539
  )
1540
1540
 
@@ -146,7 +146,7 @@ def start_tunnel(
146
146
 
147
147
  # Mirando tambien si el proceso se murio, y no solo el reloj. Un cliente que
148
148
  # falla al arrancar (`ngrok` sin autenticar) se va en menos de un segundo, y
149
- # esperarle el plazo entero dejaba a `portmaster share` pareciendo colgado
149
+ # esperarle el plazo entero dejaba a `stackhelx share` pareciendo colgado
150
150
  # antes de dar un error que ya se sabia.
151
151
  limite = time.monotonic() + timeout
152
152
  while not ready_event.wait(0.1):
@@ -635,6 +635,7 @@ body {
635
635
  border-radius: var(--radius-sm);
636
636
  white-space: nowrap;
637
637
  align-self: center;
638
+ cursor: help;
638
639
  }
639
640
 
640
641
  .service__act {
@@ -1653,6 +1654,107 @@ body {
1653
1654
  margin-top: var(--space-sm);
1654
1655
  }
1655
1656
 
1657
+ .mcp__setup {
1658
+ border: var(--rule) solid var(--color-rule);
1659
+ border-radius: var(--radius);
1660
+ background: var(--color-paper-2);
1661
+ overflow: hidden;
1662
+ }
1663
+
1664
+ .mcp__setup-summary {
1665
+ display: flex;
1666
+ align-items: center;
1667
+ justify-content: space-between;
1668
+ padding: var(--space-xs) var(--space-sm);
1669
+ font-size: var(--text-sm);
1670
+ font-weight: 500;
1671
+ color: var(--color-ink-2);
1672
+ cursor: pointer;
1673
+ user-select: none;
1674
+ list-style: none;
1675
+ }
1676
+
1677
+ .mcp__setup-summary::-webkit-details-marker {
1678
+ display: none;
1679
+ }
1680
+
1681
+ .mcp__setup-summary:hover {
1682
+ color: var(--color-ink);
1683
+ background: var(--color-paper-3);
1684
+ }
1685
+
1686
+ .mcp__setup-summary:focus-visible {
1687
+ outline: 2px solid var(--color-focus);
1688
+ outline-offset: -2px;
1689
+ }
1690
+
1691
+ .mcp__setup-icon {
1692
+ font-size: var(--text-xs);
1693
+ transition: transform 0.15s ease;
1694
+ }
1695
+
1696
+ .mcp__setup[open] .mcp__setup-icon {
1697
+ transform: rotate(180deg);
1698
+ }
1699
+
1700
+ .mcp__setup-body {
1701
+ padding: var(--space-sm) var(--space-md) var(--space-md);
1702
+ border-top: var(--rule) solid var(--color-rule);
1703
+ display: flex;
1704
+ flex-direction: column;
1705
+ gap: var(--space-xs);
1706
+ }
1707
+
1708
+ .mcp__setup-tabs {
1709
+ display: flex;
1710
+ gap: var(--space-md);
1711
+ font-size: var(--text-xs);
1712
+ color: var(--color-ink-2);
1713
+ }
1714
+
1715
+ .mcp__setup-radio {
1716
+ display: inline-flex;
1717
+ align-items: center;
1718
+ gap: 0.35rem;
1719
+ cursor: pointer;
1720
+ }
1721
+
1722
+ .mcp__code {
1723
+ font-family: var(--font-mono);
1724
+ font-size: var(--text-xs);
1725
+ background: var(--color-paper);
1726
+ border: var(--rule) solid var(--color-rule);
1727
+ border-radius: var(--radius-sm);
1728
+ padding: var(--space-xs) var(--space-sm);
1729
+ margin: 0;
1730
+ white-space: pre;
1731
+ overflow-x: auto;
1732
+ color: var(--color-ink);
1733
+ }
1734
+
1735
+ .mcp__setup-actions {
1736
+ display: flex;
1737
+ flex-wrap: wrap;
1738
+ gap: var(--space-xs);
1739
+ margin-top: 0.25rem;
1740
+ }
1741
+
1742
+ .mcp__setup-hint {
1743
+ font-size: var(--text-xs);
1744
+ color: var(--color-ink-3);
1745
+ margin: 0.25rem 0 0;
1746
+ line-height: 1.4;
1747
+ }
1748
+
1749
+ .mcp__setup-hint code {
1750
+ font-family: var(--font-mono);
1751
+ color: var(--color-ink-2);
1752
+ background: var(--color-paper);
1753
+ padding: 0.1rem 0.3rem;
1754
+ border-radius: 3px;
1755
+ border: var(--rule) solid var(--color-rule);
1756
+ }
1757
+
1656
1758
  .mcp__stats {
1657
1759
  display: flex;
1658
1760
  flex-wrap: wrap;