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.
- execforge-0.1.0.dist-info/METADATA +367 -0
- execforge-0.1.0.dist-info/RECORD +44 -0
- execforge-0.1.0.dist-info/WHEEL +5 -0
- execforge-0.1.0.dist-info/entry_points.txt +5 -0
- execforge-0.1.0.dist-info/licenses/LICENSE +21 -0
- execforge-0.1.0.dist-info/top_level.txt +1 -0
- orchestrator/__init__.py +4 -0
- orchestrator/__main__.py +5 -0
- orchestrator/backends/__init__.py +1 -0
- orchestrator/backends/base.py +29 -0
- orchestrator/backends/factory.py +53 -0
- orchestrator/backends/llm_cli_backend.py +87 -0
- orchestrator/backends/mock_backend.py +34 -0
- orchestrator/backends/shell_backend.py +49 -0
- orchestrator/cli/__init__.py +1 -0
- orchestrator/cli/main.py +971 -0
- orchestrator/config.py +272 -0
- orchestrator/domain/__init__.py +1 -0
- orchestrator/domain/types.py +77 -0
- orchestrator/exceptions.py +18 -0
- orchestrator/git/__init__.py +1 -0
- orchestrator/git/service.py +202 -0
- orchestrator/logging_setup.py +53 -0
- orchestrator/prompts/__init__.py +1 -0
- orchestrator/prompts/parser.py +91 -0
- orchestrator/reporting/__init__.py +1 -0
- orchestrator/reporting/console.py +197 -0
- orchestrator/reporting/events.py +44 -0
- orchestrator/reporting/selection_result.py +15 -0
- orchestrator/services/__init__.py +1 -0
- orchestrator/services/agent_runner.py +831 -0
- orchestrator/services/agent_service.py +122 -0
- orchestrator/services/project_service.py +47 -0
- orchestrator/services/prompt_source_service.py +65 -0
- orchestrator/services/run_service.py +42 -0
- orchestrator/services/step_executor.py +100 -0
- orchestrator/services/task_service.py +155 -0
- orchestrator/storage/__init__.py +1 -0
- orchestrator/storage/db.py +29 -0
- orchestrator/storage/models.py +95 -0
- orchestrator/utils/__init__.py +1 -0
- orchestrator/utils/process.py +44 -0
- orchestrator/validation/__init__.py +1 -0
- 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]
|