execforge 0.1.0__py3-none-any.whl

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.
Files changed (44) hide show
  1. execforge-0.1.0.dist-info/METADATA +367 -0
  2. execforge-0.1.0.dist-info/RECORD +44 -0
  3. execforge-0.1.0.dist-info/WHEEL +5 -0
  4. execforge-0.1.0.dist-info/entry_points.txt +5 -0
  5. execforge-0.1.0.dist-info/licenses/LICENSE +21 -0
  6. execforge-0.1.0.dist-info/top_level.txt +1 -0
  7. orchestrator/__init__.py +4 -0
  8. orchestrator/__main__.py +5 -0
  9. orchestrator/backends/__init__.py +1 -0
  10. orchestrator/backends/base.py +29 -0
  11. orchestrator/backends/factory.py +53 -0
  12. orchestrator/backends/llm_cli_backend.py +87 -0
  13. orchestrator/backends/mock_backend.py +34 -0
  14. orchestrator/backends/shell_backend.py +49 -0
  15. orchestrator/cli/__init__.py +1 -0
  16. orchestrator/cli/main.py +971 -0
  17. orchestrator/config.py +272 -0
  18. orchestrator/domain/__init__.py +1 -0
  19. orchestrator/domain/types.py +77 -0
  20. orchestrator/exceptions.py +18 -0
  21. orchestrator/git/__init__.py +1 -0
  22. orchestrator/git/service.py +202 -0
  23. orchestrator/logging_setup.py +53 -0
  24. orchestrator/prompts/__init__.py +1 -0
  25. orchestrator/prompts/parser.py +91 -0
  26. orchestrator/reporting/__init__.py +1 -0
  27. orchestrator/reporting/console.py +197 -0
  28. orchestrator/reporting/events.py +44 -0
  29. orchestrator/reporting/selection_result.py +15 -0
  30. orchestrator/services/__init__.py +1 -0
  31. orchestrator/services/agent_runner.py +831 -0
  32. orchestrator/services/agent_service.py +122 -0
  33. orchestrator/services/project_service.py +47 -0
  34. orchestrator/services/prompt_source_service.py +65 -0
  35. orchestrator/services/run_service.py +42 -0
  36. orchestrator/services/step_executor.py +100 -0
  37. orchestrator/services/task_service.py +155 -0
  38. orchestrator/storage/__init__.py +1 -0
  39. orchestrator/storage/db.py +29 -0
  40. orchestrator/storage/models.py +95 -0
  41. orchestrator/utils/__init__.py +1 -0
  42. orchestrator/utils/process.py +44 -0
  43. orchestrator/validation/__init__.py +1 -0
  44. orchestrator/validation/pipeline.py +52 -0
@@ -0,0 +1,44 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ import os
5
+ from pathlib import Path
6
+ import shutil
7
+ import subprocess
8
+
9
+
10
+ @dataclass(slots=True)
11
+ class ProcessResult:
12
+ code: int
13
+ stdout: str
14
+ stderr: str
15
+
16
+
17
+ def run_command(command: list[str], cwd: Path, timeout: int = 900) -> ProcessResult:
18
+ if not command:
19
+ return ProcessResult(code=127, stdout="", stderr="executable not found: <empty command>")
20
+
21
+ exec_cmd = list(command)
22
+ resolved = shutil.which(command[0])
23
+ if resolved:
24
+ exec_cmd[0] = resolved
25
+
26
+ # Windows package managers often install CLI shims as .cmd/.bat.
27
+ # CreateProcess cannot execute these reliably without going through cmd.exe.
28
+ if os.name == "nt" and resolved and resolved.lower().endswith((".cmd", ".bat")):
29
+ exec_cmd = ["cmd", "/c", resolved, *command[1:]]
30
+
31
+ try:
32
+ proc = subprocess.run(
33
+ exec_cmd,
34
+ cwd=str(cwd),
35
+ text=True,
36
+ encoding="utf-8",
37
+ errors="replace",
38
+ capture_output=True,
39
+ timeout=timeout,
40
+ check=False,
41
+ )
42
+ return ProcessResult(code=proc.returncode, stdout=proc.stdout, stderr=proc.stderr)
43
+ except FileNotFoundError as exc:
44
+ return ProcessResult(code=127, stdout="", stderr=f"executable not found: {command[0]} ({exc})")
@@ -0,0 +1 @@
1
+ """Validation pipeline package."""
@@ -0,0 +1,52 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import asdict
4
+ from pathlib import Path
5
+ import re
6
+ import shlex
7
+
8
+ from orchestrator.domain.types import ValidationStepResult
9
+ from orchestrator.utils.process import run_command
10
+
11
+
12
+ def run_validation_pipeline(project_path: Path, steps: list[dict], timeout: int = 900) -> list[ValidationStepResult]:
13
+ results: list[ValidationStepResult] = []
14
+ for idx, step in enumerate(steps):
15
+ step_type = step.get("type")
16
+ name = step.get("name") or f"step-{idx + 1}"
17
+
18
+ if step_type == "command":
19
+ command = step.get("command", "")
20
+ parts = shlex.split(command)
21
+ if not parts:
22
+ results.append(ValidationStepResult(name=name, success=False, details="empty command"))
23
+ continue
24
+ outcome = run_command(parts, cwd=project_path, timeout=timeout)
25
+ details = outcome.stdout.strip() or outcome.stderr.strip()
26
+ results.append(ValidationStepResult(name=name, success=outcome.code == 0, details=details[:500]))
27
+ continue
28
+
29
+ if step_type == "file_exists":
30
+ rel = step.get("path", "")
31
+ exists = (project_path / rel).exists()
32
+ results.append(ValidationStepResult(name=name, success=exists, details=f"exists={exists} path={rel}"))
33
+ continue
34
+
35
+ if step_type == "grep":
36
+ rel = step.get("path", "")
37
+ pattern = step.get("pattern", "")
38
+ target = project_path / rel
39
+ if not target.exists():
40
+ results.append(ValidationStepResult(name=name, success=False, details=f"missing file {rel}"))
41
+ continue
42
+ content = target.read_text(encoding="utf-8")
43
+ matched = re.search(pattern, content, re.MULTILINE) is not None
44
+ results.append(ValidationStepResult(name=name, success=matched, details=f"pattern={pattern} matched={matched}"))
45
+ continue
46
+
47
+ results.append(ValidationStepResult(name=name, success=False, details=f"unknown type={step_type}"))
48
+ return results
49
+
50
+
51
+ def validation_results_to_dict(results: list[ValidationStepResult]) -> list[dict]:
52
+ return [asdict(r) for r in results]