snapcontext 3.1.0__tar.gz → 3.2.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.
- {snapcontext-3.1.0/snapcontext.egg-info → snapcontext-3.2.0}/PKG-INFO +16 -2
- {snapcontext-3.1.0 → snapcontext-3.2.0}/README.md +15 -1
- {snapcontext-3.1.0 → snapcontext-3.2.0}/pyproject.toml +1 -1
- {snapcontext-3.1.0 → snapcontext-3.2.0/snapcontext.egg-info}/PKG-INFO +16 -2
- {snapcontext-3.1.0 → snapcontext-3.2.0}/snapcontext.egg-info/SOURCES.txt +2 -0
- {snapcontext-3.1.0 → snapcontext-3.2.0}/snapcontext.py +106 -3
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_auto_017.py +1 -1
- snapcontext-3.2.0/tests/test_bienvenida_311.py +133 -0
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_diagnostico_310.py +1 -1
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_diff_editor_210.py +1 -1
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_editor_ast_220.py +1 -1
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_editor_propio_200.py +1 -1
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_exe_150.py +1 -1
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_jetbrains_170.py +5 -3
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_mcp_014.py +1 -1
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_mcp_140.py +1 -1
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_memoria_015.py +1 -1
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_memoria_300.py +1 -1
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_permisos_013.py +1 -1
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_plan_012.py +1 -1
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_plan_mcp_230.py +1 -1
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_v010.py +1 -1
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_validacion_130.py +1 -1
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_vscode_016.py +11 -7
- snapcontext-3.2.0/tests/test_vscode_ts_320.py +138 -0
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_web_160.py +1 -1
- {snapcontext-3.1.0 → snapcontext-3.2.0}/LICENSE +0 -0
- {snapcontext-3.1.0 → snapcontext-3.2.0}/agentes.py +0 -0
- {snapcontext-3.1.0 → snapcontext-3.2.0}/orquestador.py +0 -0
- {snapcontext-3.1.0 → snapcontext-3.2.0}/setup.cfg +0 -0
- {snapcontext-3.1.0 → snapcontext-3.2.0}/snapcontext.egg-info/__pycache__/top_level.cpython-313.pyc +0 -0
- {snapcontext-3.1.0 → snapcontext-3.2.0}/snapcontext.egg-info/dependency_links.txt +0 -0
- {snapcontext-3.1.0 → snapcontext-3.2.0}/snapcontext.egg-info/entry_points.txt +0 -0
- {snapcontext-3.1.0 → snapcontext-3.2.0}/snapcontext.egg-info/requires.txt +0 -0
- {snapcontext-3.1.0 → snapcontext-3.2.0}/snapcontext.egg-info/top_level.txt +0 -0
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_agentes.py +0 -0
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_deteccion.py +0 -0
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_embeddings_110.py +0 -0
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_orquestador.py +0 -0
- {snapcontext-3.1.0 → snapcontext-3.2.0}/tests/test_web_120.py +0 -0
- {snapcontext-3.1.0 → snapcontext-3.2.0}/web/__init__.py +0 -0
- {snapcontext-3.1.0 → snapcontext-3.2.0}/web/app.py +0 -0
- {snapcontext-3.1.0 → snapcontext-3.2.0}/web/static/index.html +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: snapcontext
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: SnapContext: Asistente de IA con contexto automático para desarrollo
|
|
5
5
|
Author-email: SnapContext Contributors <tu@email.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -85,7 +85,7 @@ pip install aider-chat # ediciones de código
|
|
|
85
85
|
snapcontext --init # asistente inicial + API key
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
## 🧰 Instalación y onboarding sin fricción (v3.1.
|
|
88
|
+
## 🧰 Instalación y onboarding sin fricción (v3.1.x)
|
|
89
89
|
|
|
90
90
|
Objetivo: instalar SnapContext y empezar a usarlo en menos de 5 minutos.
|
|
91
91
|
|
|
@@ -117,6 +117,11 @@ Arregla instalaciones rotas:
|
|
|
117
117
|
|
|
118
118
|
### Tutorial y asistente mejorados
|
|
119
119
|
- `snapcontext --bienvenida`: tutorial interactivo de primeros pasos.
|
|
120
|
+
- **(v3.1.1)** `snapcontext` sin argumentos muestra una ayuda resumida y
|
|
121
|
+
amigable con ejemplos (código 0), en lugar de un error.
|
|
122
|
+
- **(v3.1.1)** En el primer uso se ejecuta la bienvenida automáticamente:
|
|
123
|
+
se registra en `~/.snapcontext/estado.json` (`"primer_uso": false`) para
|
|
124
|
+
no repetirla; `--bienvenida` explícito permite volver a verla.
|
|
120
125
|
- `snapcontext --init` ahora también pregunta si quieres configurar Ollama
|
|
121
126
|
(ofreciendo abrir <https://ollama.com>), crear un proyecto de prueba y
|
|
122
127
|
ejecutar el tutorial interactivo.
|
|
@@ -129,6 +134,15 @@ El instalador NSIS (`installer.nsi`, generado con
|
|
|
129
134
|
comando `snapcontext` disponible en cualquier terminal.
|
|
130
135
|
- Sigue añadiendo la carpeta al PATH y creando accesos directos.
|
|
131
136
|
|
|
137
|
+
### Extensión VS Code en TypeScript (v3.2.0)
|
|
138
|
+
La extensión vive en `vscode/` y está escrita en TypeScript:
|
|
139
|
+
- `vscode/src/extension.ts` — código fuente tipado (modo `strict`).
|
|
140
|
+
- `vscode/tsconfig.json` — ES2020, commonjs, salida en `out/`.
|
|
141
|
+
- Compilar: `cd vscode && npm install && npm run compile` (genera
|
|
142
|
+
`out/extension.js`, que es el entry point del manifiesto).
|
|
143
|
+
- Empaquetar: `vscode/scripts/empaquetar.ps1` / `.sh` (compilan antes de
|
|
144
|
+
ejecutar `vsce package`).
|
|
145
|
+
|
|
132
146
|
## 🚀 Inicio rápido (5 minutos)
|
|
133
147
|
|
|
134
148
|
```bash
|
|
@@ -40,7 +40,7 @@ pip install aider-chat # ediciones de código
|
|
|
40
40
|
snapcontext --init # asistente inicial + API key
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
## 🧰 Instalación y onboarding sin fricción (v3.1.
|
|
43
|
+
## 🧰 Instalación y onboarding sin fricción (v3.1.x)
|
|
44
44
|
|
|
45
45
|
Objetivo: instalar SnapContext y empezar a usarlo en menos de 5 minutos.
|
|
46
46
|
|
|
@@ -72,6 +72,11 @@ Arregla instalaciones rotas:
|
|
|
72
72
|
|
|
73
73
|
### Tutorial y asistente mejorados
|
|
74
74
|
- `snapcontext --bienvenida`: tutorial interactivo de primeros pasos.
|
|
75
|
+
- **(v3.1.1)** `snapcontext` sin argumentos muestra una ayuda resumida y
|
|
76
|
+
amigable con ejemplos (código 0), en lugar de un error.
|
|
77
|
+
- **(v3.1.1)** En el primer uso se ejecuta la bienvenida automáticamente:
|
|
78
|
+
se registra en `~/.snapcontext/estado.json` (`"primer_uso": false`) para
|
|
79
|
+
no repetirla; `--bienvenida` explícito permite volver a verla.
|
|
75
80
|
- `snapcontext --init` ahora también pregunta si quieres configurar Ollama
|
|
76
81
|
(ofreciendo abrir <https://ollama.com>), crear un proyecto de prueba y
|
|
77
82
|
ejecutar el tutorial interactivo.
|
|
@@ -84,6 +89,15 @@ El instalador NSIS (`installer.nsi`, generado con
|
|
|
84
89
|
comando `snapcontext` disponible en cualquier terminal.
|
|
85
90
|
- Sigue añadiendo la carpeta al PATH y creando accesos directos.
|
|
86
91
|
|
|
92
|
+
### Extensión VS Code en TypeScript (v3.2.0)
|
|
93
|
+
La extensión vive en `vscode/` y está escrita en TypeScript:
|
|
94
|
+
- `vscode/src/extension.ts` — código fuente tipado (modo `strict`).
|
|
95
|
+
- `vscode/tsconfig.json` — ES2020, commonjs, salida en `out/`.
|
|
96
|
+
- Compilar: `cd vscode && npm install && npm run compile` (genera
|
|
97
|
+
`out/extension.js`, que es el entry point del manifiesto).
|
|
98
|
+
- Empaquetar: `vscode/scripts/empaquetar.ps1` / `.sh` (compilan antes de
|
|
99
|
+
ejecutar `vsce package`).
|
|
100
|
+
|
|
87
101
|
## 🚀 Inicio rápido (5 minutos)
|
|
88
102
|
|
|
89
103
|
```bash
|
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
# Nombre verificado disponible en PyPI (18/08/2026): pypi.org/pypi/snapcontext → 404.
|
|
7
7
|
# Alternativas si algún día estuviera ocupado: "snapcontext-cli" o "snapcontext-tool".
|
|
8
8
|
name = "snapcontext"
|
|
9
|
-
version = "3.
|
|
9
|
+
version = "3.2.0"
|
|
10
10
|
description = "SnapContext: Asistente de IA con contexto automático para desarrollo"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
requires-python = ">=3.9"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: snapcontext
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: SnapContext: Asistente de IA con contexto automático para desarrollo
|
|
5
5
|
Author-email: SnapContext Contributors <tu@email.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -85,7 +85,7 @@ pip install aider-chat # ediciones de código
|
|
|
85
85
|
snapcontext --init # asistente inicial + API key
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
## 🧰 Instalación y onboarding sin fricción (v3.1.
|
|
88
|
+
## 🧰 Instalación y onboarding sin fricción (v3.1.x)
|
|
89
89
|
|
|
90
90
|
Objetivo: instalar SnapContext y empezar a usarlo en menos de 5 minutos.
|
|
91
91
|
|
|
@@ -117,6 +117,11 @@ Arregla instalaciones rotas:
|
|
|
117
117
|
|
|
118
118
|
### Tutorial y asistente mejorados
|
|
119
119
|
- `snapcontext --bienvenida`: tutorial interactivo de primeros pasos.
|
|
120
|
+
- **(v3.1.1)** `snapcontext` sin argumentos muestra una ayuda resumida y
|
|
121
|
+
amigable con ejemplos (código 0), en lugar de un error.
|
|
122
|
+
- **(v3.1.1)** En el primer uso se ejecuta la bienvenida automáticamente:
|
|
123
|
+
se registra en `~/.snapcontext/estado.json` (`"primer_uso": false`) para
|
|
124
|
+
no repetirla; `--bienvenida` explícito permite volver a verla.
|
|
120
125
|
- `snapcontext --init` ahora también pregunta si quieres configurar Ollama
|
|
121
126
|
(ofreciendo abrir <https://ollama.com>), crear un proyecto de prueba y
|
|
122
127
|
ejecutar el tutorial interactivo.
|
|
@@ -129,6 +134,15 @@ El instalador NSIS (`installer.nsi`, generado con
|
|
|
129
134
|
comando `snapcontext` disponible en cualquier terminal.
|
|
130
135
|
- Sigue añadiendo la carpeta al PATH y creando accesos directos.
|
|
131
136
|
|
|
137
|
+
### Extensión VS Code en TypeScript (v3.2.0)
|
|
138
|
+
La extensión vive en `vscode/` y está escrita en TypeScript:
|
|
139
|
+
- `vscode/src/extension.ts` — código fuente tipado (modo `strict`).
|
|
140
|
+
- `vscode/tsconfig.json` — ES2020, commonjs, salida en `out/`.
|
|
141
|
+
- Compilar: `cd vscode && npm install && npm run compile` (genera
|
|
142
|
+
`out/extension.js`, que es el entry point del manifiesto).
|
|
143
|
+
- Empaquetar: `vscode/scripts/empaquetar.ps1` / `.sh` (compilan antes de
|
|
144
|
+
ejecutar `vsce package`).
|
|
145
|
+
|
|
132
146
|
## 🚀 Inicio rápido (5 minutos)
|
|
133
147
|
|
|
134
148
|
```bash
|
|
@@ -13,6 +13,7 @@ snapcontext.egg-info/top_level.txt
|
|
|
13
13
|
snapcontext.egg-info/__pycache__/top_level.cpython-313.pyc
|
|
14
14
|
tests/test_agentes.py
|
|
15
15
|
tests/test_auto_017.py
|
|
16
|
+
tests/test_bienvenida_311.py
|
|
16
17
|
tests/test_deteccion.py
|
|
17
18
|
tests/test_diagnostico_310.py
|
|
18
19
|
tests/test_diff_editor_210.py
|
|
@@ -32,6 +33,7 @@ tests/test_plan_mcp_230.py
|
|
|
32
33
|
tests/test_v010.py
|
|
33
34
|
tests/test_validacion_130.py
|
|
34
35
|
tests/test_vscode_016.py
|
|
36
|
+
tests/test_vscode_ts_320.py
|
|
35
37
|
tests/test_web_120.py
|
|
36
38
|
tests/test_web_160.py
|
|
37
39
|
web/__init__.py
|
|
@@ -111,7 +111,7 @@ except ImportError: # pragma: no cover
|
|
|
111
111
|
import concurrent.futures
|
|
112
112
|
from concurrent.futures import ThreadPoolExecutor
|
|
113
113
|
|
|
114
|
-
VERSION = "3.
|
|
114
|
+
VERSION = "3.2.0"
|
|
115
115
|
|
|
116
116
|
# v3.1.0 — Claves de API reconocidas para el modo por defecto (offline).
|
|
117
117
|
CLAVES_API_CONOCIDAS = (
|
|
@@ -294,6 +294,8 @@ MODELO_DEFECTO = os.environ.get("SNAPCONTEXT_MODELO") or None
|
|
|
294
294
|
# Preferencias guardadas (~/.snapcontext/config.json): proveedor y modelo.
|
|
295
295
|
CONFIG_DIR = Path.home() / ".snapcontext"
|
|
296
296
|
CONFIG_PATH = CONFIG_DIR / "config.json"
|
|
297
|
+
# v3.1.1: estado ligero de primer uso (~/.snapcontext/estado.json).
|
|
298
|
+
ESTADO_PATH = CONFIG_DIR / "estado.json"
|
|
297
299
|
BACKUPS_DIR = CONFIG_DIR / "backups"
|
|
298
300
|
MAX_ARCHIVOS_DEFECTO = 3 # archivos que recibe Aider
|
|
299
301
|
MAX_CANDIDATOS_DEFECTO = 80 # candidatos que se envían al selector IA
|
|
@@ -1088,6 +1090,54 @@ def seleccionar_archivos_con_anthropic(consulta: str, archivos: List[str],
|
|
|
1088
1090
|
return normalizar_seleccion(parsear_json(texto), archivos, max_archivos)
|
|
1089
1091
|
|
|
1090
1092
|
|
|
1093
|
+
# ---------------------------------------------------------------------------
|
|
1094
|
+
# Estado de primer uso (v3.1.1): ~/.snapcontext/estado.json
|
|
1095
|
+
# ---------------------------------------------------------------------------
|
|
1096
|
+
def _cargar_estado() -> dict:
|
|
1097
|
+
"""Lee ~/.snapcontext/estado.json. Devuelve {} si no existe o falla."""
|
|
1098
|
+
try:
|
|
1099
|
+
if ESTADO_PATH.is_file():
|
|
1100
|
+
datos = json.loads(ESTADO_PATH.read_text(encoding="utf-8"))
|
|
1101
|
+
if isinstance(datos, dict):
|
|
1102
|
+
return datos
|
|
1103
|
+
except (json.JSONDecodeError, OSError):
|
|
1104
|
+
pass
|
|
1105
|
+
return {}
|
|
1106
|
+
|
|
1107
|
+
|
|
1108
|
+
def _guardar_estado(datos: dict) -> bool:
|
|
1109
|
+
"""Escribe el dict de estado en ESTADO_PATH. True si tuvo éxito."""
|
|
1110
|
+
try:
|
|
1111
|
+
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
1112
|
+
ESTADO_PATH.write_text(
|
|
1113
|
+
json.dumps(datos, indent=2, ensure_ascii=False) + "\n",
|
|
1114
|
+
encoding="utf-8")
|
|
1115
|
+
return True
|
|
1116
|
+
except OSError:
|
|
1117
|
+
return False
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
def _primer_uso_pendiente() -> bool:
|
|
1121
|
+
"""True si la bienvenida aún no se ha mostrado (estado ausente o True)."""
|
|
1122
|
+
estado = _cargar_estado()
|
|
1123
|
+
return bool(estado.get("primer_uso", True))
|
|
1124
|
+
|
|
1125
|
+
|
|
1126
|
+
def _marcar_primer_uso_completado() -> None:
|
|
1127
|
+
"""Guarda primer_uso=False (nunca rompe el flujo principal)."""
|
|
1128
|
+
estado = _cargar_estado()
|
|
1129
|
+
estado["primer_uso"] = False
|
|
1130
|
+
_guardar_estado(estado)
|
|
1131
|
+
|
|
1132
|
+
|
|
1133
|
+
def _entrada_interactiva() -> bool:
|
|
1134
|
+
"""True si stdin es un terminal (evita bloqueos en tests/CI/scripts)."""
|
|
1135
|
+
try:
|
|
1136
|
+
return sys.stdin is not None and sys.stdin.isatty()
|
|
1137
|
+
except (AttributeError, ValueError): # pragma: no cover
|
|
1138
|
+
return False
|
|
1139
|
+
|
|
1140
|
+
|
|
1091
1141
|
def cargar_configuracion() -> dict:
|
|
1092
1142
|
"""Lee la configuración guardada en ~/.snapcontext/config.json.
|
|
1093
1143
|
|
|
@@ -6633,6 +6683,42 @@ def _construir_ayuda(parser: argparse.ArgumentParser) -> str:
|
|
|
6633
6683
|
|
|
6634
6684
|
|
|
6635
6685
|
|
|
6686
|
+
def _mostrar_ayuda_resumida() -> None:
|
|
6687
|
+
"""Ayuda amigable cuando se ejecuta `snapcontext` sin argumentos (v3.1.1).
|
|
6688
|
+
|
|
6689
|
+
Más corta que --help: comandos de uso común con ejemplos listos para
|
|
6690
|
+
copiar y pegar.
|
|
6691
|
+
"""
|
|
6692
|
+
print(_LOGO_SMALL)
|
|
6693
|
+
lineas = [
|
|
6694
|
+
"Bienvenido a SnapContext — tu asistente de IA con contexto automático.",
|
|
6695
|
+
"",
|
|
6696
|
+
_pintar("Uso básico:", _CYAN),
|
|
6697
|
+
' snapcontext "describe lo que quieres cambiar"',
|
|
6698
|
+
"",
|
|
6699
|
+
_pintar("Comandos más útiles:", _CYAN),
|
|
6700
|
+
" snapcontext --bienvenida Tutorial interactivo de primeros pasos",
|
|
6701
|
+
" snapcontext --init Configurar claves API y proveedor",
|
|
6702
|
+
" snapcontext --diagnostico Revisar tu instalación",
|
|
6703
|
+
" snapcontext --reparar Arreglar una instalación rota",
|
|
6704
|
+
" snapcontext --demo Demo autónoma (sin API key)",
|
|
6705
|
+
" snapcontext --chat Conversar con el proveedor de IA",
|
|
6706
|
+
" snapcontext --plan \"tarea\" Planificar y ejecutar paso a paso",
|
|
6707
|
+
" snapcontext --help Ayuda completa agrupada",
|
|
6708
|
+
"",
|
|
6709
|
+
_pintar("Ejemplos:", _CYAN),
|
|
6710
|
+
' snapcontext "el botón de pago no funciona"',
|
|
6711
|
+
' snapcontext "añadir login" --test-loop',
|
|
6712
|
+
' snapcontext "revisar pago" --vista-previa # solo ver, no editar',
|
|
6713
|
+
"",
|
|
6714
|
+
_pintar("Sin API key", _CYAN) +
|
|
6715
|
+
": SnapContext usa Ollama local automáticamente (modo offline).",
|
|
6716
|
+
"Instala Ollama desde https://ollama.com y ejecuta: ollama pull llama3.2",
|
|
6717
|
+
"",
|
|
6718
|
+
]
|
|
6719
|
+
print("\n".join(lineas))
|
|
6720
|
+
|
|
6721
|
+
|
|
6636
6722
|
class _AyudaAccion(argparse.Action):
|
|
6637
6723
|
"""Muestra la ayuda agrupada por categorías y termina."""
|
|
6638
6724
|
|
|
@@ -7672,12 +7758,31 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|
|
7672
7758
|
# Al ejecutar como script (`python snapcontext.py ...`) argparse debe
|
|
7673
7759
|
# ver los argumentos reales; si pasáramos [] vacío, se perderían.
|
|
7674
7760
|
argv = sys.argv[1:]
|
|
7761
|
+
# v3.1.1: sin argumentos → ayuda resumida y amigable (no un error).
|
|
7762
|
+
if not argv:
|
|
7763
|
+
_mostrar_ayuda_resumida()
|
|
7764
|
+
return 0
|
|
7675
7765
|
args = crear_parser().parse_args(_preparar_argv_aliases(argv))
|
|
7676
7766
|
try:
|
|
7677
7767
|
# Permisos (v0.13.0): sincroniza el interruptor global con --confirmar
|
|
7678
7768
|
# para todos los modos (chat, planificador, ...).
|
|
7679
7769
|
global CONFIRMAR_ACCIONES
|
|
7680
7770
|
CONFIRMAR_ACCIONES = getattr(args, "confirmar", True)
|
|
7771
|
+
# v3.1.1: --bienvenida explícito ejecuta el tutorial y marca el
|
|
7772
|
+
# primer uso como completado (por si quiere volver a verlo).
|
|
7773
|
+
if getattr(args, "bienvenida", False):
|
|
7774
|
+
codigo = _tutorial_interactivo()
|
|
7775
|
+
_marcar_primer_uso_completado()
|
|
7776
|
+
return codigo
|
|
7777
|
+
# v3.1.1: primer uso → tutorial automático y se continúa con el
|
|
7778
|
+
# comando pedido. Solo en terminales interactivos (nunca en tests,
|
|
7779
|
+
# CI o scripts) para evitar bloqueos.
|
|
7780
|
+
if _primer_uso_pendiente() and _entrada_interactiva():
|
|
7781
|
+
info("👋 Parece que es tu primera vez con SnapContext. "
|
|
7782
|
+
"Mostrando el tutorial (--bienvenida)...")
|
|
7783
|
+
print()
|
|
7784
|
+
_tutorial_interactivo()
|
|
7785
|
+
_marcar_primer_uso_completado()
|
|
7681
7786
|
# --init-claude es independiente: crea la memoria del proyecto y sale.
|
|
7682
7787
|
if getattr(args, "init_claude", False):
|
|
7683
7788
|
_generar_claude_md(getattr(args, "provider", None),
|
|
@@ -7702,8 +7807,6 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|
|
7702
7807
|
return _ejecutar_diagnostico(args)
|
|
7703
7808
|
if getattr(args, "reparar", False):
|
|
7704
7809
|
return _ejecutar_reparacion(args)
|
|
7705
|
-
if getattr(args, "bienvenida", False):
|
|
7706
|
-
return _tutorial_interactivo()
|
|
7707
7810
|
# --web inicia la interfaz web (FastAPI + WebSockets) y bloquea hasta parar.
|
|
7708
7811
|
if getattr(args, "web", False):
|
|
7709
7812
|
return iniciar_servidor_web(args)
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Tests de la v3.1.1: ayuda sin argumentos y bienvenida en el primer uso."""
|
|
4
|
+
|
|
5
|
+
import io
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
import tempfile
|
|
10
|
+
import unittest
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from unittest import mock
|
|
13
|
+
|
|
14
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
15
|
+
|
|
16
|
+
import snapcontext as sc
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class _EstadoTemporal(unittest.TestCase):
|
|
20
|
+
"""Aísla ESTADO_PATH en un directorio temporal por test."""
|
|
21
|
+
|
|
22
|
+
def setUp(self):
|
|
23
|
+
self._tmp = tempfile.TemporaryDirectory()
|
|
24
|
+
self.estado = Path(self._tmp.name) / "estado.json"
|
|
25
|
+
patcher = mock.patch.object(sc, "ESTADO_PATH", self.estado)
|
|
26
|
+
patcher.start()
|
|
27
|
+
self.addCleanup(patcher.stop)
|
|
28
|
+
self.addCleanup(self._tmp.cleanup)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TestAyudaSinArgumentos(_EstadoTemporal):
|
|
32
|
+
"""`snapcontext` sin argumentos muestra la ayuda resumida (código 0)."""
|
|
33
|
+
|
|
34
|
+
def test_main_sin_args_muestra_ayuda_y_devuelve_0(self):
|
|
35
|
+
buffer = io.StringIO()
|
|
36
|
+
with mock.patch.object(sys, "stdout", buffer), \
|
|
37
|
+
mock.patch.object(sc, "_registrar_manejadores_senales"):
|
|
38
|
+
codigo = sc.main([])
|
|
39
|
+
self.assertEqual(codigo, 0)
|
|
40
|
+
salida = buffer.getvalue()
|
|
41
|
+
self.assertIn("snapcontext --bienvenida", salida)
|
|
42
|
+
self.assertIn("snapcontext --diagnostico", salida)
|
|
43
|
+
self.assertIn("Ejemplos:", salida)
|
|
44
|
+
|
|
45
|
+
def test_mostrar_ayuda_resumida_contiene_comandos_clave(self):
|
|
46
|
+
buffer = io.StringIO()
|
|
47
|
+
with mock.patch.object(sys, "stdout", buffer):
|
|
48
|
+
sc._mostrar_ayuda_resumida()
|
|
49
|
+
salida = buffer.getvalue()
|
|
50
|
+
for fragmento in ("--init", "--reparar", "--demo", "--chat",
|
|
51
|
+
"--plan", "ollama"):
|
|
52
|
+
self.assertIn(fragmento, salida)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class TestEstadoPrimerUso(_EstadoTemporal):
|
|
56
|
+
"""Helpers de estado: primer_uso en ~/.snapcontext/estado.json."""
|
|
57
|
+
|
|
58
|
+
def test_estado_ausente_significa_primer_uso(self):
|
|
59
|
+
self.assertFalse(self.estado.exists())
|
|
60
|
+
self.assertTrue(sc._primer_uso_pendiente())
|
|
61
|
+
|
|
62
|
+
def test_marcar_completado_crea_estado(self):
|
|
63
|
+
sc._marcar_primer_uso_completado()
|
|
64
|
+
self.assertTrue(self.estado.exists())
|
|
65
|
+
datos = json.loads(self.estado.read_text(encoding="utf-8"))
|
|
66
|
+
self.assertFalse(datos["primer_uso"])
|
|
67
|
+
self.assertFalse(sc._primer_uso_pendiente())
|
|
68
|
+
|
|
69
|
+
def test_primer_uso_false_en_archivo(self):
|
|
70
|
+
self.estado.write_text(json.dumps({"primer_uso": False}),
|
|
71
|
+
encoding="utf-8")
|
|
72
|
+
self.assertFalse(sc._primer_uso_pendiente())
|
|
73
|
+
|
|
74
|
+
def test_estado_corrupto_se_trata_como_primer_uso(self):
|
|
75
|
+
self.estado.write_text("{no es json", encoding="utf-8")
|
|
76
|
+
self.assertTrue(sc._primer_uso_pendiente())
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class TestBienvenidaAutomatica(_EstadoTemporal):
|
|
80
|
+
"""Primer uso → tutorial automático; después ya no se repite."""
|
|
81
|
+
|
|
82
|
+
def _ejecutar_main_con_mocks(self):
|
|
83
|
+
llamadas = []
|
|
84
|
+
with mock.patch.object(sc, "_tutorial_interactivo",
|
|
85
|
+
side_effect=lambda: llamadas.append(1) or 0), \
|
|
86
|
+
mock.patch.object(sc, "_entrada_interactiva",
|
|
87
|
+
return_value=True), \
|
|
88
|
+
mock.patch.object(sc, "_registrar_manejadores_senales"), \
|
|
89
|
+
mock.patch.object(sc, "_limpiar_historial", return_value=True), \
|
|
90
|
+
mock.patch.object(sys, "stdout", io.StringIO()), \
|
|
91
|
+
mock.patch.object(sys, "stderr", io.StringIO()):
|
|
92
|
+
codigo = sc.main(["--historial-limpiar"])
|
|
93
|
+
return codigo, llamadas
|
|
94
|
+
|
|
95
|
+
def test_primera_ejecucion_lanza_bienvenida_y_marca_estado(self):
|
|
96
|
+
codigo, llamadas = self._ejecutar_main_con_mocks()
|
|
97
|
+
self.assertEqual(codigo, 0)
|
|
98
|
+
self.assertEqual(len(llamadas), 1)
|
|
99
|
+
self.assertFalse(sc._primer_uso_pendiente())
|
|
100
|
+
|
|
101
|
+
def test_segunda_ejecucion_no_repite_bienvenida(self):
|
|
102
|
+
self._ejecutar_main_con_mocks()
|
|
103
|
+
_, llamadas = self._ejecutar_main_con_mocks()
|
|
104
|
+
self.assertEqual(len(llamadas), 0)
|
|
105
|
+
|
|
106
|
+
def test_entrada_no_interactiva_no_bloquea(self):
|
|
107
|
+
with mock.patch.object(sc, "_tutorial_interactivo") as tutorial, \
|
|
108
|
+
mock.patch.object(sc, "_entrada_interactiva",
|
|
109
|
+
return_value=False), \
|
|
110
|
+
mock.patch.object(sc, "_registrar_manejadores_senales"), \
|
|
111
|
+
mock.patch.object(sc, "_limpiar_historial", return_value=True), \
|
|
112
|
+
mock.patch.object(sys, "stdout", io.StringIO()), \
|
|
113
|
+
mock.patch.object(sys, "stderr", io.StringIO()):
|
|
114
|
+
sc.main(["--historial-limpiar"])
|
|
115
|
+
tutorial.assert_not_called()
|
|
116
|
+
# El estado no se marca: se mostrará en una sesión interactiva real.
|
|
117
|
+
self.assertTrue(sc._primer_uso_pendiente())
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class TestBienvenidaExplicita(_EstadoTemporal):
|
|
121
|
+
"""--bienvenida explícito ejecuta el tutorial y marca el estado."""
|
|
122
|
+
|
|
123
|
+
def test_flag_bienvenida_marca_primer_uso(self):
|
|
124
|
+
with mock.patch.object(sc, "_tutorial_interactivo", return_value=0), \
|
|
125
|
+
mock.patch.object(sc, "_registrar_manejadores_senales"), \
|
|
126
|
+
mock.patch.object(sys, "stdout", io.StringIO()):
|
|
127
|
+
codigo = sc.main(["--bienvenida"])
|
|
128
|
+
self.assertEqual(codigo, 0)
|
|
129
|
+
self.assertFalse(sc._primer_uso_pendiente())
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
if __name__ == "__main__":
|
|
133
|
+
unittest.main()
|
|
@@ -96,7 +96,7 @@ class TestFlagsEdicion(unittest.TestCase):
|
|
|
96
96
|
self.assertEqual(parser.parse_args(["--modo-edicion", "sobrescribir", "c"]).modo_edicion, "sobrescribir")
|
|
97
97
|
|
|
98
98
|
def test_version_es_2_1_0(self):
|
|
99
|
-
self.assertEqual(sc.VERSION, "3.
|
|
99
|
+
self.assertEqual(sc.VERSION, "3.2.0")
|
|
100
100
|
|
|
101
101
|
|
|
102
102
|
if __name__ == "__main__":
|
|
@@ -174,7 +174,7 @@ class TestFlagsEdicionAST(unittest.TestCase):
|
|
|
174
174
|
self.assertEqual(args2.modo_edicion, "auto")
|
|
175
175
|
|
|
176
176
|
def test_version_es_2_2_0(self):
|
|
177
|
-
self.assertEqual(sc.VERSION, "3.
|
|
177
|
+
self.assertEqual(sc.VERSION, "3.2.0")
|
|
178
178
|
|
|
179
179
|
|
|
180
180
|
if __name__ == "__main__":
|
|
@@ -84,7 +84,7 @@ class TestFlagsEditor(unittest.TestCase):
|
|
|
84
84
|
self.assertIsInstance(orch.agente_editor_propio, AgenteEditorPropio)
|
|
85
85
|
|
|
86
86
|
def test_version_es_2_0_0(self):
|
|
87
|
-
self.assertEqual(sc.VERSION, "3.
|
|
87
|
+
self.assertEqual(sc.VERSION, "3.2.0")
|
|
88
88
|
|
|
89
89
|
|
|
90
90
|
if __name__ == "__main__":
|
|
@@ -17,7 +17,7 @@ import snapcontext as sc # noqa: E402
|
|
|
17
17
|
|
|
18
18
|
class TestVersion(unittest.TestCase):
|
|
19
19
|
def test_version_170_coherente(self):
|
|
20
|
-
self.assertEqual(sc.VERSION, "3.
|
|
20
|
+
self.assertEqual(sc.VERSION, "3.2.0")
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class TestEstructuraJetBrains(unittest.TestCase):
|
|
@@ -102,8 +102,10 @@ class TestGradleYKotlin(unittest.TestCase):
|
|
|
102
102
|
self.assertIn("GEMINI_API_KEY", servicio) # clave opcional
|
|
103
103
|
|
|
104
104
|
def test_contexto_como_vscode(self):
|
|
105
|
-
# Mismo mecanismo de contexto visual que la extensión de VS Code
|
|
106
|
-
|
|
105
|
+
# Mismo mecanismo de contexto visual que la extensión de VS Code
|
|
106
|
+
# (migrada a TypeScript en v3.2.0).
|
|
107
|
+
vscode = (RAIZ / "vscode" / "src" / "extension.ts").read_text(
|
|
108
|
+
encoding="utf-8")
|
|
107
109
|
kotlin = (JB / "src/main/kotlin/com/snapcontext/jetbrains/"
|
|
108
110
|
"SnapContextService.kt").read_text(encoding="utf-8")
|
|
109
111
|
self.assertIn("Revisa especialmente estos archivos:", vscode)
|
|
@@ -248,7 +248,7 @@ class TestChatComandosGrafo(unittest.TestCase):
|
|
|
248
248
|
|
|
249
249
|
class TestVersion(unittest.TestCase):
|
|
250
250
|
def test_version_140_coherente(self):
|
|
251
|
-
self.assertEqual(sc.VERSION, "3.
|
|
251
|
+
self.assertEqual(sc.VERSION, "3.2.0")
|
|
252
252
|
|
|
253
253
|
|
|
254
254
|
if __name__ == "__main__":
|
|
@@ -182,7 +182,7 @@ class TestFlagsConfirmarCLI(unittest.TestCase):
|
|
|
182
182
|
self.assertFalse(self._parse(["--chat", "--no-confirmar"]).confirmar)
|
|
183
183
|
|
|
184
184
|
def test_version_es_1_2_0(self):
|
|
185
|
-
self.assertEqual(sc.VERSION, "3.
|
|
185
|
+
self.assertEqual(sc.VERSION, "3.2.0")
|
|
186
186
|
|
|
187
187
|
|
|
188
188
|
if __name__ == "__main__":
|
|
@@ -269,7 +269,7 @@ class TestFlagsPlanCLI(unittest.TestCase):
|
|
|
269
269
|
self.assertIsNone(self._parse(["--plan", "x"]).branch)
|
|
270
270
|
|
|
271
271
|
def test_version_es_1_2_0(self):
|
|
272
|
-
self.assertEqual(sc.VERSION, "3.
|
|
272
|
+
self.assertEqual(sc.VERSION, "3.2.0")
|
|
273
273
|
|
|
274
274
|
|
|
275
275
|
if __name__ == "__main__":
|
|
@@ -210,7 +210,7 @@ class TestEjecutarToolExecuteCommand(unittest.TestCase):
|
|
|
210
210
|
|
|
211
211
|
class TestVersionYFlags(unittest.TestCase):
|
|
212
212
|
def test_version_230(self):
|
|
213
|
-
self.assertEqual(sc.VERSION, "3.
|
|
213
|
+
self.assertEqual(sc.VERSION, "3.2.0")
|
|
214
214
|
|
|
215
215
|
def test_flag_paralelo(self):
|
|
216
216
|
args = sc.crear_parser().parse_args(["--plan", "t", "--paralelo", "3"])
|
|
@@ -168,7 +168,7 @@ class TestFlagsCLI(unittest.TestCase):
|
|
|
168
168
|
self.assertTrue(args.vista_previa)
|
|
169
169
|
|
|
170
170
|
def test_version_es_1_2_0(self):
|
|
171
|
-
self.assertEqual(sc.VERSION, "3.
|
|
171
|
+
self.assertEqual(sc.VERSION, "3.2.0")
|
|
172
172
|
|
|
173
173
|
|
|
174
174
|
class TestComandosAgenteChat(unittest.TestCase):
|
|
@@ -126,7 +126,7 @@ class TestIniciarProyecto(unittest.TestCase):
|
|
|
126
126
|
class TestVersion(unittest.TestCase):
|
|
127
127
|
def test_version_130_coherente(self):
|
|
128
128
|
import snapcontext as sc
|
|
129
|
-
self.assertEqual(sc.VERSION, "3.
|
|
129
|
+
self.assertEqual(sc.VERSION, "3.2.0")
|
|
130
130
|
|
|
131
131
|
|
|
132
132
|
if __name__ == "__main__":
|
|
@@ -22,7 +22,8 @@ import snapcontext as sc
|
|
|
22
22
|
|
|
23
23
|
class TestEstructuraVsCode(unittest.TestCase):
|
|
24
24
|
def test_archivos_obligatorios_existen(self):
|
|
25
|
-
for relativo in ("package.json", "
|
|
25
|
+
for relativo in ("package.json", "tsconfig.json",
|
|
26
|
+
"src/extension.ts",
|
|
26
27
|
"webview/index.html", "webview/servidor_webview.py",
|
|
27
28
|
"scripts/empaquetar.ps1", "scripts/empaquetar.sh"):
|
|
28
29
|
self.assertTrue((VSCODE / relativo).is_file(), relativo)
|
|
@@ -30,8 +31,8 @@ class TestEstructuraVsCode(unittest.TestCase):
|
|
|
30
31
|
def test_package_json_valido(self):
|
|
31
32
|
manifiesto = json.loads((VSCODE / "package.json").read_text("utf-8"))
|
|
32
33
|
self.assertEqual(manifiesto["name"], "snapcontext-vscode")
|
|
33
|
-
self.assertEqual(manifiesto["version"], "
|
|
34
|
-
self.assertEqual(manifiesto["main"], "./extension.js")
|
|
34
|
+
self.assertEqual(manifiesto["version"], "3.2.0")
|
|
35
|
+
self.assertEqual(manifiesto["main"], "./out/extension.js")
|
|
35
36
|
|
|
36
37
|
def test_comandos_contribuidos(self):
|
|
37
38
|
manifiesto = json.loads((VSCODE / "package.json").read_text("utf-8"))
|
|
@@ -56,8 +57,8 @@ class TestEstructuraVsCode(unittest.TestCase):
|
|
|
56
57
|
"snapcontext.apiKey", "snapcontext.confirmar"):
|
|
57
58
|
self.assertIn(esperada, propiedades)
|
|
58
59
|
|
|
59
|
-
def
|
|
60
|
-
codigo = (VSCODE / "extension.
|
|
60
|
+
def test_extension_ts_registra_los_comandos(self):
|
|
61
|
+
codigo = (VSCODE / "src" / "extension.ts").read_text(encoding="utf-8")
|
|
61
62
|
for comando in ("abrirChat", "ejecutarConsulta", "planificar",
|
|
62
63
|
"configurarApiKey", "anadirAlContexto",
|
|
63
64
|
"limpiarSeleccion"):
|
|
@@ -65,10 +66,13 @@ class TestEstructuraVsCode(unittest.TestCase):
|
|
|
65
66
|
# Canal de salida dedicado.
|
|
66
67
|
self.assertIn('"SnapContext Output"', codigo)
|
|
67
68
|
# Usa la CLI/módulo python con el workspace como directorio.
|
|
68
|
-
self.assertIn("-m
|
|
69
|
+
self.assertIn('"-m", "snapcontext"', codigo)
|
|
69
70
|
self.assertIn("cwd: ws", codigo)
|
|
70
71
|
self.assertIn("--no-confirmar", codigo)
|
|
71
72
|
self.assertIn("--plan", codigo)
|
|
73
|
+
# Migración a TypeScript (v3.2.0).
|
|
74
|
+
self.assertIn("import * as vscode from \"vscode\"", codigo)
|
|
75
|
+
self.assertIn("vscode.ExtensionContext", codigo)
|
|
72
76
|
|
|
73
77
|
def test_webview_es_copia_de_la_interfaz_web(self):
|
|
74
78
|
web = (RAIZ / "web" / "static" / "index.html").read_text(
|
|
@@ -87,7 +91,7 @@ class TestEstructuraVsCode(unittest.TestCase):
|
|
|
87
91
|
|
|
88
92
|
class TestVersionCli(unittest.TestCase):
|
|
89
93
|
def test_version_es_1_2_0(self):
|
|
90
|
-
self.assertEqual(sc.VERSION, "3.
|
|
94
|
+
self.assertEqual(sc.VERSION, "3.2.0")
|
|
91
95
|
|
|
92
96
|
|
|
93
97
|
if __name__ == "__main__":
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Tests de la migración a TypeScript de la extensión VS Code — v3.2.0.
|
|
4
|
+
|
|
5
|
+
Validan la configuración TypeScript, el entry point compilado y que los
|
|
6
|
+
comandos se registran en ``src/extension.ts``. Si hay un compilador
|
|
7
|
+
disponible (node_modules o npx), también ejecutan ``tsc --noEmit``.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import shutil
|
|
12
|
+
import subprocess
|
|
13
|
+
import unittest
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
RAIZ = Path(__file__).resolve().parents[1]
|
|
17
|
+
VSCODE = RAIZ / "vscode"
|
|
18
|
+
TSCONFIG_OBLIGATORIO = {
|
|
19
|
+
"target": "ES2020",
|
|
20
|
+
"module": "commonjs",
|
|
21
|
+
"strict": True,
|
|
22
|
+
"esModuleInterop": True,
|
|
23
|
+
"skipLibCheck": True,
|
|
24
|
+
"forceConsistentCasingInFileNames": True,
|
|
25
|
+
"outDir": "./out",
|
|
26
|
+
"rootDir": "./src",
|
|
27
|
+
"sourceMap": True,
|
|
28
|
+
"declaration": True,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _leer_json(ruta: Path) -> dict:
|
|
33
|
+
return json.loads(ruta.read_text("utf-8"))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class TestTsConfig(unittest.TestCase):
|
|
37
|
+
def test_tsconfig_existe_y_tiene_opciones_obligatorias(self):
|
|
38
|
+
opciones = _leer_json(VSCODE / "tsconfig.json")["compilerOptions"]
|
|
39
|
+
for clave, valor in TSCONFIG_OBLIGATORIO.items():
|
|
40
|
+
self.assertEqual(opciones.get(clave), valor,
|
|
41
|
+
f"compilerOptions['{clave}']")
|
|
42
|
+
|
|
43
|
+
def test_tsconfig_include_exclude(self):
|
|
44
|
+
config = _leer_json(VSCODE / "tsconfig.json")
|
|
45
|
+
self.assertIn("src/**/*", config["include"])
|
|
46
|
+
for excluido in ("node_modules", "out", "webview"):
|
|
47
|
+
self.assertIn(excluido, config["exclude"])
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class TestPackageJsonTypeScript(unittest.TestCase):
|
|
51
|
+
def setUp(self):
|
|
52
|
+
self.manifiesto = _leer_json(VSCODE / "package.json")
|
|
53
|
+
|
|
54
|
+
def test_main_apunta_a_out(self):
|
|
55
|
+
self.assertEqual(self.manifiesto["main"], "./out/extension.js")
|
|
56
|
+
|
|
57
|
+
def test_scripts_de_compilacion(self):
|
|
58
|
+
scripts = self.manifiesto["scripts"]
|
|
59
|
+
self.assertEqual(scripts["compile"], "tsc -p ./")
|
|
60
|
+
self.assertEqual(scripts["watch"], "tsc -watch -p ./")
|
|
61
|
+
self.assertEqual(scripts["package"], "vsce package")
|
|
62
|
+
# El empaquetado compila antes de generar el .vsix.
|
|
63
|
+
self.assertIn("compile", scripts["vscode:prepublish"])
|
|
64
|
+
|
|
65
|
+
def test_dev_dependencies_typescript(self):
|
|
66
|
+
dev = self.manifiesto["devDependencies"]
|
|
67
|
+
for paquete in ("typescript", "@types/vscode", "@types/node"):
|
|
68
|
+
self.assertIn(paquete, dev)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class TestExtensionTs(unittest.TestCase):
|
|
72
|
+
"""El código TypeScript mantiene la funcionalidad de la v0.16.0."""
|
|
73
|
+
|
|
74
|
+
@classmethod
|
|
75
|
+
def setUpClass(cls):
|
|
76
|
+
cls.codigo = (VSCODE / "src" / "extension.ts").read_text("utf-8")
|
|
77
|
+
|
|
78
|
+
def test_imports_modulo_vscode(self):
|
|
79
|
+
self.assertIn('import * as vscode from "vscode"', self.codigo)
|
|
80
|
+
|
|
81
|
+
def test_activate_tipado(self):
|
|
82
|
+
self.assertIn("export function activate("
|
|
83
|
+
"context: vscode.ExtensionContext): void",
|
|
84
|
+
self.codigo)
|
|
85
|
+
self.assertIn("export function deactivate(): void", self.codigo)
|
|
86
|
+
|
|
87
|
+
def test_comandos_registrados(self):
|
|
88
|
+
for comando in ("snapcontext.abrirChat", "snapcontext.ejecutarConsulta",
|
|
89
|
+
"snapcontext.planificar", "snapcontext.configurarApiKey",
|
|
90
|
+
"snapcontext.anadirAlContexto",
|
|
91
|
+
"snapcontext.limpiarSeleccion"):
|
|
92
|
+
self.assertIn(f'"{comando}"', self.codigo)
|
|
93
|
+
# Los registros pasan por registerCommand (disposables en subscriptions).
|
|
94
|
+
self.assertIn("registerCommand", self.codigo)
|
|
95
|
+
self.assertIn("subscriptions.push(disposable)", self.codigo)
|
|
96
|
+
|
|
97
|
+
def test_webview_sigue_sirviendo_el_servidor_web(self):
|
|
98
|
+
self.assertIn("createWebviewPanel", self.codigo)
|
|
99
|
+
self.assertIn("http://localhost:${puerto}", self.codigo)
|
|
100
|
+
self.assertIn("esperarPuerto(puerto, 10000)", self.codigo)
|
|
101
|
+
# Puerto dinámico: parte en 8765 y busca uno libre.
|
|
102
|
+
self.assertIn("let puerto = 8765", self.codigo)
|
|
103
|
+
|
|
104
|
+
def test_tipos_completos_en_firmas(self):
|
|
105
|
+
self.assertIn(": vscode.OutputChannel | null", self.codigo)
|
|
106
|
+
self.assertIn(": vscode.StatusBarItem | null", self.codigo)
|
|
107
|
+
self.assertIn("uri?: vscode.Uri", self.codigo)
|
|
108
|
+
self.assertIn("Promise<number>", self.codigo)
|
|
109
|
+
self.assertIn("ChildProcessWithoutNullStreams | null", self.codigo)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class TestCompilacionReal(unittest.TestCase):
|
|
113
|
+
"""Ejecuta `tsc --noEmit` si hay un compilador disponible."""
|
|
114
|
+
|
|
115
|
+
def test_tsc_no_emit_pasa_si_hay_compilador(self):
|
|
116
|
+
tsc_local = VSCODE / "node_modules" / ".bin" / ("tsc.cmd"
|
|
117
|
+
if shutil.which("node") and Path(VSCODE /
|
|
118
|
+
"node_modules/.bin/tsc.cmd").exists() else "tsc")
|
|
119
|
+
if not tsc_local.exists():
|
|
120
|
+
self.skipTest("TypeScript no está instalado (npm install)")
|
|
121
|
+
proc = subprocess.run(
|
|
122
|
+
[str(tsc_local), "--noEmit", "-p", str(VSCODE / "tsconfig.json")],
|
|
123
|
+
capture_output=True, text=True, timeout=120)
|
|
124
|
+
self.assertEqual(proc.returncode, 0,
|
|
125
|
+
f"tsc --noEmit falló:\n{proc.stdout}\n{proc.stderr}")
|
|
126
|
+
|
|
127
|
+
def test_out_extension_js_generado_tras_compilar(self):
|
|
128
|
+
compilado = VSCODE / "out" / "extension.js"
|
|
129
|
+
if not compilado.exists():
|
|
130
|
+
self.skipTest("La extensión aún no se ha compilado "
|
|
131
|
+
"(ejecuta npm run compile)")
|
|
132
|
+
codigo = compilado.read_text("utf-8")
|
|
133
|
+
for comando in ("snapcontext.abrirChat", "snapcontext.planificar"):
|
|
134
|
+
self.assertIn(comando, codigo)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
if __name__ == "__main__":
|
|
138
|
+
unittest.main()
|
|
@@ -17,7 +17,7 @@ INDEX = RAIZ / "web" / "static" / "index.html"
|
|
|
17
17
|
|
|
18
18
|
class TestVersion(unittest.TestCase):
|
|
19
19
|
def test_version_160_coherente(self):
|
|
20
|
-
self.assertEqual(sc.VERSION, "3.
|
|
20
|
+
self.assertEqual(sc.VERSION, "3.2.0")
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class TestFrontendV160(unittest.TestCase):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{snapcontext-3.1.0 → snapcontext-3.2.0}/snapcontext.egg-info/__pycache__/top_level.cpython-313.pyc
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|