snapcontext 3.2.0__tar.gz → 3.4.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.2.0/snapcontext.egg-info → snapcontext-3.4.0}/PKG-INFO +51 -2
- {snapcontext-3.2.0 → snapcontext-3.4.0}/README.md +50 -1
- {snapcontext-3.2.0 → snapcontext-3.4.0}/agentes.py +214 -38
- {snapcontext-3.2.0 → snapcontext-3.4.0}/orquestador.py +3 -0
- {snapcontext-3.2.0 → snapcontext-3.4.0}/pyproject.toml +1 -1
- {snapcontext-3.2.0 → snapcontext-3.4.0/snapcontext.egg-info}/PKG-INFO +51 -2
- {snapcontext-3.2.0 → snapcontext-3.4.0}/snapcontext.egg-info/SOURCES.txt +2 -0
- {snapcontext-3.2.0 → snapcontext-3.4.0}/snapcontext.py +514 -14
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_auto_017.py +1 -1
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_diagnostico_310.py +1 -1
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_diff_editor_210.py +3 -1
- snapcontext-3.4.0/tests/test_editor_330.py +347 -0
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_editor_ast_220.py +1 -1
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_editor_propio_200.py +1 -1
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_exe_150.py +1 -1
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_jetbrains_170.py +1 -1
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_mcp_014.py +1 -1
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_mcp_140.py +1 -1
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_memoria_015.py +1 -1
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_memoria_300.py +1 -1
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_permisos_013.py +1 -1
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_plan_012.py +1 -1
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_plan_mcp_230.py +1 -1
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_v010.py +1 -1
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_validacion_130.py +1 -1
- snapcontext-3.4.0/tests/test_validacion_sintaxis_340.py +195 -0
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_vscode_016.py +1 -1
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_web_160.py +1 -1
- {snapcontext-3.2.0 → snapcontext-3.4.0}/LICENSE +0 -0
- {snapcontext-3.2.0 → snapcontext-3.4.0}/setup.cfg +0 -0
- {snapcontext-3.2.0 → snapcontext-3.4.0}/snapcontext.egg-info/__pycache__/top_level.cpython-313.pyc +0 -0
- {snapcontext-3.2.0 → snapcontext-3.4.0}/snapcontext.egg-info/dependency_links.txt +0 -0
- {snapcontext-3.2.0 → snapcontext-3.4.0}/snapcontext.egg-info/entry_points.txt +0 -0
- {snapcontext-3.2.0 → snapcontext-3.4.0}/snapcontext.egg-info/requires.txt +0 -0
- {snapcontext-3.2.0 → snapcontext-3.4.0}/snapcontext.egg-info/top_level.txt +0 -0
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_agentes.py +0 -0
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_bienvenida_311.py +0 -0
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_deteccion.py +0 -0
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_embeddings_110.py +0 -0
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_orquestador.py +0 -0
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_vscode_ts_320.py +0 -0
- {snapcontext-3.2.0 → snapcontext-3.4.0}/tests/test_web_120.py +0 -0
- {snapcontext-3.2.0 → snapcontext-3.4.0}/web/__init__.py +0 -0
- {snapcontext-3.2.0 → snapcontext-3.4.0}/web/app.py +0 -0
- {snapcontext-3.2.0 → snapcontext-3.4.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.4.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
|
|
@@ -45,7 +45,7 @@ Dynamic: license-file
|
|
|
45
45
|
|
|
46
46
|
# SnapContext
|
|
47
47
|
|
|
48
|
-

|
|
49
49
|
[](https://pypi.org/project/snapcontext/)
|
|
50
50
|
[](https://github.com/NicolasBruña24/snapcontext/actions)
|
|
51
51
|

|
|
@@ -143,6 +143,18 @@ La extensión vive en `vscode/` y está escrita en TypeScript:
|
|
|
143
143
|
- Empaquetar: `vscode/scripts/empaquetar.ps1` / `.sh` (compilan antes de
|
|
144
144
|
ejecutar `vsce package`).
|
|
145
145
|
|
|
146
|
+
### Editor propio refinado (v3.3.0)
|
|
147
|
+
- **Detección de lenguaje robusta**: extensión + heurística por contenido
|
|
148
|
+
(shebang, patrones de código) para proyectos mixtos.
|
|
149
|
+
- **Manejo de conflictos en parches**: validación previa del contenido del
|
|
150
|
+
archivo y resolución automática línea a línea si `git apply`/`patch`
|
|
151
|
+
fallan (con aviso de aplicación parcial); ya no se sobrescribe a ciegas.
|
|
152
|
+
- **Prompt de edición mejorado**: lenguaje, tamaño, resumen AST con
|
|
153
|
+
posiciones y reglas de precisión (estilo conservado, cambios mínimos).
|
|
154
|
+
- **Aprendizaje de patrones**: las ediciones exitosas se guardan como skills
|
|
155
|
+
(`editor-<patrón>`) y su estrategia se reutiliza automáticamente en
|
|
156
|
+
tareas similares.
|
|
157
|
+
|
|
146
158
|
## 🚀 Inicio rápido (5 minutos)
|
|
147
159
|
|
|
148
160
|
```bash
|
|
@@ -187,6 +199,37 @@ snapcontext "corregir tipado en login" --editor propio --modo-edicion parche
|
|
|
187
199
|
|
|
188
200
|
# Refactorización estructural con AST
|
|
189
201
|
snapcontext "renombrar la variable 'usuario' a 'cuenta'" --editor propio --modo-edicion ast
|
|
202
|
+
|
|
203
|
+
# Validación de sintaxis desactivada (comportamiento previo a v3.4.0)
|
|
204
|
+
snapcontext "corregir tipado" --editor propio --no-validar-sintaxis
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Validación de sintaxis antes de guardar (v3.4.0)
|
|
208
|
+
|
|
209
|
+
Cuando uses `--editor propio`, SnapContext **valida la sintaxis** del código que
|
|
210
|
+
ha generado la IA **antes de escribirlo en disco**. Si la validación falla, el
|
|
211
|
+
cambio se rechaza y se le envía el error al proveedor para que lo corrija,
|
|
212
|
+
repitiéndose hasta `--max-intentos-validacion` intentos (por defecto 3). Si tras
|
|
213
|
+
esos intentos el código sigue roto, se **cancela la edición** y el archivo queda
|
|
214
|
+
intacto.
|
|
215
|
+
|
|
216
|
+
- Se detecta el lenguaje del archivo y se usa el validador adecuado:
|
|
217
|
+
Python (`py_compile`), JavaScript/TypeScript (`node --check`), Dart
|
|
218
|
+
(`dart analyze` → `dart format`), Go (`go build -n` → `gofmt -e`), Rust
|
|
219
|
+
(`rustc --parse-only`), Java (`javac -Xlint:none`), C/C++ (`gcc`/`clang
|
|
220
|
+
-fsyntax-only`). Si no hay validador o comando disponible, se omite la
|
|
221
|
+
validación (solo aviso en logs con `--depurar`).
|
|
222
|
+
- La validación se hace sobre un **archivo temporal**, nunca toca el original.
|
|
223
|
+
- Flag `--validar` (por defecto activado) / `--no-validar-sintaxis` para
|
|
224
|
+
desactivarla. Nota: el nombre usa `--no-validar-sintaxis` porque
|
|
225
|
+
`--no-validar` ya es alias de `--iniciar-proyecto`.
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
# Desactivar la validación por completo (comportamiento anterior)
|
|
229
|
+
snapcontext "..." --editor propio --no-validar-sintaxis
|
|
230
|
+
|
|
231
|
+
# Ajustar el número de reintentos
|
|
232
|
+
snapcontext "..." --editor propio --max-intentos-validacion 5
|
|
190
233
|
```
|
|
191
234
|
|
|
192
235
|
## 🧭 Modos y alias
|
|
@@ -1001,6 +1044,12 @@ locales y te deja elegir con las flechas:
|
|
|
1001
1044
|
| `--url-defecto` | `http://localhost:5000` | URL para abrir el navegador si Flutter no reporta una |
|
|
1002
1045
|
| `--comando-test` | `"flutter test"` | Comando del bucle de pruebas |
|
|
1003
1046
|
| `--max-iteraciones` | `3` | Iteraciones máximas del bucle |
|
|
1047
|
+
| `--validar` | on | Valida la sintaxis del código antes de guardar en el editor propio |
|
|
1048
|
+
| `--no-validar-sintaxis` | off | Desactiva la validación de sintaxis del editor propio |
|
|
1049
|
+
| `--max-intentos-validacion` | `3` | Intentos de validación de sintaxis antes de cancelar la edición |
|
|
1050
|
+
|
|
1051
|
+
> **Nota:** el flag negativo de validación de sintaxis se llama `--no-validar-sintaxis`
|
|
1052
|
+
> porque `--no-validar` ya existe como alias de `--iniciar-proyecto`.
|
|
1004
1053
|
|
|
1005
1054
|
---
|
|
1006
1055
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SnapContext
|
|
2
2
|
|
|
3
|
-

|
|
4
4
|
[](https://pypi.org/project/snapcontext/)
|
|
5
5
|
[](https://github.com/NicolasBruña24/snapcontext/actions)
|
|
6
6
|

|
|
@@ -98,6 +98,18 @@ La extensión vive en `vscode/` y está escrita en TypeScript:
|
|
|
98
98
|
- Empaquetar: `vscode/scripts/empaquetar.ps1` / `.sh` (compilan antes de
|
|
99
99
|
ejecutar `vsce package`).
|
|
100
100
|
|
|
101
|
+
### Editor propio refinado (v3.3.0)
|
|
102
|
+
- **Detección de lenguaje robusta**: extensión + heurística por contenido
|
|
103
|
+
(shebang, patrones de código) para proyectos mixtos.
|
|
104
|
+
- **Manejo de conflictos en parches**: validación previa del contenido del
|
|
105
|
+
archivo y resolución automática línea a línea si `git apply`/`patch`
|
|
106
|
+
fallan (con aviso de aplicación parcial); ya no se sobrescribe a ciegas.
|
|
107
|
+
- **Prompt de edición mejorado**: lenguaje, tamaño, resumen AST con
|
|
108
|
+
posiciones y reglas de precisión (estilo conservado, cambios mínimos).
|
|
109
|
+
- **Aprendizaje de patrones**: las ediciones exitosas se guardan como skills
|
|
110
|
+
(`editor-<patrón>`) y su estrategia se reutiliza automáticamente en
|
|
111
|
+
tareas similares.
|
|
112
|
+
|
|
101
113
|
## 🚀 Inicio rápido (5 minutos)
|
|
102
114
|
|
|
103
115
|
```bash
|
|
@@ -142,6 +154,37 @@ snapcontext "corregir tipado en login" --editor propio --modo-edicion parche
|
|
|
142
154
|
|
|
143
155
|
# Refactorización estructural con AST
|
|
144
156
|
snapcontext "renombrar la variable 'usuario' a 'cuenta'" --editor propio --modo-edicion ast
|
|
157
|
+
|
|
158
|
+
# Validación de sintaxis desactivada (comportamiento previo a v3.4.0)
|
|
159
|
+
snapcontext "corregir tipado" --editor propio --no-validar-sintaxis
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Validación de sintaxis antes de guardar (v3.4.0)
|
|
163
|
+
|
|
164
|
+
Cuando uses `--editor propio`, SnapContext **valida la sintaxis** del código que
|
|
165
|
+
ha generado la IA **antes de escribirlo en disco**. Si la validación falla, el
|
|
166
|
+
cambio se rechaza y se le envía el error al proveedor para que lo corrija,
|
|
167
|
+
repitiéndose hasta `--max-intentos-validacion` intentos (por defecto 3). Si tras
|
|
168
|
+
esos intentos el código sigue roto, se **cancela la edición** y el archivo queda
|
|
169
|
+
intacto.
|
|
170
|
+
|
|
171
|
+
- Se detecta el lenguaje del archivo y se usa el validador adecuado:
|
|
172
|
+
Python (`py_compile`), JavaScript/TypeScript (`node --check`), Dart
|
|
173
|
+
(`dart analyze` → `dart format`), Go (`go build -n` → `gofmt -e`), Rust
|
|
174
|
+
(`rustc --parse-only`), Java (`javac -Xlint:none`), C/C++ (`gcc`/`clang
|
|
175
|
+
-fsyntax-only`). Si no hay validador o comando disponible, se omite la
|
|
176
|
+
validación (solo aviso en logs con `--depurar`).
|
|
177
|
+
- La validación se hace sobre un **archivo temporal**, nunca toca el original.
|
|
178
|
+
- Flag `--validar` (por defecto activado) / `--no-validar-sintaxis` para
|
|
179
|
+
desactivarla. Nota: el nombre usa `--no-validar-sintaxis` porque
|
|
180
|
+
`--no-validar` ya es alias de `--iniciar-proyecto`.
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# Desactivar la validación por completo (comportamiento anterior)
|
|
184
|
+
snapcontext "..." --editor propio --no-validar-sintaxis
|
|
185
|
+
|
|
186
|
+
# Ajustar el número de reintentos
|
|
187
|
+
snapcontext "..." --editor propio --max-intentos-validacion 5
|
|
145
188
|
```
|
|
146
189
|
|
|
147
190
|
## 🧭 Modos y alias
|
|
@@ -956,6 +999,12 @@ locales y te deja elegir con las flechas:
|
|
|
956
999
|
| `--url-defecto` | `http://localhost:5000` | URL para abrir el navegador si Flutter no reporta una |
|
|
957
1000
|
| `--comando-test` | `"flutter test"` | Comando del bucle de pruebas |
|
|
958
1001
|
| `--max-iteraciones` | `3` | Iteraciones máximas del bucle |
|
|
1002
|
+
| `--validar` | on | Valida la sintaxis del código antes de guardar en el editor propio |
|
|
1003
|
+
| `--no-validar-sintaxis` | off | Desactiva la validación de sintaxis del editor propio |
|
|
1004
|
+
| `--max-intentos-validacion` | `3` | Intentos de validación de sintaxis antes de cancelar la edición |
|
|
1005
|
+
|
|
1006
|
+
> **Nota:** el flag negativo de validación de sintaxis se llama `--no-validar-sintaxis`
|
|
1007
|
+
> porque `--no-validar` ya existe como alias de `--iniciar-proyecto`.
|
|
959
1008
|
|
|
960
1009
|
---
|
|
961
1010
|
|
|
@@ -181,15 +181,22 @@ class AgenteEditorPropio:
|
|
|
181
181
|
self,
|
|
182
182
|
parche: str,
|
|
183
183
|
directorio: str = ".",
|
|
184
|
+
contenido_esperado: Optional[str] = None,
|
|
184
185
|
) -> bool:
|
|
185
|
-
"""Aplica un parche unificado en ``directorio
|
|
186
|
+
"""Aplica un parche unificado en ``directorio`` (v3.3.0).
|
|
187
|
+
|
|
188
|
+
Con ``contenido_esperado`` se valida antes que el archivo coincida
|
|
189
|
+
con el usado para generar el parche y, si hay conflicto, se intenta
|
|
190
|
+
la resolución incremental línea a línea.
|
|
186
191
|
|
|
187
192
|
Devuelve ``True`` si se aplicó con éxito, ``False`` si falló.
|
|
188
193
|
"""
|
|
189
194
|
import snapcontext as sc
|
|
190
195
|
|
|
191
196
|
sc.depurar(f"[AgenteEditorPropio] Aplicando parche en '{directorio}'")
|
|
192
|
-
return sc.
|
|
197
|
+
return sc._aplicar_parche_con_resolucion(
|
|
198
|
+
parche, directorio=directorio,
|
|
199
|
+
contenido_esperado=contenido_esperado)
|
|
193
200
|
|
|
194
201
|
def _cadena_modos(self, archivo: str, mensaje: str,
|
|
195
202
|
modo_edicion: str) -> List[str]:
|
|
@@ -225,55 +232,191 @@ class AgenteEditorPropio:
|
|
|
225
232
|
return sc._editor_ast(archivo, tarea, directorio=directorio,
|
|
226
233
|
proveedor=proveedor, modelo=modelo)
|
|
227
234
|
|
|
235
|
+
@staticmethod
|
|
236
|
+
def _aplicar_parche_preview(parche: str, contenido_actual: str):
|
|
237
|
+
"""Aplica ``parche`` a ``contenido_actual`` **en memoria** para poder
|
|
238
|
+
validar la sintaxis del resultado sin tocar el archivo real.
|
|
239
|
+
|
|
240
|
+
Reutiliza la segmentación de hunks de ``_parsear_hunks`` y aplica cada
|
|
241
|
+
hunk con tolerancia a pequeños desfases. Devuelve ``(resultado, n)``
|
|
242
|
+
donde ``n`` es el número de hunks aplicados (0 ⇒ no se pudo reproducir).
|
|
243
|
+
"""
|
|
244
|
+
import snapcontext as sc
|
|
245
|
+
|
|
246
|
+
resultado = (contenido_actual or "").splitlines()
|
|
247
|
+
desplazamiento = 0
|
|
248
|
+
aplicados = 0
|
|
249
|
+
for inicio_orig, cambios in sc._parsear_hunks(parche):
|
|
250
|
+
base = max(inicio_orig - 1 + desplazamiento, 0)
|
|
251
|
+
n_borrados = sum(1 for marca, _ in cambios if marca != "+")
|
|
252
|
+
nuevos = [texto for marca, texto in cambios if marca != "-"]
|
|
253
|
+
|
|
254
|
+
posicion = -1
|
|
255
|
+
for delta in (0, 1, -1, 2, -2, 3, -3, 5, -5, 10, -10):
|
|
256
|
+
candidato = base + delta
|
|
257
|
+
if candidato < 0 or candidato > len(resultado):
|
|
258
|
+
continue
|
|
259
|
+
idx = candidato
|
|
260
|
+
encaja = True
|
|
261
|
+
for marca, texto in cambios:
|
|
262
|
+
if marca == "+":
|
|
263
|
+
continue
|
|
264
|
+
if idx >= len(resultado) or resultado[idx] != texto:
|
|
265
|
+
encaja = False
|
|
266
|
+
break
|
|
267
|
+
idx += 1
|
|
268
|
+
if encaja:
|
|
269
|
+
posicion = candidato
|
|
270
|
+
break
|
|
271
|
+
if posicion < 0:
|
|
272
|
+
continue
|
|
273
|
+
|
|
274
|
+
resultado[posicion:posicion + n_borrados] = nuevos
|
|
275
|
+
desplazamiento += len(nuevos) - n_borrados
|
|
276
|
+
aplicados += 1
|
|
277
|
+
|
|
278
|
+
return "\n".join(resultado) + "\n", aplicados
|
|
279
|
+
|
|
228
280
|
def _aplicar_modo_parche(self, archivo: str, mensaje: str,
|
|
229
281
|
contenido_actual: str, modelo: Optional[str],
|
|
230
|
-
directorio: str
|
|
282
|
+
directorio: str, validar: bool = True,
|
|
283
|
+
max_intentos_validacion: int = 3) -> bool:
|
|
231
284
|
"""Intenta editar `archivo` pidiendo un parche unificado al proveedor."""
|
|
232
285
|
import snapcontext as sc
|
|
233
286
|
|
|
234
287
|
pref = sc.cargar_configuracion()
|
|
235
288
|
proveedor = pref.get("provider") or sc.PROVEEDOR_DEFECTO
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
289
|
+
lenguaje = sc._lenguaje_archivo(archivo, contenido_actual) or "?"
|
|
290
|
+
num_lineas = (contenido_actual.count("\n") + 1
|
|
291
|
+
if contenido_actual else 0)
|
|
292
|
+
max_val = max(1, int(max_intentos_validacion))
|
|
293
|
+
|
|
294
|
+
error_msj = ""
|
|
295
|
+
for intento in range(1, max_val + 1):
|
|
296
|
+
prompt = (
|
|
297
|
+
f"Genera un parche unificado (unified diff) que modifique el archivo "
|
|
298
|
+
f"para cumplir con la tarea con precisión máxima.\n"
|
|
299
|
+
f"El parche debe ser aplicable con 'patch -p1' o 'git apply' "
|
|
300
|
+
f"(encabezados --- a/{archivo} y +++ b/{archivo}).\n\n"
|
|
301
|
+
f"Tarea: {mensaje}\n"
|
|
302
|
+
f"Archivo: {archivo} (lenguaje: {lenguaje}, {num_lineas} líneas)\n\n"
|
|
303
|
+
f"Instrucciones:\n"
|
|
304
|
+
f"- Incluye suficiente contexto (3 líneas) para anclar cada hunk.\n"
|
|
305
|
+
f"- Conserva el estilo existente (indentación, comillas).\n"
|
|
306
|
+
f"- Modifica SOLO lo necesario; no reorganices el resto del archivo.\n"
|
|
307
|
+
)
|
|
308
|
+
if error_msj:
|
|
309
|
+
prompt += (
|
|
310
|
+
f"\nTu intento anterior produjo un error de sintaxis que debes "
|
|
311
|
+
f"corregir:\n{error_msj}\n\n"
|
|
312
|
+
f"Mantén el objetivo de la tarea pero arregla ese error.\n"
|
|
313
|
+
)
|
|
314
|
+
prompt += (
|
|
315
|
+
f"\nContenido actual completo:\n```{lenguaje}\n{contenido_actual}\n```\n\n"
|
|
316
|
+
f"Devuelve ÚNICAMENTE el diff unificado, sin explicaciones ni rodeos."
|
|
317
|
+
)
|
|
318
|
+
respuesta = sc._enviar_al_proveedor(
|
|
319
|
+
proveedor, modelo, [{"role": "user", "content": prompt}])
|
|
320
|
+
diff_limpio = respuesta
|
|
321
|
+
if "--- " in diff_limpio and "+++ " in diff_limpio:
|
|
322
|
+
idx_inicio = diff_limpio.find("--- ")
|
|
323
|
+
diff_limpio = diff_limpio[idx_inicio:]
|
|
324
|
+
if "```" in diff_limpio:
|
|
325
|
+
diff_limpio = diff_limpio[:diff_limpio.find("```")]
|
|
326
|
+
if not ("--- " in diff_limpio and "+++ " in diff_limpio
|
|
327
|
+
and "@@" in diff_limpio):
|
|
328
|
+
return False
|
|
329
|
+
|
|
330
|
+
if not validar:
|
|
331
|
+
# v3.3.0: validación previa + resolución de conflictos.
|
|
332
|
+
return self.aplicar_parche(diff_limpio, directorio,
|
|
333
|
+
contenido_esperado=contenido_actual)
|
|
334
|
+
|
|
335
|
+
# v3.4.0: validar la sintaxis del contenido resultante.
|
|
336
|
+
preview, aplicados = self._aplicar_parche_preview(
|
|
337
|
+
diff_limpio, contenido_actual)
|
|
338
|
+
if aplicados == 0:
|
|
339
|
+
# No se pudo reproducir en memoria → se omite la validación.
|
|
340
|
+
return self.aplicar_parche(diff_limpio, directorio,
|
|
341
|
+
contenido_esperado=contenido_actual)
|
|
342
|
+
|
|
343
|
+
sc.info(f"Validando sintaxis de {archivo}...")
|
|
344
|
+
exito, err = sc._validar_sintaxis(archivo, preview, directorio)
|
|
345
|
+
if exito:
|
|
346
|
+
sc.exito("Sintaxis válida.")
|
|
347
|
+
return self.aplicar_parche(diff_limpio, directorio,
|
|
348
|
+
contenido_esperado=contenido_actual)
|
|
349
|
+
|
|
350
|
+
error_msj = err
|
|
351
|
+
if intento < max_val:
|
|
352
|
+
sc.error(
|
|
353
|
+
f"Error de sintaxis: {err}. "
|
|
354
|
+
f"Reintentando ({intento}/{max_val})...")
|
|
355
|
+
else:
|
|
356
|
+
sc.error(
|
|
357
|
+
f"No se pudo validar tras {max_val} intentos. "
|
|
358
|
+
f"Edición cancelada.")
|
|
359
|
+
return False
|
|
254
360
|
return False
|
|
255
361
|
|
|
256
362
|
def _aplicar_modo_sobrescribir(self, archivo: str, mensaje: str,
|
|
257
363
|
contenido_actual: str, modelo: Optional[str],
|
|
258
|
-
directorio: str
|
|
364
|
+
directorio: str, validar: bool = True,
|
|
365
|
+
max_intentos_validacion: int = 3) -> bool:
|
|
259
366
|
"""Sobrescribe `archivo` con el código completo que devuelve el proveedor."""
|
|
260
367
|
import snapcontext as sc
|
|
261
368
|
|
|
262
369
|
pref = sc.cargar_configuracion()
|
|
263
370
|
proveedor = pref.get("provider") or sc.PROVEEDOR_DEFECTO
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
371
|
+
lenguaje = sc._lenguaje_archivo(archivo, contenido_actual) or "?"
|
|
372
|
+
max_val = max(1, int(max_intentos_validacion))
|
|
373
|
+
|
|
374
|
+
error_msj = ""
|
|
375
|
+
nuevo_contenido = ""
|
|
376
|
+
for intento in range(1, max_val + 1):
|
|
377
|
+
prompt = (
|
|
378
|
+
f"Modifica el siguiente archivo ({lenguaje}) para cumplir con la "
|
|
379
|
+
f"tarea, conservando el estilo existente y cambiando solo lo necesario.\n\n"
|
|
380
|
+
f"Tarea: {mensaje}\n"
|
|
381
|
+
f"Archivo: {archivo} (lenguaje: {lenguaje})\n"
|
|
382
|
+
)
|
|
383
|
+
if error_msj:
|
|
384
|
+
prompt += (
|
|
385
|
+
f"\nTu intento anterior produjo un error de sintaxis que debes "
|
|
386
|
+
f"corregir:\n{error_msj}\n\n"
|
|
387
|
+
f"Mantén el objetivo de la tarea pero arregla ese error.\n"
|
|
388
|
+
)
|
|
389
|
+
prompt += (
|
|
390
|
+
f"\nContenido actual completo:\n```{lenguaje}\n{contenido_actual}\n```\n\n"
|
|
391
|
+
f"Devuelve ÚNICAMENTE el código completo resultante, sin explicaciones ni markdown."
|
|
392
|
+
)
|
|
393
|
+
respuesta = sc._enviar_al_proveedor(
|
|
394
|
+
proveedor, modelo, [{"role": "user", "content": prompt}])
|
|
395
|
+
nuevo_contenido = respuesta
|
|
396
|
+
if nuevo_contenido.startswith("```"):
|
|
397
|
+
lineas = nuevo_contenido.splitlines()
|
|
398
|
+
if len(lineas) >= 2 and lineas[-1].startswith("```"):
|
|
399
|
+
nuevo_contenido = "\n".join(lineas[1:-1])
|
|
400
|
+
|
|
401
|
+
if not validar:
|
|
402
|
+
break
|
|
403
|
+
|
|
404
|
+
sc.info(f"Validando sintaxis de {archivo}...")
|
|
405
|
+
exito, err = sc._validar_sintaxis(archivo, nuevo_contenido, directorio)
|
|
406
|
+
if exito:
|
|
407
|
+
sc.exito("Sintaxis válida.")
|
|
408
|
+
break
|
|
409
|
+
error_msj = err
|
|
410
|
+
if intento < max_val:
|
|
411
|
+
sc.error(
|
|
412
|
+
f"Error de sintaxis: {err}. "
|
|
413
|
+
f"Reintentando ({intento}/{max_val})...")
|
|
414
|
+
else:
|
|
415
|
+
sc.error(
|
|
416
|
+
f"No se pudo validar tras {max_val} intentos. "
|
|
417
|
+
f"Edición cancelada.")
|
|
418
|
+
return False
|
|
419
|
+
|
|
277
420
|
return self.sobrescribir(archivo, nuevo_contenido, directorio)
|
|
278
421
|
|
|
279
422
|
def ejecutar(
|
|
@@ -283,6 +426,8 @@ class AgenteEditorPropio:
|
|
|
283
426
|
directorio: str = ".",
|
|
284
427
|
modo_edicion: str = "auto",
|
|
285
428
|
modelo: Optional[str] = None,
|
|
429
|
+
validar: bool = True,
|
|
430
|
+
max_intentos_validacion: int = 3,
|
|
286
431
|
) -> bool:
|
|
287
432
|
"""Aplica las modificaciones para `archivos` con el `mensaje` especificado.
|
|
288
433
|
|
|
@@ -298,6 +443,18 @@ class AgenteEditorPropio:
|
|
|
298
443
|
raiz = Path(directorio).resolve()
|
|
299
444
|
todo_ok = True
|
|
300
445
|
|
|
446
|
+
# v3.3.0 — Integración con skills: si ya existe un patrón de edición
|
|
447
|
+
# aprendido para esta tarea, se prioriza su estrategia (sin pasar por
|
|
448
|
+
# el proveedor de IA cuando el skill lo permita).
|
|
449
|
+
estrategia_aprendida = None
|
|
450
|
+
try:
|
|
451
|
+
estrategia_aprendida = sc._skill_editor_estrategia(mensaje)
|
|
452
|
+
if estrategia_aprendida:
|
|
453
|
+
sc.info("[skills] Reutilizando patrón de edición aprendido: "
|
|
454
|
+
f"'{estrategia_aprendida}'.")
|
|
455
|
+
except Exception as exc:
|
|
456
|
+
sc.depurar(f"[skills] Búsqueda de skill falló: {exc}")
|
|
457
|
+
|
|
301
458
|
for arch in archivos:
|
|
302
459
|
camino = (raiz / arch).resolve()
|
|
303
460
|
contenido_actual = ""
|
|
@@ -309,6 +466,10 @@ class AgenteEditorPropio:
|
|
|
309
466
|
pass
|
|
310
467
|
|
|
311
468
|
cadena = self._cadena_modos(arch, mensaje, modo_edicion)
|
|
469
|
+
# El skill aprendido se coloca el primero en la cadena.
|
|
470
|
+
if estrategia_aprendida and estrategia_aprendida in cadena:
|
|
471
|
+
cadena.remove(estrategia_aprendida)
|
|
472
|
+
cadena.insert(0, estrategia_aprendida)
|
|
312
473
|
conseguido = False
|
|
313
474
|
for estrategia in cadena:
|
|
314
475
|
try:
|
|
@@ -317,10 +478,14 @@ class AgenteEditorPropio:
|
|
|
317
478
|
str(raiz), modelo)
|
|
318
479
|
elif estrategia == "parche":
|
|
319
480
|
conseguido = self._aplicar_modo_parche(
|
|
320
|
-
arch, mensaje, contenido_actual, modelo, str(raiz)
|
|
481
|
+
arch, mensaje, contenido_actual, modelo, str(raiz),
|
|
482
|
+
validar=validar,
|
|
483
|
+
max_intentos_validacion=max_intentos_validacion)
|
|
321
484
|
elif estrategia == "sobrescribir":
|
|
322
485
|
conseguido = self._aplicar_modo_sobrescribir(
|
|
323
|
-
arch, mensaje, contenido_actual, modelo, str(raiz)
|
|
486
|
+
arch, mensaje, contenido_actual, modelo, str(raiz),
|
|
487
|
+
validar=validar,
|
|
488
|
+
max_intentos_validacion=max_intentos_validacion)
|
|
324
489
|
else:
|
|
325
490
|
conseguido = False
|
|
326
491
|
except Exception as exc:
|
|
@@ -329,7 +494,18 @@ class AgenteEditorPropio:
|
|
|
329
494
|
conseguido = False
|
|
330
495
|
if conseguido:
|
|
331
496
|
break
|
|
332
|
-
|
|
497
|
+
|
|
498
|
+
if conseguido:
|
|
499
|
+
# v3.3.0 — Aprendizaje: guardar/reforzar el patrón de edición.
|
|
500
|
+
patron = sc._editor_clasificar_tarea(mensaje)
|
|
501
|
+
sid = sc._skill_editor_guardar(
|
|
502
|
+
mensaje, arch, patron, estrategia=estrategia)
|
|
503
|
+
if sid is not None:
|
|
504
|
+
try:
|
|
505
|
+
sc._skill_registrar_exito(sid)
|
|
506
|
+
except Exception as exc:
|
|
507
|
+
sc.depurar(f"[skills] Refuerzo falló: {exc}")
|
|
508
|
+
else:
|
|
333
509
|
sc.error(f"[AgenteEditorPropio] No se pudo editar '{arch}'.")
|
|
334
510
|
todo_ok = False
|
|
335
511
|
|
|
@@ -190,6 +190,9 @@ class Orquestador:
|
|
|
190
190
|
directorio=str(raiz),
|
|
191
191
|
modo_edicion=modo_ed,
|
|
192
192
|
modelo=getattr(args, "modelo", None),
|
|
193
|
+
validar=getattr(args, "validar", True),
|
|
194
|
+
max_intentos_validacion=getattr(
|
|
195
|
+
args, "max_intentos_validacion", 3),
|
|
193
196
|
)
|
|
194
197
|
self._emitir_tipo("editor", accion="fin", ok=ok)
|
|
195
198
|
else:
|
|
@@ -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.4.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.4.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
|
|
@@ -45,7 +45,7 @@ Dynamic: license-file
|
|
|
45
45
|
|
|
46
46
|
# SnapContext
|
|
47
47
|
|
|
48
|
-

|
|
49
49
|
[](https://pypi.org/project/snapcontext/)
|
|
50
50
|
[](https://github.com/NicolasBruña24/snapcontext/actions)
|
|
51
51
|

|
|
@@ -143,6 +143,18 @@ La extensión vive en `vscode/` y está escrita en TypeScript:
|
|
|
143
143
|
- Empaquetar: `vscode/scripts/empaquetar.ps1` / `.sh` (compilan antes de
|
|
144
144
|
ejecutar `vsce package`).
|
|
145
145
|
|
|
146
|
+
### Editor propio refinado (v3.3.0)
|
|
147
|
+
- **Detección de lenguaje robusta**: extensión + heurística por contenido
|
|
148
|
+
(shebang, patrones de código) para proyectos mixtos.
|
|
149
|
+
- **Manejo de conflictos en parches**: validación previa del contenido del
|
|
150
|
+
archivo y resolución automática línea a línea si `git apply`/`patch`
|
|
151
|
+
fallan (con aviso de aplicación parcial); ya no se sobrescribe a ciegas.
|
|
152
|
+
- **Prompt de edición mejorado**: lenguaje, tamaño, resumen AST con
|
|
153
|
+
posiciones y reglas de precisión (estilo conservado, cambios mínimos).
|
|
154
|
+
- **Aprendizaje de patrones**: las ediciones exitosas se guardan como skills
|
|
155
|
+
(`editor-<patrón>`) y su estrategia se reutiliza automáticamente en
|
|
156
|
+
tareas similares.
|
|
157
|
+
|
|
146
158
|
## 🚀 Inicio rápido (5 minutos)
|
|
147
159
|
|
|
148
160
|
```bash
|
|
@@ -187,6 +199,37 @@ snapcontext "corregir tipado en login" --editor propio --modo-edicion parche
|
|
|
187
199
|
|
|
188
200
|
# Refactorización estructural con AST
|
|
189
201
|
snapcontext "renombrar la variable 'usuario' a 'cuenta'" --editor propio --modo-edicion ast
|
|
202
|
+
|
|
203
|
+
# Validación de sintaxis desactivada (comportamiento previo a v3.4.0)
|
|
204
|
+
snapcontext "corregir tipado" --editor propio --no-validar-sintaxis
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Validación de sintaxis antes de guardar (v3.4.0)
|
|
208
|
+
|
|
209
|
+
Cuando uses `--editor propio`, SnapContext **valida la sintaxis** del código que
|
|
210
|
+
ha generado la IA **antes de escribirlo en disco**. Si la validación falla, el
|
|
211
|
+
cambio se rechaza y se le envía el error al proveedor para que lo corrija,
|
|
212
|
+
repitiéndose hasta `--max-intentos-validacion` intentos (por defecto 3). Si tras
|
|
213
|
+
esos intentos el código sigue roto, se **cancela la edición** y el archivo queda
|
|
214
|
+
intacto.
|
|
215
|
+
|
|
216
|
+
- Se detecta el lenguaje del archivo y se usa el validador adecuado:
|
|
217
|
+
Python (`py_compile`), JavaScript/TypeScript (`node --check`), Dart
|
|
218
|
+
(`dart analyze` → `dart format`), Go (`go build -n` → `gofmt -e`), Rust
|
|
219
|
+
(`rustc --parse-only`), Java (`javac -Xlint:none`), C/C++ (`gcc`/`clang
|
|
220
|
+
-fsyntax-only`). Si no hay validador o comando disponible, se omite la
|
|
221
|
+
validación (solo aviso en logs con `--depurar`).
|
|
222
|
+
- La validación se hace sobre un **archivo temporal**, nunca toca el original.
|
|
223
|
+
- Flag `--validar` (por defecto activado) / `--no-validar-sintaxis` para
|
|
224
|
+
desactivarla. Nota: el nombre usa `--no-validar-sintaxis` porque
|
|
225
|
+
`--no-validar` ya es alias de `--iniciar-proyecto`.
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
# Desactivar la validación por completo (comportamiento anterior)
|
|
229
|
+
snapcontext "..." --editor propio --no-validar-sintaxis
|
|
230
|
+
|
|
231
|
+
# Ajustar el número de reintentos
|
|
232
|
+
snapcontext "..." --editor propio --max-intentos-validacion 5
|
|
190
233
|
```
|
|
191
234
|
|
|
192
235
|
## 🧭 Modos y alias
|
|
@@ -1001,6 +1044,12 @@ locales y te deja elegir con las flechas:
|
|
|
1001
1044
|
| `--url-defecto` | `http://localhost:5000` | URL para abrir el navegador si Flutter no reporta una |
|
|
1002
1045
|
| `--comando-test` | `"flutter test"` | Comando del bucle de pruebas |
|
|
1003
1046
|
| `--max-iteraciones` | `3` | Iteraciones máximas del bucle |
|
|
1047
|
+
| `--validar` | on | Valida la sintaxis del código antes de guardar en el editor propio |
|
|
1048
|
+
| `--no-validar-sintaxis` | off | Desactiva la validación de sintaxis del editor propio |
|
|
1049
|
+
| `--max-intentos-validacion` | `3` | Intentos de validación de sintaxis antes de cancelar la edición |
|
|
1050
|
+
|
|
1051
|
+
> **Nota:** el flag negativo de validación de sintaxis se llama `--no-validar-sintaxis`
|
|
1052
|
+
> porque `--no-validar` ya existe como alias de `--iniciar-proyecto`.
|
|
1004
1053
|
|
|
1005
1054
|
---
|
|
1006
1055
|
|
|
@@ -17,6 +17,7 @@ tests/test_bienvenida_311.py
|
|
|
17
17
|
tests/test_deteccion.py
|
|
18
18
|
tests/test_diagnostico_310.py
|
|
19
19
|
tests/test_diff_editor_210.py
|
|
20
|
+
tests/test_editor_330.py
|
|
20
21
|
tests/test_editor_ast_220.py
|
|
21
22
|
tests/test_editor_propio_200.py
|
|
22
23
|
tests/test_embeddings_110.py
|
|
@@ -32,6 +33,7 @@ tests/test_plan_012.py
|
|
|
32
33
|
tests/test_plan_mcp_230.py
|
|
33
34
|
tests/test_v010.py
|
|
34
35
|
tests/test_validacion_130.py
|
|
36
|
+
tests/test_validacion_sintaxis_340.py
|
|
35
37
|
tests/test_vscode_016.py
|
|
36
38
|
tests/test_vscode_ts_320.py
|
|
37
39
|
tests/test_web_120.py
|