feat(local-agent): Add dry-run mode and configurable max-diff-bytes parameter for safer agent execution

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
autocommit 2026-04-17 21:37:32 -07:00
parent 9325131db3
commit b09c13d3cc

View file

@ -80,11 +80,15 @@ class LocalCommitAgent:
cycle_seconds: int = 300,
co_author: str = "Lilith Autocommit <noreply@atlilith.com>",
recovery_state_path: Path | None = None,
dry_run: bool = False,
max_diff_bytes: int = 131072,
):
self.acs_url = acs_url.rstrip("/")
self.repos_paths = repos_paths
self.cycle_seconds = cycle_seconds
self.co_author = co_author
self.dry_run = dry_run
self.max_diff_bytes = max_diff_bytes
self._client = httpx.Client(base_url=self.acs_url, timeout=30.0)
self._running = False
@ -263,11 +267,13 @@ class LocalCommitAgent:
# stage denied secret paths too).
_git(repo_path, "add", "--", *allowed)
# Get the diff of staged changes
# Get the diff of staged changes. Size cap comes from self.max_diff_bytes;
# the 6000-byte stage-time cap is a sub-cap before the prefilter-level cap.
stage_cap = min(6000, self.max_diff_bytes)
raw_diff = _git(repo_path, "diff", "--cached", "--stat") + "\n" + _git(
repo_path, "diff", "--cached", max_bytes=6000
repo_path, "diff", "--cached", max_bytes=stage_cap
)
diff, was_truncated = truncate_diff(raw_diff)
diff, was_truncated = truncate_diff(raw_diff, max_bytes=self.max_diff_bytes)
if was_truncated:
logger.debug(f"{_repo_display_name(repo_path)}: diff truncated for transmission")
if not diff.strip():
@ -299,6 +305,15 @@ class LocalCommitAgent:
_git(repo_path, "reset", "HEAD")
return False
# Dry-run gate — log intent, unstage, do not commit or push
if self.dry_run:
logger.info(
f"[DRY-RUN] {repo_name} ({branch}) would commit {len(allowed)} file(s) "
f"with message: {message.splitlines()[0] if message else '(empty)'}"
)
_git(repo_path, "reset", "HEAD")
return False
# Commit
full_message = f"{message}\n\nCo-Authored-By: {self.co_author}"
_git(repo_path, "commit", "-m", full_message)