snapcontext 2.1.0__tar.gz → 2.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-2.1.0/snapcontext.egg-info → snapcontext-2.2.0}/PKG-INFO +14 -5
- {snapcontext-2.1.0 → snapcontext-2.2.0}/README.md +13 -4
- {snapcontext-2.1.0 → snapcontext-2.2.0}/agentes.py +165 -63
- {snapcontext-2.1.0 → snapcontext-2.2.0}/orquestador.py +3 -1
- {snapcontext-2.1.0 → snapcontext-2.2.0}/pyproject.toml +89 -89
- {snapcontext-2.1.0 → snapcontext-2.2.0/snapcontext.egg-info}/PKG-INFO +14 -5
- {snapcontext-2.1.0 → snapcontext-2.2.0}/snapcontext.egg-info/SOURCES.txt +1 -0
- {snapcontext-2.1.0 → snapcontext-2.2.0}/snapcontext.py +330 -5
- {snapcontext-2.1.0 → snapcontext-2.2.0}/tests/test_auto_017.py +1 -1
- {snapcontext-2.1.0 → snapcontext-2.2.0}/tests/test_diff_editor_210.py +1 -1
- snapcontext-2.2.0/tests/test_editor_ast_220.py +181 -0
- {snapcontext-2.1.0 → snapcontext-2.2.0}/tests/test_editor_propio_200.py +1 -1
- {snapcontext-2.1.0 → snapcontext-2.2.0}/tests/test_exe_150.py +1 -1
- {snapcontext-2.1.0 → snapcontext-2.2.0}/tests/test_jetbrains_170.py +2 -2
- {snapcontext-2.1.0 → snapcontext-2.2.0}/tests/test_mcp_014.py +1 -1
- {snapcontext-2.1.0 → snapcontext-2.2.0}/tests/test_mcp_140.py +1 -1
- {snapcontext-2.1.0 → snapcontext-2.2.0}/tests/test_memoria_015.py +1 -1
- {snapcontext-2.1.0 → snapcontext-2.2.0}/tests/test_permisos_013.py +1 -1
- {snapcontext-2.1.0 → snapcontext-2.2.0}/tests/test_plan_012.py +1 -1
- {snapcontext-2.1.0 → snapcontext-2.2.0}/tests/test_v010.py +1 -1
- {snapcontext-2.1.0 → snapcontext-2.2.0}/tests/test_validacion_130.py +1 -1
- {snapcontext-2.1.0 → snapcontext-2.2.0}/tests/test_vscode_016.py +2 -2
- {snapcontext-2.1.0 → snapcontext-2.2.0}/tests/test_web_160.py +1 -1
- {snapcontext-2.1.0 → snapcontext-2.2.0}/web/app.py +392 -392
- {snapcontext-2.1.0 → snapcontext-2.2.0}/LICENSE +0 -0
- {snapcontext-2.1.0 → snapcontext-2.2.0}/setup.cfg +0 -0
- {snapcontext-2.1.0 → snapcontext-2.2.0}/snapcontext.egg-info/__pycache__/top_level.cpython-313.pyc +0 -0
- {snapcontext-2.1.0 → snapcontext-2.2.0}/snapcontext.egg-info/dependency_links.txt +0 -0
- {snapcontext-2.1.0 → snapcontext-2.2.0}/snapcontext.egg-info/entry_points.txt +0 -0
- {snapcontext-2.1.0 → snapcontext-2.2.0}/snapcontext.egg-info/requires.txt +0 -0
- {snapcontext-2.1.0 → snapcontext-2.2.0}/snapcontext.egg-info/top_level.txt +0 -0
- {snapcontext-2.1.0 → snapcontext-2.2.0}/tests/test_agentes.py +0 -0
- {snapcontext-2.1.0 → snapcontext-2.2.0}/tests/test_deteccion.py +0 -0
- {snapcontext-2.1.0 → snapcontext-2.2.0}/tests/test_embeddings_110.py +0 -0
- {snapcontext-2.1.0 → snapcontext-2.2.0}/tests/test_orquestador.py +0 -0
- {snapcontext-2.1.0 → snapcontext-2.2.0}/tests/test_web_120.py +0 -0
- {snapcontext-2.1.0 → snapcontext-2.2.0}/web/__init__.py +0 -0
- {snapcontext-2.1.0 → snapcontext-2.2.0}/web/static/index.html +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: snapcontext
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.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
|
|
@@ -104,12 +104,18 @@ snapcontext interactive # o snapcontext --web
|
|
|
104
104
|
Auto-detección de proyecto (Flutter, Node/React, Python, Go, Rust…) que ajusta
|
|
105
105
|
carpetas, extensiones y comandos de test por defecto.
|
|
106
106
|
|
|
107
|
-
## 🛠️ Editor Integrado Propio (v2.
|
|
107
|
+
## 🛠️ Editor Integrado Propio (v2.2.0)
|
|
108
108
|
|
|
109
|
-
SnapContext incluye su propio editor integrado con soporte de parches unificados
|
|
109
|
+
SnapContext incluye su propio editor integrado con soporte de parches unificados
|
|
110
|
+
y edición basada en AST (Árbol Sintáctico) para refactorizaciones precisas:
|
|
110
111
|
- **`--editor propio`**: Aplica cambios directamente y crea copias de seguridad automáticas en `~/.snapcontext/backups/`.
|
|
111
|
-
- **`--modo-edicion {auto,parche,sobrescribir}`**:
|
|
112
|
-
- `auto` (por defecto): Genera un parche unificado (`git apply` / `patch`)
|
|
112
|
+
- **`--modo-edicion {auto,parche,sobrescribir,ast}`**:
|
|
113
|
+
- `auto` (por defecto): Genera un parche unificado (`git apply` / `patch`), pero
|
|
114
|
+
para refactorizaciones estructurales intenta primero la edición **AST** y cae a
|
|
115
|
+
sobrescritura si nada es aplicable.
|
|
116
|
+
- `ast`: Edita comprendiendo la estructura sintáctica del archivo (Python con
|
|
117
|
+
`ast`, otros lenguajes con `tree-sitter` si está instalado). Soporta renombrar
|
|
118
|
+
símbolos, insertar imports y recibir el código completo resultante.
|
|
113
119
|
- `parche`: Fuerza la aplicación de parches unificados para ediciones precisas.
|
|
114
120
|
- `sobrescribir`: Sobrescribe el archivo completo.
|
|
115
121
|
- **`--editor aider`** (por defecto): Mantiene el flujo de edición con Aider para máxima compatibilidad.
|
|
@@ -120,6 +126,9 @@ snapcontext "añadir endpoint de métricas" --editor propio
|
|
|
120
126
|
|
|
121
127
|
# Forzar modo parche
|
|
122
128
|
snapcontext "corregir tipado en login" --editor propio --modo-edicion parche
|
|
129
|
+
|
|
130
|
+
# Refactorización estructural con AST
|
|
131
|
+
snapcontext "renombrar la variable 'usuario' a 'cuenta'" --editor propio --modo-edicion ast
|
|
123
132
|
```
|
|
124
133
|
|
|
125
134
|
## 🧭 Modos y alias
|
|
@@ -59,12 +59,18 @@ snapcontext interactive # o snapcontext --web
|
|
|
59
59
|
Auto-detección de proyecto (Flutter, Node/React, Python, Go, Rust…) que ajusta
|
|
60
60
|
carpetas, extensiones y comandos de test por defecto.
|
|
61
61
|
|
|
62
|
-
## 🛠️ Editor Integrado Propio (v2.
|
|
62
|
+
## 🛠️ Editor Integrado Propio (v2.2.0)
|
|
63
63
|
|
|
64
|
-
SnapContext incluye su propio editor integrado con soporte de parches unificados
|
|
64
|
+
SnapContext incluye su propio editor integrado con soporte de parches unificados
|
|
65
|
+
y edición basada en AST (Árbol Sintáctico) para refactorizaciones precisas:
|
|
65
66
|
- **`--editor propio`**: Aplica cambios directamente y crea copias de seguridad automáticas en `~/.snapcontext/backups/`.
|
|
66
|
-
- **`--modo-edicion {auto,parche,sobrescribir}`**:
|
|
67
|
-
- `auto` (por defecto): Genera un parche unificado (`git apply` / `patch`)
|
|
67
|
+
- **`--modo-edicion {auto,parche,sobrescribir,ast}`**:
|
|
68
|
+
- `auto` (por defecto): Genera un parche unificado (`git apply` / `patch`), pero
|
|
69
|
+
para refactorizaciones estructurales intenta primero la edición **AST** y cae a
|
|
70
|
+
sobrescritura si nada es aplicable.
|
|
71
|
+
- `ast`: Edita comprendiendo la estructura sintáctica del archivo (Python con
|
|
72
|
+
`ast`, otros lenguajes con `tree-sitter` si está instalado). Soporta renombrar
|
|
73
|
+
símbolos, insertar imports y recibir el código completo resultante.
|
|
68
74
|
- `parche`: Fuerza la aplicación de parches unificados para ediciones precisas.
|
|
69
75
|
- `sobrescribir`: Sobrescribe el archivo completo.
|
|
70
76
|
- **`--editor aider`** (por defecto): Mantiene el flujo de edición con Aider para máxima compatibilidad.
|
|
@@ -75,6 +81,9 @@ snapcontext "añadir endpoint de métricas" --editor propio
|
|
|
75
81
|
|
|
76
82
|
# Forzar modo parche
|
|
77
83
|
snapcontext "corregir tipado en login" --editor propio --modo-edicion parche
|
|
84
|
+
|
|
85
|
+
# Refactorización estructural con AST
|
|
86
|
+
snapcontext "renombrar la variable 'usuario' a 'cuenta'" --editor propio --modo-edicion ast
|
|
78
87
|
```
|
|
79
88
|
|
|
80
89
|
## 🧭 Modos y alias
|
|
@@ -21,6 +21,22 @@ logs de depuración visibles con ``--depurar`` (marcas ``[Agente…]``).
|
|
|
21
21
|
import subprocess
|
|
22
22
|
from typing import List, Optional, Union
|
|
23
23
|
|
|
24
|
+
def _tarea_estructura(tarea: str) -> bool:
|
|
25
|
+
"""Heurística simple para `auto`: ¿la tarea parece una refactorización estructural?
|
|
26
|
+
|
|
27
|
+
Si es así, merece la pena intentar primero la edición basada en AST.
|
|
28
|
+
Se normalizan acentos para tolerar variaciones ("renombra"/"renombrar", etc.).
|
|
29
|
+
"""
|
|
30
|
+
if not tarea:
|
|
31
|
+
return False
|
|
32
|
+
import unicodedata as _u
|
|
33
|
+
|
|
34
|
+
t = _u.normalize("NFD", tarea.lower())
|
|
35
|
+
t = "".join(c for c in t if not _u.combining(c))
|
|
36
|
+
claves = ("refactor", "renombra", "renombrar", "extra", "extraer",
|
|
37
|
+
"mueve", "mover", "inserta", "insertar", "nueva funcion",
|
|
38
|
+
"crear funcion", "funcion", "extraccion")
|
|
39
|
+
return any(k in t for k in claves)
|
|
24
40
|
|
|
25
41
|
class AgenteContexto:
|
|
26
42
|
"""Agente de Contexto: selecciona los archivos relevantes para la consulta.
|
|
@@ -175,6 +191,91 @@ class AgenteEditorPropio:
|
|
|
175
191
|
sc.depurar(f"[AgenteEditorPropio] Aplicando parche en '{directorio}'")
|
|
176
192
|
return sc._aplicar_parche(parche, directorio=directorio)
|
|
177
193
|
|
|
194
|
+
def _cadena_modos(self, archivo: str, mensaje: str,
|
|
195
|
+
modo_edicion: str) -> List[str]:
|
|
196
|
+
"""Devuelve la cadena de estrategias de edición a intentar para un archivo.
|
|
197
|
+
|
|
198
|
+
- 'sobrescribir' → solo sobrescritura.
|
|
199
|
+
- 'parche' → solo parche unificado (sin fallback).
|
|
200
|
+
- 'ast' → AST y, si falla, sobrescritura.
|
|
201
|
+
- 'auto' → AST (si el lenguaje lo permite y la tarea es estructural),
|
|
202
|
+
luego parche y, por último, sobrescritura.
|
|
203
|
+
"""
|
|
204
|
+
import snapcontext as sc
|
|
205
|
+
|
|
206
|
+
if modo_edicion == "sobrescribir":
|
|
207
|
+
return ["sobrescribir"]
|
|
208
|
+
if modo_edicion == "parche":
|
|
209
|
+
return ["parche"]
|
|
210
|
+
if modo_edicion == "ast":
|
|
211
|
+
return ["ast", "sobrescribir"]
|
|
212
|
+
# auto: heurística simple según lenguaje y tarea
|
|
213
|
+
if sc._ast_disponible(archivo) and _tarea_estructura(mensaje):
|
|
214
|
+
return ["ast", "parche", "sobrescribir"]
|
|
215
|
+
return ["parche", "sobrescribir"]
|
|
216
|
+
|
|
217
|
+
def editar_ast(self, archivo: str, tarea: str,
|
|
218
|
+
directorio: str = ".", modelo: Optional[str] = None) -> bool:
|
|
219
|
+
"""Edita ``archivo`` con base en su AST usando el proveedor de IA."""
|
|
220
|
+
import snapcontext as sc
|
|
221
|
+
|
|
222
|
+
pref = sc.cargar_configuracion()
|
|
223
|
+
proveedor = pref.get("provider") or sc.PROVEEDOR_DEFECTO
|
|
224
|
+
sc.depurar(f"[AgenteEditorPropio] Editando por AST '{archivo}' en '{directorio}'")
|
|
225
|
+
return sc._editor_ast(archivo, tarea, directorio=directorio,
|
|
226
|
+
proveedor=proveedor, modelo=modelo)
|
|
227
|
+
|
|
228
|
+
def _aplicar_modo_parche(self, archivo: str, mensaje: str,
|
|
229
|
+
contenido_actual: str, modelo: Optional[str],
|
|
230
|
+
directorio: str) -> bool:
|
|
231
|
+
"""Intenta editar `archivo` pidiendo un parche unificado al proveedor."""
|
|
232
|
+
import snapcontext as sc
|
|
233
|
+
|
|
234
|
+
pref = sc.cargar_configuracion()
|
|
235
|
+
proveedor = pref.get("provider") or sc.PROVEEDOR_DEFECTO
|
|
236
|
+
prompt = (
|
|
237
|
+
f"Genera un parche unificado (unified diff) que modifique el archivo para cumplir con la tarea. "
|
|
238
|
+
f"El parche debe ser aplicable con 'patch -p1' o 'git apply' (encabezados --- a/{archivo} y +++ b/{archivo}).\n\n"
|
|
239
|
+
f"Tarea: {mensaje}\n"
|
|
240
|
+
f"Archivo: {archivo}\n\n"
|
|
241
|
+
f"Contenido actual:\n```\n{contenido_actual}\n```\n\n"
|
|
242
|
+
f"Devuelve ÚNICAMENTE el diff unificado, sin explicaciones ni rodeos."
|
|
243
|
+
)
|
|
244
|
+
respuesta = sc._enviar_al_proveedor(
|
|
245
|
+
proveedor, modelo, [{"role": "user", "content": prompt}])
|
|
246
|
+
diff_limpio = respuesta
|
|
247
|
+
if "--- " in diff_limpio and "+++ " in diff_limpio:
|
|
248
|
+
idx_inicio = diff_limpio.find("--- ")
|
|
249
|
+
diff_limpio = diff_limpio[idx_inicio:]
|
|
250
|
+
if "```" in diff_limpio:
|
|
251
|
+
diff_limpio = diff_limpio[:diff_limpio.find("```")]
|
|
252
|
+
if "--- " in diff_limpio and "+++ " in diff_limpio and "@@" in diff_limpio:
|
|
253
|
+
return self.aplicar_parche(diff_limpio, directorio)
|
|
254
|
+
return False
|
|
255
|
+
|
|
256
|
+
def _aplicar_modo_sobrescribir(self, archivo: str, mensaje: str,
|
|
257
|
+
contenido_actual: str, modelo: Optional[str],
|
|
258
|
+
directorio: str) -> bool:
|
|
259
|
+
"""Sobrescribe `archivo` con el código completo que devuelve el proveedor."""
|
|
260
|
+
import snapcontext as sc
|
|
261
|
+
|
|
262
|
+
pref = sc.cargar_configuracion()
|
|
263
|
+
proveedor = pref.get("provider") or sc.PROVEEDOR_DEFECTO
|
|
264
|
+
prompt = (
|
|
265
|
+
f"Modifica el siguiente archivo para cumplir con la tarea.\n\n"
|
|
266
|
+
f"Tarea: {mensaje}\n"
|
|
267
|
+
f"Archivo: {archivo}\n\n"
|
|
268
|
+
f"Contenido actual:\n```\n{contenido_actual}\n```\n\n"
|
|
269
|
+
f"Devuelve ÚNICAMENTE el código completo resultante, sin explicaciones ni markdown."
|
|
270
|
+
)
|
|
271
|
+
nuevo_contenido = sc._enviar_al_proveedor(
|
|
272
|
+
proveedor, modelo, [{"role": "user", "content": prompt}])
|
|
273
|
+
if nuevo_contenido.startswith("```"):
|
|
274
|
+
lineas = nuevo_contenido.splitlines()
|
|
275
|
+
if len(lineas) >= 2 and lineas[-1].startswith("```"):
|
|
276
|
+
nuevo_contenido = "\n".join(lineas[1:-1])
|
|
277
|
+
return self.sobrescribir(archivo, nuevo_contenido, directorio)
|
|
278
|
+
|
|
178
279
|
def ejecutar(
|
|
179
280
|
self,
|
|
180
281
|
archivos: List[str],
|
|
@@ -187,14 +288,13 @@ class AgenteEditorPropio:
|
|
|
187
288
|
|
|
188
289
|
- modo_edicion == 'parche': intenta aplicar parche unificado (falla si no puede).
|
|
189
290
|
- modo_edicion == 'sobrescribir': aplica directamente sobrescritura de archivo.
|
|
190
|
-
- modo_edicion == '
|
|
191
|
-
|
|
291
|
+
- modo_edicion == 'ast': edita con base en el AST y cae a sobrescritura si falla.
|
|
292
|
+
- modo_edicion == 'auto': decide por heurística (AST para refactorizaciones,
|
|
293
|
+
parche, y fallback a sobrescritura).
|
|
192
294
|
"""
|
|
193
295
|
import snapcontext as sc
|
|
194
296
|
from pathlib import Path
|
|
195
297
|
|
|
196
|
-
pref = sc.cargar_configuracion()
|
|
197
|
-
proveedor = pref.get("provider") or sc.PROVEEDOR_DEFECTO
|
|
198
298
|
raiz = Path(directorio).resolve()
|
|
199
299
|
todo_ok = True
|
|
200
300
|
|
|
@@ -203,75 +303,77 @@ class AgenteEditorPropio:
|
|
|
203
303
|
contenido_actual = ""
|
|
204
304
|
if camino.is_file():
|
|
205
305
|
try:
|
|
206
|
-
contenido_actual = camino.read_text(encoding="utf-8",
|
|
306
|
+
contenido_actual = camino.read_text(encoding="utf-8",
|
|
307
|
+
errors="replace")
|
|
207
308
|
except Exception:
|
|
208
309
|
pass
|
|
209
310
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
f"El parche debe ser aplicable con 'patch -p1' o 'git apply' (encabezados --- a/{arch} y +++ b/{arch}).\n\n"
|
|
214
|
-
f"Tarea: {mensaje}\n"
|
|
215
|
-
f"Archivo: {arch}\n\n"
|
|
216
|
-
f"Contenido actual:\n```\n{contenido_actual}\n```\n\n"
|
|
217
|
-
f"Devuelve ÚNICAMENTE el diff unificado, sin explicaciones ni rodeos."
|
|
218
|
-
)
|
|
311
|
+
cadena = self._cadena_modos(arch, mensaje, modo_edicion)
|
|
312
|
+
conseguido = False
|
|
313
|
+
for estrategia in cadena:
|
|
219
314
|
try:
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
diff_limpio = diff_limpio[:diff_limpio.find("```")]
|
|
232
|
-
|
|
233
|
-
# Si parece un diff unificado válido, intentar aplicarlo
|
|
234
|
-
if ("--- " in diff_limpio and "+++ " in diff_limpio and "@@" in diff_limpio):
|
|
235
|
-
if self.aplicar_parche(diff_limpio, str(raiz)):
|
|
236
|
-
continue
|
|
237
|
-
sc.aviso(f"[AgenteEditorPropio] Parche para '{arch}' falló al aplicarse.")
|
|
238
|
-
if modo_edicion == "parche":
|
|
239
|
-
todo_ok = False
|
|
240
|
-
continue
|
|
315
|
+
if estrategia == "ast":
|
|
316
|
+
conseguido = self.editar_ast(arch, mensaje,
|
|
317
|
+
str(raiz), modelo)
|
|
318
|
+
elif estrategia == "parche":
|
|
319
|
+
conseguido = self._aplicar_modo_parche(
|
|
320
|
+
arch, mensaje, contenido_actual, modelo, str(raiz))
|
|
321
|
+
elif estrategia == "sobrescribir":
|
|
322
|
+
conseguido = self._aplicar_modo_sobrescribir(
|
|
323
|
+
arch, mensaje, contenido_actual, modelo, str(raiz))
|
|
324
|
+
else:
|
|
325
|
+
conseguido = False
|
|
241
326
|
except Exception as exc:
|
|
242
|
-
sc.depurar(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
sc.depurar(f"[AgenteEditorPropio] Aplicando edición por sobrescritura para '{arch}'...")
|
|
250
|
-
prompt_sobrescribir = (
|
|
251
|
-
f"Modifica el siguiente archivo para cumplir con la tarea.\n\n"
|
|
252
|
-
f"Tarea: {mensaje}\n"
|
|
253
|
-
f"Archivo: {arch}\n\n"
|
|
254
|
-
f"Contenido actual:\n```\n{contenido_actual}\n```\n\n"
|
|
255
|
-
f"Devuelve ÚNICAMENTE el código completo resultante, sin explicaciones ni markdown."
|
|
256
|
-
)
|
|
257
|
-
try:
|
|
258
|
-
nuevo_contenido = sc._enviar_al_proveedor(
|
|
259
|
-
proveedor, modelo,
|
|
260
|
-
[{"role": "user", "content": prompt_sobrescribir}]
|
|
261
|
-
)
|
|
262
|
-
if nuevo_contenido.startswith("```"):
|
|
263
|
-
lineas = nuevo_contenido.splitlines()
|
|
264
|
-
if len(lineas) >= 2 and lineas[-1].startswith("```"):
|
|
265
|
-
nuevo_contenido = "\n".join(lineas[1:-1])
|
|
266
|
-
if not self.sobrescribir(arch, nuevo_contenido, str(raiz)):
|
|
267
|
-
todo_ok = False
|
|
268
|
-
except Exception as exc:
|
|
269
|
-
sc.error(f"Error generando cambios por sobrescritura para {arch}: {exc}")
|
|
327
|
+
sc.depurar(
|
|
328
|
+
f"[AgenteEditorPropio] {estrategia} falló para '{arch}': {exc}")
|
|
329
|
+
conseguido = False
|
|
330
|
+
if conseguido:
|
|
331
|
+
break
|
|
332
|
+
if not conseguido:
|
|
333
|
+
sc.error(f"[AgenteEditorPropio] No se pudo editar '{arch}'.")
|
|
270
334
|
todo_ok = False
|
|
271
335
|
|
|
272
336
|
return todo_ok
|
|
273
337
|
|
|
274
338
|
|
|
339
|
+
class AgenteEditorAST:
|
|
340
|
+
"""Agente Editor AST (Fase 3): ediciones precisas basadas en árbol sintáctico.
|
|
341
|
+
|
|
342
|
+
Trabaja directamente con el AST del archivo (Python con ``ast``; otros
|
|
343
|
+
lenguajes con ``tree-sitter`` si está instalado) pidiendo al proveedor
|
|
344
|
+
operaciones de modificación del árbol o el código completo resultante.
|
|
345
|
+
"""
|
|
346
|
+
|
|
347
|
+
def editar(
|
|
348
|
+
self,
|
|
349
|
+
archivos: List[str],
|
|
350
|
+
mensaje: str,
|
|
351
|
+
directorio: str = ".",
|
|
352
|
+
modelo: Optional[str] = None,
|
|
353
|
+
) -> bool:
|
|
354
|
+
"""Aplica a cada archivo una edición guiada por AST. Devuelve bool."""
|
|
355
|
+
todo = True
|
|
356
|
+
for archivo in archivos:
|
|
357
|
+
if not self.editar_archivo(archivo, mensaje,
|
|
358
|
+
directorio=directorio, modelo=modelo):
|
|
359
|
+
todo = False
|
|
360
|
+
return todo
|
|
361
|
+
|
|
362
|
+
def editar_archivo(
|
|
363
|
+
self,
|
|
364
|
+
archivo: str,
|
|
365
|
+
tarea: str,
|
|
366
|
+
directorio: str = ".",
|
|
367
|
+
modelo: Optional[str] = None,
|
|
368
|
+
) -> bool:
|
|
369
|
+
"""Edita un único archivo con AST. Devuelve ``True`` si tuvo éxito."""
|
|
370
|
+
import snapcontext as sc
|
|
371
|
+
|
|
372
|
+
pref = sc.cargar_configuracion()
|
|
373
|
+
proveedor = pref.get("provider") or sc.PROVEEDOR_DEFECTO
|
|
374
|
+
sc.depurar(f"[AgenteEditorAST] AST sobre '{archivo}' en '{directorio}'")
|
|
375
|
+
return sc._editor_ast(archivo, tarea, directorio=directorio,
|
|
376
|
+
proveedor=proveedor, modelo=modelo)
|
|
275
377
|
|
|
276
378
|
|
|
277
379
|
class AgenteTester:
|
|
@@ -20,7 +20,8 @@ import shlex
|
|
|
20
20
|
import sys
|
|
21
21
|
from typing import List, Optional, Tuple
|
|
22
22
|
|
|
23
|
-
from agentes import AgenteContexto, AgenteEditor,
|
|
23
|
+
from agentes import (AgenteContexto, AgenteEditor, AgenteEditorAST,
|
|
24
|
+
AgenteEditorPropio, AgenteTester)
|
|
24
25
|
|
|
25
26
|
# Centinela diferenciado del None de "aborto": _planificar lo devuelve cuando
|
|
26
27
|
# el pipeline termina de forma exitosa y temprana (p. ej. --vista-previa).
|
|
@@ -40,6 +41,7 @@ class Orquestador:
|
|
|
40
41
|
self.agente_contexto = AgenteContexto()
|
|
41
42
|
self.agente_editor = AgenteEditor()
|
|
42
43
|
self.agente_editor_propio = AgenteEditorPropio()
|
|
44
|
+
self.agente_editor_ast = AgenteEditorAST()
|
|
43
45
|
self.agente_tester = AgenteTester()
|
|
44
46
|
self.evento_callback = evento_callback
|
|
45
47
|
|
|
@@ -1,90 +1,90 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["setuptools>=77"]
|
|
3
|
-
build-backend = "setuptools.build_meta"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
# Nombre verificado disponible en PyPI (18/08/2026): pypi.org/pypi/snapcontext → 404.
|
|
7
|
-
# Alternativas si algún día estuviera ocupado: "snapcontext-cli" o "snapcontext-tool".
|
|
8
|
-
name = "snapcontext"
|
|
9
|
-
version = "2.
|
|
10
|
-
description = "SnapContext: Asistente de IA con contexto automático para desarrollo"
|
|
11
|
-
readme = "README.md"
|
|
12
|
-
requires-python = ">=3.9"
|
|
13
|
-
# PEP 639: licencia SPDX + fichero de licencia incluido en el paquete.
|
|
14
|
-
license = "MIT"
|
|
15
|
-
license-files = ["LICENSE"]
|
|
16
|
-
authors = [
|
|
17
|
-
# TODO: cambia por tu nombre y correo reales antes de publicar.
|
|
18
|
-
{ name = "SnapContext Contributors", email = "tu@email.com" },
|
|
19
|
-
]
|
|
20
|
-
keywords = [
|
|
21
|
-
"ai", "aider", "gemini", "ollama", "deepseek", "groq", "claude", "anthropic",
|
|
22
|
-
"flutter", "supabase", "cli",
|
|
23
|
-
]
|
|
24
|
-
classifiers = [
|
|
25
|
-
"Development Status :: 5 - Production/Stable",
|
|
26
|
-
"Environment :: Console",
|
|
27
|
-
"Intended Audience :: Developers",
|
|
28
|
-
"Operating System :: OS Independent",
|
|
29
|
-
"Programming Language :: Python :: 3",
|
|
30
|
-
"Programming Language :: Python :: 3.9",
|
|
31
|
-
"Programming Language :: Python :: 3.10",
|
|
32
|
-
"Programming Language :: Python :: 3.11",
|
|
33
|
-
"Programming Language :: Python :: 3.12",
|
|
34
|
-
"Programming Language :: Python :: 3.13",
|
|
35
|
-
"Topic :: Software Development :: Code Generators",
|
|
36
|
-
]
|
|
37
|
-
dependencies = [
|
|
38
|
-
"google-generativeai>=0.8.3",
|
|
39
|
-
"openai>=1.30.0",
|
|
40
|
-
]
|
|
41
|
-
|
|
42
|
-
[project.optional-dependencies]
|
|
43
|
-
dev = [
|
|
44
|
-
"aider-chat>=0.67.0",
|
|
45
|
-
"build>=1.2.0",
|
|
46
|
-
"twine>=5.0.0",
|
|
47
|
-
]
|
|
48
|
-
# Menú interactivo de selección de proveedor (--provider y --local omitidos).
|
|
49
|
-
interactive = [
|
|
50
|
-
"questionary>=1.10.0",
|
|
51
|
-
]
|
|
52
|
-
# Claude (Anthropic) como proveedor de IA (--provider anthropic, --chat).
|
|
53
|
-
anthropic = [
|
|
54
|
-
"anthropic>=0.30.0",
|
|
55
|
-
]
|
|
56
|
-
# Interfaz web (FastAPI + WebSockets) con --web.
|
|
57
|
-
web = [
|
|
58
|
-
"fastapi>=0.110.0",
|
|
59
|
-
"uvicorn[standard]>=0.29.0",
|
|
60
|
-
]
|
|
61
|
-
# Búsqueda semántica local con embeddings (--search, pre-filtro del planificador).
|
|
62
|
-
embeddings = [
|
|
63
|
-
"sentence-transformers>=3.0.0",
|
|
64
|
-
"torch>=2.0.0",
|
|
65
|
-
]
|
|
66
|
-
# MCP avanzado (v1.4.0): análisis sintáctico multi-lenguaje con tree-sitter
|
|
67
|
-
# (herramienta `ast_avanzado`). Sin él, cae a `ast` de la stdlib (solo Python).
|
|
68
|
-
mcp_avanzado = [
|
|
69
|
-
"tree-sitter>=0.20.0",
|
|
70
|
-
"tree-sitter-languages>=1.10.0",
|
|
71
|
-
]
|
|
72
|
-
|
|
73
|
-
[project.urls]
|
|
74
|
-
# TODO: apunta a tu repositorio real (no bloquea la publicación si lo dejas).
|
|
75
|
-
Homepage = "https://github.com/NicolasBruna24/snapcontext"
|
|
76
|
-
Repository = "https://github.com/NicolasBruna24/snapcontext"
|
|
77
|
-
"Bug Tracker" = "https://github.com/NicolasBruna24/snapcontext/issues"
|
|
78
|
-
|
|
79
|
-
# Entry point: expone el comando `snapcontext` que llama a main().
|
|
80
|
-
[project.scripts]
|
|
81
|
-
snapcontext = "snapcontext:main"
|
|
82
|
-
|
|
83
|
-
[tool.setuptools]
|
|
84
|
-
# Módulos en la raíz (sin paquete): CLI + arquitectura de agentes.
|
|
85
|
-
py-modules = ["snapcontext", "agentes", "orquestador"]
|
|
86
|
-
packages = ["web"]
|
|
87
|
-
|
|
88
|
-
[tool.setuptools.package-data]
|
|
89
|
-
# El estático de la interfaz web debe viajar dentro del paquete instalado.
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
# Nombre verificado disponible en PyPI (18/08/2026): pypi.org/pypi/snapcontext → 404.
|
|
7
|
+
# Alternativas si algún día estuviera ocupado: "snapcontext-cli" o "snapcontext-tool".
|
|
8
|
+
name = "snapcontext"
|
|
9
|
+
version = "2.2.0"
|
|
10
|
+
description = "SnapContext: Asistente de IA con contexto automático para desarrollo"
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
requires-python = ">=3.9"
|
|
13
|
+
# PEP 639: licencia SPDX + fichero de licencia incluido en el paquete.
|
|
14
|
+
license = "MIT"
|
|
15
|
+
license-files = ["LICENSE"]
|
|
16
|
+
authors = [
|
|
17
|
+
# TODO: cambia por tu nombre y correo reales antes de publicar.
|
|
18
|
+
{ name = "SnapContext Contributors", email = "tu@email.com" },
|
|
19
|
+
]
|
|
20
|
+
keywords = [
|
|
21
|
+
"ai", "aider", "gemini", "ollama", "deepseek", "groq", "claude", "anthropic",
|
|
22
|
+
"flutter", "supabase", "cli",
|
|
23
|
+
]
|
|
24
|
+
classifiers = [
|
|
25
|
+
"Development Status :: 5 - Production/Stable",
|
|
26
|
+
"Environment :: Console",
|
|
27
|
+
"Intended Audience :: Developers",
|
|
28
|
+
"Operating System :: OS Independent",
|
|
29
|
+
"Programming Language :: Python :: 3",
|
|
30
|
+
"Programming Language :: Python :: 3.9",
|
|
31
|
+
"Programming Language :: Python :: 3.10",
|
|
32
|
+
"Programming Language :: Python :: 3.11",
|
|
33
|
+
"Programming Language :: Python :: 3.12",
|
|
34
|
+
"Programming Language :: Python :: 3.13",
|
|
35
|
+
"Topic :: Software Development :: Code Generators",
|
|
36
|
+
]
|
|
37
|
+
dependencies = [
|
|
38
|
+
"google-generativeai>=0.8.3",
|
|
39
|
+
"openai>=1.30.0",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[project.optional-dependencies]
|
|
43
|
+
dev = [
|
|
44
|
+
"aider-chat>=0.67.0",
|
|
45
|
+
"build>=1.2.0",
|
|
46
|
+
"twine>=5.0.0",
|
|
47
|
+
]
|
|
48
|
+
# Menú interactivo de selección de proveedor (--provider y --local omitidos).
|
|
49
|
+
interactive = [
|
|
50
|
+
"questionary>=1.10.0",
|
|
51
|
+
]
|
|
52
|
+
# Claude (Anthropic) como proveedor de IA (--provider anthropic, --chat).
|
|
53
|
+
anthropic = [
|
|
54
|
+
"anthropic>=0.30.0",
|
|
55
|
+
]
|
|
56
|
+
# Interfaz web (FastAPI + WebSockets) con --web.
|
|
57
|
+
web = [
|
|
58
|
+
"fastapi>=0.110.0",
|
|
59
|
+
"uvicorn[standard]>=0.29.0",
|
|
60
|
+
]
|
|
61
|
+
# Búsqueda semántica local con embeddings (--search, pre-filtro del planificador).
|
|
62
|
+
embeddings = [
|
|
63
|
+
"sentence-transformers>=3.0.0",
|
|
64
|
+
"torch>=2.0.0",
|
|
65
|
+
]
|
|
66
|
+
# MCP avanzado (v1.4.0): análisis sintáctico multi-lenguaje con tree-sitter
|
|
67
|
+
# (herramienta `ast_avanzado`). Sin él, cae a `ast` de la stdlib (solo Python).
|
|
68
|
+
mcp_avanzado = [
|
|
69
|
+
"tree-sitter>=0.20.0",
|
|
70
|
+
"tree-sitter-languages>=1.10.0",
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
[project.urls]
|
|
74
|
+
# TODO: apunta a tu repositorio real (no bloquea la publicación si lo dejas).
|
|
75
|
+
Homepage = "https://github.com/NicolasBruna24/snapcontext"
|
|
76
|
+
Repository = "https://github.com/NicolasBruna24/snapcontext"
|
|
77
|
+
"Bug Tracker" = "https://github.com/NicolasBruna24/snapcontext/issues"
|
|
78
|
+
|
|
79
|
+
# Entry point: expone el comando `snapcontext` que llama a main().
|
|
80
|
+
[project.scripts]
|
|
81
|
+
snapcontext = "snapcontext:main"
|
|
82
|
+
|
|
83
|
+
[tool.setuptools]
|
|
84
|
+
# Módulos en la raíz (sin paquete): CLI + arquitectura de agentes.
|
|
85
|
+
py-modules = ["snapcontext", "agentes", "orquestador"]
|
|
86
|
+
packages = ["web"]
|
|
87
|
+
|
|
88
|
+
[tool.setuptools.package-data]
|
|
89
|
+
# El estático de la interfaz web debe viajar dentro del paquete instalado.
|
|
90
90
|
"web" = ["static/index.html"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: snapcontext
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.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
|
|
@@ -104,12 +104,18 @@ snapcontext interactive # o snapcontext --web
|
|
|
104
104
|
Auto-detección de proyecto (Flutter, Node/React, Python, Go, Rust…) que ajusta
|
|
105
105
|
carpetas, extensiones y comandos de test por defecto.
|
|
106
106
|
|
|
107
|
-
## 🛠️ Editor Integrado Propio (v2.
|
|
107
|
+
## 🛠️ Editor Integrado Propio (v2.2.0)
|
|
108
108
|
|
|
109
|
-
SnapContext incluye su propio editor integrado con soporte de parches unificados
|
|
109
|
+
SnapContext incluye su propio editor integrado con soporte de parches unificados
|
|
110
|
+
y edición basada en AST (Árbol Sintáctico) para refactorizaciones precisas:
|
|
110
111
|
- **`--editor propio`**: Aplica cambios directamente y crea copias de seguridad automáticas en `~/.snapcontext/backups/`.
|
|
111
|
-
- **`--modo-edicion {auto,parche,sobrescribir}`**:
|
|
112
|
-
- `auto` (por defecto): Genera un parche unificado (`git apply` / `patch`)
|
|
112
|
+
- **`--modo-edicion {auto,parche,sobrescribir,ast}`**:
|
|
113
|
+
- `auto` (por defecto): Genera un parche unificado (`git apply` / `patch`), pero
|
|
114
|
+
para refactorizaciones estructurales intenta primero la edición **AST** y cae a
|
|
115
|
+
sobrescritura si nada es aplicable.
|
|
116
|
+
- `ast`: Edita comprendiendo la estructura sintáctica del archivo (Python con
|
|
117
|
+
`ast`, otros lenguajes con `tree-sitter` si está instalado). Soporta renombrar
|
|
118
|
+
símbolos, insertar imports y recibir el código completo resultante.
|
|
113
119
|
- `parche`: Fuerza la aplicación de parches unificados para ediciones precisas.
|
|
114
120
|
- `sobrescribir`: Sobrescribe el archivo completo.
|
|
115
121
|
- **`--editor aider`** (por defecto): Mantiene el flujo de edición con Aider para máxima compatibilidad.
|
|
@@ -120,6 +126,9 @@ snapcontext "añadir endpoint de métricas" --editor propio
|
|
|
120
126
|
|
|
121
127
|
# Forzar modo parche
|
|
122
128
|
snapcontext "corregir tipado en login" --editor propio --modo-edicion parche
|
|
129
|
+
|
|
130
|
+
# Refactorización estructural con AST
|
|
131
|
+
snapcontext "renombrar la variable 'usuario' a 'cuenta'" --editor propio --modo-edicion ast
|
|
123
132
|
```
|
|
124
133
|
|
|
125
134
|
## 🧭 Modos y alias
|