Add system dependencies support for tools
Tools can now declare system-level package dependencies (apt, brew, pacman, dnf) that get checked and optionally installed when the tool is installed or run. Features: - SystemDependency dataclass with short form (string) and long form (dict) - New system_deps.py module with platform detection and installation - `cmdforge system-deps <tool>` command to check/install system packages - `cmdforge check` now shows both CmdForge and system dependencies - `cmdforge registry install` prompts for system dep installation - GUI: System Dependencies section in Tool Builder with add/edit dialog - Runner warns about missing system deps before execution - Integration with project install (manifest and lock-based) Also includes: - Quote paths in wrapper scripts for spaces support - Tests for value type preservation in code steps - Unskip invalid tool name test Co-Authored-By: Claude Opus 4.5 <[email protected]>
This commit is contained in:
+56
-4
@@ -92,6 +92,21 @@ Line 3: {var1} again"""
|
||||
result = substitute_variables("Count: {n}", {"n": 42})
|
||||
assert result == "Count: 42"
|
||||
|
||||
def test_zero_value_preserved(self):
|
||||
"""Zero should be preserved, not converted to empty string."""
|
||||
result = substitute_variables("Count: {n}", {"n": 0})
|
||||
assert result == "Count: 0"
|
||||
|
||||
def test_false_value_preserved(self):
|
||||
"""False should be preserved, not converted to empty string."""
|
||||
result = substitute_variables("Enabled: {flag}", {"flag": False})
|
||||
assert result == "Enabled: False"
|
||||
|
||||
def test_true_value_preserved(self):
|
||||
"""True should be preserved as 'True'."""
|
||||
result = substitute_variables("Enabled: {flag}", {"flag": True})
|
||||
assert result == "Enabled: True"
|
||||
|
||||
|
||||
class TestExecutePromptStep:
|
||||
"""Tests for prompt step execution."""
|
||||
@@ -179,9 +194,10 @@ class TestExecuteCodeStep:
|
||||
outputs, success = execute_code_step(step, variables)
|
||||
|
||||
assert success is True
|
||||
assert outputs["a"] == "1"
|
||||
assert outputs["b"] == "2"
|
||||
assert outputs["c"] == "3"
|
||||
# Code step outputs preserve original types (integers here)
|
||||
assert outputs["a"] == 1
|
||||
assert outputs["b"] == 2
|
||||
assert outputs["c"] == 3
|
||||
|
||||
def test_code_uses_variables(self):
|
||||
step = CodeStep(
|
||||
@@ -242,7 +258,43 @@ class TestExecuteCodeStep:
|
||||
outputs, success = execute_code_step(step, variables)
|
||||
|
||||
assert success is True
|
||||
assert outputs["result"] == "3"
|
||||
# Code step outputs preserve original types (integer here)
|
||||
assert outputs["result"] == 3
|
||||
|
||||
def test_code_preserves_complex_types(self):
|
||||
"""Code step outputs preserve lists/dicts for code→code workflows."""
|
||||
step = CodeStep(
|
||||
code="data = [1, 2, 3]\ninfo = {'key': 'value'}",
|
||||
output_var="data, info"
|
||||
)
|
||||
variables = {"input": ""}
|
||||
|
||||
outputs, success = execute_code_step(step, variables)
|
||||
|
||||
assert success is True
|
||||
assert outputs["data"] == [1, 2, 3]
|
||||
assert outputs["info"] == {"key": "value"}
|
||||
|
||||
def test_code_to_code_workflow(self):
|
||||
"""Test that complex types can flow between code steps."""
|
||||
# First step creates a list
|
||||
step1 = CodeStep(
|
||||
code="numbers = [1, 2, 3, 4, 5]",
|
||||
output_var="numbers"
|
||||
)
|
||||
variables = {"input": ""}
|
||||
outputs1, _ = execute_code_step(step1, variables)
|
||||
variables.update(outputs1)
|
||||
|
||||
# Second step uses the list directly (no re-parsing needed)
|
||||
step2 = CodeStep(
|
||||
code="total = sum(numbers)",
|
||||
output_var="total"
|
||||
)
|
||||
outputs2, success = execute_code_step(step2, variables)
|
||||
|
||||
assert success is True
|
||||
assert outputs2["total"] == 15
|
||||
|
||||
|
||||
class TestRunTool:
|
||||
|
||||
Reference in New Issue
Block a user