zerochat 9.3.0__tar.gz → 9.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.
- {zerochat-9.3.0/zerochat.egg-info → zerochat-9.4.0}/PKG-INFO +1 -1
- {zerochat-9.3.0 → zerochat-9.4.0}/pyproject.toml +1 -1
- {zerochat-9.3.0 → zerochat-9.4.0/zerochat.egg-info}/PKG-INFO +1 -1
- {zerochat-9.3.0 → zerochat-9.4.0}/zerochat.py +153 -1
- {zerochat-9.3.0 → zerochat-9.4.0}/LICENSE +0 -0
- {zerochat-9.3.0 → zerochat-9.4.0}/README.md +0 -0
- {zerochat-9.3.0 → zerochat-9.4.0}/setup.cfg +0 -0
- {zerochat-9.3.0 → zerochat-9.4.0}/zerochat.egg-info/SOURCES.txt +0 -0
- {zerochat-9.3.0 → zerochat-9.4.0}/zerochat.egg-info/dependency_links.txt +0 -0
- {zerochat-9.3.0 → zerochat-9.4.0}/zerochat.egg-info/entry_points.txt +0 -0
- {zerochat-9.3.0 → zerochat-9.4.0}/zerochat.egg-info/top_level.txt +0 -0
|
@@ -46,7 +46,7 @@ import zlib
|
|
|
46
46
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
47
47
|
from pathlib import Path
|
|
48
48
|
|
|
49
|
-
SOURCE_BACKEND_VERSION = "9.
|
|
49
|
+
SOURCE_BACKEND_VERSION = "9.4.0"
|
|
50
50
|
|
|
51
51
|
def _read_source_version(filename: str) -> str | None:
|
|
52
52
|
"""Lee la versión de un archivo del repositorio cuando se ejecuta desde fuentes."""
|
|
@@ -1344,6 +1344,150 @@ def set_project_directory(path) -> dict:
|
|
|
1344
1344
|
return {"cwd": str(target), "previous_cwd": previous}
|
|
1345
1345
|
|
|
1346
1346
|
|
|
1347
|
+
def open_file_diff(path: str, tool: str | None = None, commit_fallback: bool = False) -> dict:
|
|
1348
|
+
"""Abre un visor externo de diffs (git difftool) para comparar el fichero actual frente a Git HEAD, su último commit o fichero vacío."""
|
|
1349
|
+
if not isinstance(path, str) or not path.strip():
|
|
1350
|
+
raise ValueError("La ruta del archivo está vacía.")
|
|
1351
|
+
if len(path) > MAX_PATH_LENGTH:
|
|
1352
|
+
raise ValueError("La ruta del archivo excede el tamaño máximo permitido.")
|
|
1353
|
+
if tool is not None:
|
|
1354
|
+
if not isinstance(tool, str) or not re.match(r"^[a-zA-Z0-9_-]{1,32}$", tool):
|
|
1355
|
+
raise ValueError("Nombre de herramienta de diff inválido.")
|
|
1356
|
+
|
|
1357
|
+
target = Path(path.strip()).expanduser().resolve()
|
|
1358
|
+
if not target.exists():
|
|
1359
|
+
raise ValueError(f"El archivo '{path}' no existe.")
|
|
1360
|
+
if not target.is_file():
|
|
1361
|
+
raise ValueError(f"'{path}' no es un archivo regular.")
|
|
1362
|
+
|
|
1363
|
+
git_bin = shutil.which("git")
|
|
1364
|
+
if not git_bin:
|
|
1365
|
+
raise RuntimeError("Git no está instalado o no se encuentra en el PATH del sistema.")
|
|
1366
|
+
|
|
1367
|
+
try:
|
|
1368
|
+
res = subprocess.run(
|
|
1369
|
+
[git_bin, "rev-parse", "--show-toplevel"],
|
|
1370
|
+
cwd=str(target.parent),
|
|
1371
|
+
capture_output=True,
|
|
1372
|
+
text=True,
|
|
1373
|
+
timeout=5,
|
|
1374
|
+
check=False
|
|
1375
|
+
)
|
|
1376
|
+
except Exception as err:
|
|
1377
|
+
raise RuntimeError(f"Error comprobando repositorio Git: {err}")
|
|
1378
|
+
|
|
1379
|
+
if res.returncode != 0:
|
|
1380
|
+
raise ValueError(f"El archivo '{target.name}' no pertenece a ningún repositorio Git.")
|
|
1381
|
+
|
|
1382
|
+
repo_root = Path(res.stdout.strip()).resolve()
|
|
1383
|
+
|
|
1384
|
+
has_head = subprocess.run(
|
|
1385
|
+
[git_bin, "rev-parse", "--verify", "HEAD"],
|
|
1386
|
+
cwd=str(repo_root),
|
|
1387
|
+
stdout=subprocess.DEVNULL,
|
|
1388
|
+
stderr=subprocess.DEVNULL,
|
|
1389
|
+
timeout=5,
|
|
1390
|
+
check=False
|
|
1391
|
+
).returncode == 0
|
|
1392
|
+
|
|
1393
|
+
in_head = False
|
|
1394
|
+
has_working_tree_diff = True
|
|
1395
|
+
if has_head:
|
|
1396
|
+
try:
|
|
1397
|
+
rel_path = target.relative_to(repo_root).as_posix()
|
|
1398
|
+
in_head = subprocess.run(
|
|
1399
|
+
[git_bin, "cat-file", "-e", f"HEAD:{rel_path}"],
|
|
1400
|
+
cwd=str(repo_root),
|
|
1401
|
+
stdout=subprocess.DEVNULL,
|
|
1402
|
+
stderr=subprocess.DEVNULL,
|
|
1403
|
+
timeout=5,
|
|
1404
|
+
check=False
|
|
1405
|
+
).returncode == 0
|
|
1406
|
+
if in_head:
|
|
1407
|
+
diff_check = subprocess.run(
|
|
1408
|
+
[git_bin, "diff", "--quiet", "HEAD", "--", str(target)],
|
|
1409
|
+
cwd=str(repo_root),
|
|
1410
|
+
timeout=5,
|
|
1411
|
+
check=False
|
|
1412
|
+
)
|
|
1413
|
+
has_working_tree_diff = (diff_check.returncode != 0)
|
|
1414
|
+
except ValueError:
|
|
1415
|
+
in_head = False
|
|
1416
|
+
|
|
1417
|
+
cmd = [git_bin, "difftool", "--no-prompt", "-y"]
|
|
1418
|
+
if tool:
|
|
1419
|
+
cmd.append(f"--tool={tool}")
|
|
1420
|
+
else:
|
|
1421
|
+
cmd.append("--gui")
|
|
1422
|
+
|
|
1423
|
+
mode = "working_tree"
|
|
1424
|
+
last_commit = ""
|
|
1425
|
+
if has_working_tree_diff:
|
|
1426
|
+
if in_head:
|
|
1427
|
+
cmd.extend(["HEAD", "--", str(target)])
|
|
1428
|
+
else:
|
|
1429
|
+
cmd.extend(["--no-index", "--", os.devnull, str(target)])
|
|
1430
|
+
else:
|
|
1431
|
+
# No hay cambios pendientes en el árbol de trabajo. Comprobar historial del archivo.
|
|
1432
|
+
rel_path = target.relative_to(repo_root).as_posix()
|
|
1433
|
+
log_res = subprocess.run(
|
|
1434
|
+
[git_bin, "log", "-n", "1", "--format=%H", "--", rel_path],
|
|
1435
|
+
cwd=str(repo_root),
|
|
1436
|
+
capture_output=True,
|
|
1437
|
+
text=True,
|
|
1438
|
+
timeout=5,
|
|
1439
|
+
check=False
|
|
1440
|
+
)
|
|
1441
|
+
last_commit = log_res.stdout.strip() if log_res.returncode == 0 else ""
|
|
1442
|
+
if not commit_fallback or not last_commit:
|
|
1443
|
+
return {
|
|
1444
|
+
"success": True,
|
|
1445
|
+
"has_diff": False,
|
|
1446
|
+
"in_head": in_head,
|
|
1447
|
+
"has_commit_history": bool(last_commit),
|
|
1448
|
+
"last_commit": last_commit or None,
|
|
1449
|
+
"path": str(target)
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
mode = "commit"
|
|
1453
|
+
parent_check = subprocess.run(
|
|
1454
|
+
[git_bin, "rev-parse", "--verify", f"{last_commit}~1"],
|
|
1455
|
+
cwd=str(repo_root),
|
|
1456
|
+
stdout=subprocess.DEVNULL,
|
|
1457
|
+
stderr=subprocess.DEVNULL,
|
|
1458
|
+
timeout=5,
|
|
1459
|
+
check=False
|
|
1460
|
+
)
|
|
1461
|
+
if parent_check.returncode == 0:
|
|
1462
|
+
cmd.extend([f"{last_commit}~1", last_commit, "--", str(target)])
|
|
1463
|
+
else:
|
|
1464
|
+
cmd.extend(["4b825dc642cb6eb9a060e54bf8d69288fbee4904", last_commit, "--", str(target)])
|
|
1465
|
+
|
|
1466
|
+
popen_kwargs = {
|
|
1467
|
+
"cwd": str(repo_root),
|
|
1468
|
+
"stdout": subprocess.DEVNULL,
|
|
1469
|
+
"stderr": subprocess.DEVNULL,
|
|
1470
|
+
}
|
|
1471
|
+
if os.name == "nt":
|
|
1472
|
+
popen_kwargs["creationflags"] = (
|
|
1473
|
+
getattr(subprocess, "DETACHED_PROCESS", 0) |
|
|
1474
|
+
getattr(subprocess, "CREATE_NEW_PROCESS_GROUP", 0)
|
|
1475
|
+
)
|
|
1476
|
+
else:
|
|
1477
|
+
popen_kwargs["start_new_session"] = True
|
|
1478
|
+
|
|
1479
|
+
proc = subprocess.Popen(cmd, **popen_kwargs)
|
|
1480
|
+
return {
|
|
1481
|
+
"success": True,
|
|
1482
|
+
"has_diff": True,
|
|
1483
|
+
"mode": mode,
|
|
1484
|
+
"pid": proc.pid,
|
|
1485
|
+
"path": str(target),
|
|
1486
|
+
"in_head": in_head,
|
|
1487
|
+
"commit": last_commit or None
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
|
|
1347
1491
|
def search_files(query: str, path: str = ".", file_pattern: str = None, max_results: int = 100) -> str:
|
|
1348
1492
|
"""Recursively search for plain text or regular expressions across project files."""
|
|
1349
1493
|
try:
|
|
@@ -3286,6 +3430,14 @@ class ZeroChatServerHandler(BaseHTTPRequestHandler):
|
|
|
3286
3430
|
return set_project_directory(params.get("path")), None, ""
|
|
3287
3431
|
except (ValueError, OSError) as exc:
|
|
3288
3432
|
return None, {"code": -32602, "message": str(exc)}, ""
|
|
3433
|
+
if method == "zerochat/diff/open":
|
|
3434
|
+
try:
|
|
3435
|
+
commit_fallback = bool(params.get("commit_fallback") or params.get("commitFallback"))
|
|
3436
|
+
return open_file_diff(params.get("path"), params.get("tool"), commit_fallback), None, ""
|
|
3437
|
+
except (ValueError, RuntimeError) as exc:
|
|
3438
|
+
return None, {"code": -32602, "message": str(exc)}, ""
|
|
3439
|
+
except Exception as exc:
|
|
3440
|
+
return None, {"code": -32603, "message": f"Error al abrir el visor de diffs: {exc}"}, str(exc)
|
|
3289
3441
|
if method == "zerochat/web/fetch":
|
|
3290
3442
|
try:
|
|
3291
3443
|
return fetch_web_resource(params), None, ""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|