|
| 1 | +from __future__ import annotations |
| 2 | + |
| 3 | +import os |
| 4 | +import subprocess |
| 5 | +from pathlib import Path |
| 6 | + |
| 7 | + |
| 8 | +def run_cmd(command: str | list[str], cwd: Path, timeout: int | None = None) -> subprocess.CompletedProcess[str]: |
| 9 | + shell = isinstance(command, str) |
| 10 | + return subprocess.run( |
| 11 | + command, |
| 12 | + cwd=str(cwd), |
| 13 | + shell=shell, |
| 14 | + text=True, |
| 15 | + capture_output=True, |
| 16 | + timeout=timeout, |
| 17 | + ) |
| 18 | + |
| 19 | + |
| 20 | +def is_git_repo(root: Path) -> bool: |
| 21 | + return (root / ".git").exists() or run_cmd(["git", "rev-parse", "--is-inside-work-tree"], root).returncode == 0 |
| 22 | + |
| 23 | + |
| 24 | +def git_status(root: Path) -> str: |
| 25 | + if not is_git_repo(root): |
| 26 | + return "Not a git repository." |
| 27 | + result = run_cmd(["git", "status", "--short"], root) |
| 28 | + return result.stdout.strip() or "Clean working tree." |
| 29 | + |
| 30 | + |
| 31 | +def git_diff(root: Path) -> str: |
| 32 | + if not is_git_repo(root): |
| 33 | + return "" |
| 34 | + result = run_cmd(["git", "diff", "--", ":!*.lock"], root) |
| 35 | + return result.stdout |
| 36 | + |
| 37 | + |
| 38 | +def project_tree(root: Path, max_entries: int = 200) -> str: |
| 39 | + ignored = {".git", ".agentflow", "__pycache__", ".pytest_cache", "node_modules", "dist", "build", ".venv"} |
| 40 | + rows: list[str] = [] |
| 41 | + for current, dirs, files in os.walk(root): |
| 42 | + cur = Path(current) |
| 43 | + dirs[:] = [d for d in sorted(dirs) if d not in ignored and not d.startswith(".")] |
| 44 | + files = [f for f in sorted(files) if f not in ignored and not f.endswith((".pyc", ".class"))] |
| 45 | + rel = cur.relative_to(root) |
| 46 | + depth = 0 if str(rel) == "." else len(rel.parts) |
| 47 | + if depth > 3: |
| 48 | + dirs[:] = [] |
| 49 | + continue |
| 50 | + indent = " " * depth |
| 51 | + if str(rel) != ".": |
| 52 | + rows.append(f"{indent}{cur.name}/") |
| 53 | + for f in files[:25]: |
| 54 | + rows.append(f"{indent} {f}") |
| 55 | + if len(rows) >= max_entries: |
| 56 | + rows.append(" ...") |
| 57 | + return "\n".join(rows) |
| 58 | + return "\n".join(rows) or "<empty>" |
0 commit comments