Rename project from SmartTools to CmdForge

Major rename operation:
- Rename Python package: smarttools -> cmdforge
- Update CLI entry point: smarttools -> cmdforge
- Update all imports and module references
- Update pyproject.toml with new name, URLs, entry point
- Update all HTML templates with new branding
- Update documentation (CLAUDE.md, README.md, docs/*, wiki/*)
- Update environment variables:
  - SMARTTOOLS_ENV -> CMDFORGE_ENV
  - SMARTTOOLS_REGISTRY_DB -> CMDFORGE_REGISTRY_DB
  - SMARTTOOLS_TOKEN -> CMDFORGE_TOKEN
  - SMARTTOOLS_REGISTRY_* -> CMDFORGE_REGISTRY_*
- Update Dockerfile and docker-compose.yml
- Update tests to use new package name
- Update scripts and examples
- Update package.json and tailwind.config.js

All 158 unit tests pass. The CLI is working correctly with the
new cmdforge command.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
This commit is contained in:
rob
2026-01-02 22:15:55 -04:00
co-authored by Claude Opus 4.5
parent 2939dd806b
commit 497fe87fc5
113 changed files with 822 additions and 822 deletions
+8 -8
View File
@@ -1,8 +1,8 @@
# SmartTools Test Suite
# CmdForge Test Suite
## Overview
The test suite covers the core SmartTools modules with **158 unit tests** organized into four main test files:
The test suite covers the core CmdForge modules with **158 unit tests** organized into four main test files:
| Test File | Module Tested | Test Count | Description |
|-----------|---------------|------------|-------------|
@@ -28,7 +28,7 @@ pytest tests/test_tool.py -v
pytest tests/test_runner.py::TestSubstituteVariables -v
# Run with coverage
pytest tests/ --cov=smarttools --cov-report=html
pytest tests/ --cov=cmdforge --cov-report=html
```
## Test Categories
@@ -83,7 +83,7 @@ pytest tests/ --cov=smarttools --cov-report=html
Run with a local registry server:
```bash
# Start registry server
python -m smarttools.registry.app
python -m cmdforge.registry.app
# Run integration tests
pytest tests/test_registry_integration.py -v -m integration
@@ -97,15 +97,15 @@ Common fixtures used across tests:
@pytest.fixture
def temp_tools_dir(tmp_path):
"""Redirect TOOLS_DIR and BIN_DIR to temp directory."""
with patch('smarttools.tool.TOOLS_DIR', tmp_path / ".smarttools"):
with patch('smarttools.tool.BIN_DIR', tmp_path / ".local" / "bin"):
with patch('cmdforge.tool.TOOLS_DIR', tmp_path / ".cmdforge"):
with patch('cmdforge.tool.BIN_DIR', tmp_path / ".local" / "bin"):
yield tmp_path
@pytest.fixture
def temp_providers_file(tmp_path):
"""Redirect providers.yaml to temp directory."""
providers_file = tmp_path / ".smarttools" / "providers.yaml"
with patch('smarttools.providers.PROVIDERS_FILE', providers_file):
providers_file = tmp_path / ".cmdforge" / "providers.yaml"
with patch('cmdforge.providers.PROVIDERS_FILE', providers_file):
yield providers_file
```
+61 -61
View File
@@ -7,8 +7,8 @@ from io import StringIO
import pytest
from smarttools.cli import main
from smarttools.tool import Tool, ToolArgument, PromptStep
from cmdforge.cli import main
from cmdforge.tool import Tool, ToolArgument, PromptStep
class TestCLIBasics:
@@ -17,34 +17,34 @@ class TestCLIBasics:
def test_help_flag(self, capsys):
"""--help should show usage."""
with pytest.raises(SystemExit) as exc_info:
with patch('sys.argv', ['smarttools', '--help']):
with patch('sys.argv', ['cmdforge', '--help']):
main()
assert exc_info.value.code == 0
captured = capsys.readouterr()
assert 'usage' in captured.out.lower() or 'smarttools' in captured.out.lower()
assert 'usage' in captured.out.lower() or 'cmdforge' in captured.out.lower()
def test_version_flag(self, capsys):
"""--version should show version."""
with pytest.raises(SystemExit) as exc_info:
with patch('sys.argv', ['smarttools', '--version']):
with patch('sys.argv', ['cmdforge', '--version']):
main()
assert exc_info.value.code == 0
class TestListCommand:
"""Tests for 'smarttools list' command."""
"""Tests for 'cmdforge list' command."""
@pytest.fixture
def temp_tools_dir(self, tmp_path):
with patch('smarttools.tool.TOOLS_DIR', tmp_path / ".smarttools"):
with patch('smarttools.tool.BIN_DIR', tmp_path / ".local" / "bin"):
with patch('cmdforge.tool.TOOLS_DIR', tmp_path / ".cmdforge"):
with patch('cmdforge.tool.BIN_DIR', tmp_path / ".local" / "bin"):
yield tmp_path
def test_list_empty(self, temp_tools_dir, capsys):
"""List with no tools should show message."""
with patch('sys.argv', ['smarttools', 'list']):
with patch('sys.argv', ['cmdforge', 'list']):
result = main()
assert result == 0
@@ -53,12 +53,12 @@ class TestListCommand:
def test_list_with_tools(self, temp_tools_dir, capsys):
"""List should show available tools."""
from smarttools.tool import save_tool
from cmdforge.tool import save_tool
save_tool(Tool(name="test-tool", description="A test tool"))
save_tool(Tool(name="another-tool", description="Another tool"))
with patch('sys.argv', ['smarttools', 'list']):
with patch('sys.argv', ['cmdforge', 'list']):
result = main()
assert result == 0
@@ -68,42 +68,42 @@ class TestListCommand:
class TestCreateCommand:
"""Tests for 'smarttools create' command."""
"""Tests for 'cmdforge create' command."""
@pytest.fixture
def temp_tools_dir(self, tmp_path):
with patch('smarttools.tool.TOOLS_DIR', tmp_path / ".smarttools"):
with patch('smarttools.tool.BIN_DIR', tmp_path / ".local" / "bin"):
with patch('cmdforge.tool.TOOLS_DIR', tmp_path / ".cmdforge"):
with patch('cmdforge.tool.BIN_DIR', tmp_path / ".local" / "bin"):
yield tmp_path
def test_create_minimal(self, temp_tools_dir, capsys):
"""Create a minimal tool."""
with patch('sys.argv', ['smarttools', 'create', 'my-tool']):
with patch('sys.argv', ['cmdforge', 'create', 'my-tool']):
result = main()
assert result == 0
from smarttools.tool import tool_exists
from cmdforge.tool import tool_exists
assert tool_exists('my-tool')
def test_create_with_description(self, temp_tools_dir):
"""Create tool with description."""
with patch('sys.argv', ['smarttools', 'create', 'described-tool',
with patch('sys.argv', ['cmdforge', 'create', 'described-tool',
'-d', 'A helpful description']):
result = main()
assert result == 0
from smarttools.tool import load_tool
from cmdforge.tool import load_tool
tool = load_tool('described-tool')
assert tool.description == 'A helpful description'
def test_create_duplicate_fails(self, temp_tools_dir, capsys):
"""Creating duplicate tool should fail."""
from smarttools.tool import save_tool
from cmdforge.tool import save_tool
save_tool(Tool(name="existing"))
with patch('sys.argv', ['smarttools', 'create', 'existing']):
with patch('sys.argv', ['cmdforge', 'create', 'existing']):
result = main()
assert result != 0
@@ -113,28 +113,28 @@ class TestCreateCommand:
@pytest.mark.skip(reason="CLI does not yet validate tool names - potential enhancement")
def test_create_invalid_name(self, temp_tools_dir, capsys):
"""Invalid tool name should fail."""
with patch('sys.argv', ['smarttools', 'create', 'invalid/name']):
with patch('sys.argv', ['cmdforge', 'create', 'invalid/name']):
result = main()
assert result != 0
class TestDeleteCommand:
"""Tests for 'smarttools delete' command."""
"""Tests for 'cmdforge delete' command."""
@pytest.fixture
def temp_tools_dir(self, tmp_path):
with patch('smarttools.tool.TOOLS_DIR', tmp_path / ".smarttools"):
with patch('smarttools.tool.BIN_DIR', tmp_path / ".local" / "bin"):
with patch('cmdforge.tool.TOOLS_DIR', tmp_path / ".cmdforge"):
with patch('cmdforge.tool.BIN_DIR', tmp_path / ".local" / "bin"):
yield tmp_path
def test_delete_existing(self, temp_tools_dir, capsys):
"""Delete an existing tool."""
from smarttools.tool import save_tool, tool_exists
from cmdforge.tool import save_tool, tool_exists
save_tool(Tool(name="to-delete"))
assert tool_exists("to-delete")
with patch('sys.argv', ['smarttools', 'delete', 'to-delete', '-f']):
with patch('sys.argv', ['cmdforge', 'delete', 'to-delete', '-f']):
result = main()
assert result == 0
@@ -142,24 +142,24 @@ class TestDeleteCommand:
def test_delete_nonexistent(self, temp_tools_dir, capsys):
"""Deleting nonexistent tool should fail."""
with patch('sys.argv', ['smarttools', 'delete', 'nonexistent', '-f']):
with patch('sys.argv', ['cmdforge', 'delete', 'nonexistent', '-f']):
result = main()
assert result != 0
class TestRunCommand:
"""Tests for 'smarttools run' command."""
"""Tests for 'cmdforge run' command."""
@pytest.fixture
def temp_tools_dir(self, tmp_path):
with patch('smarttools.tool.TOOLS_DIR', tmp_path / ".smarttools"):
with patch('smarttools.tool.BIN_DIR', tmp_path / ".local" / "bin"):
with patch('cmdforge.tool.TOOLS_DIR', tmp_path / ".cmdforge"):
with patch('cmdforge.tool.BIN_DIR', tmp_path / ".local" / "bin"):
yield tmp_path
def test_run_simple_tool(self, temp_tools_dir, capsys):
"""Run a simple tool without AI calls."""
from smarttools.tool import save_tool
from cmdforge.tool import save_tool
tool = Tool(
name="echo",
@@ -167,7 +167,7 @@ class TestRunCommand:
)
save_tool(tool)
with patch('sys.argv', ['smarttools', 'run', 'echo']):
with patch('sys.argv', ['cmdforge', 'run', 'echo']):
with patch('sys.stdin', StringIO("Hello")):
with patch('sys.stdin.isatty', return_value=False):
result = main()
@@ -178,7 +178,7 @@ class TestRunCommand:
def test_run_with_mock_provider(self, temp_tools_dir, capsys):
"""Run tool with mock provider."""
from smarttools.tool import save_tool
from cmdforge.tool import save_tool
tool = Tool(
name="summarize",
@@ -189,7 +189,7 @@ class TestRunCommand:
)
save_tool(tool)
with patch('sys.argv', ['smarttools', 'run', 'summarize']):
with patch('sys.argv', ['cmdforge', 'run', 'summarize']):
with patch('sys.stdin', StringIO("Some text")):
with patch('sys.stdin.isatty', return_value=False):
result = main()
@@ -200,24 +200,24 @@ class TestRunCommand:
def test_run_nonexistent_tool(self, temp_tools_dir, capsys):
"""Running nonexistent tool should fail."""
with patch('sys.argv', ['smarttools', 'run', 'nonexistent']):
with patch('sys.argv', ['cmdforge', 'run', 'nonexistent']):
result = main()
assert result != 0
class TestTestCommand:
"""Tests for 'smarttools test' command."""
"""Tests for 'cmdforge test' command."""
@pytest.fixture
def temp_tools_dir(self, tmp_path):
with patch('smarttools.tool.TOOLS_DIR', tmp_path / ".smarttools"):
with patch('smarttools.tool.BIN_DIR', tmp_path / ".local" / "bin"):
with patch('cmdforge.tool.TOOLS_DIR', tmp_path / ".cmdforge"):
with patch('cmdforge.tool.BIN_DIR', tmp_path / ".local" / "bin"):
yield tmp_path
def test_test_tool(self, temp_tools_dir, capsys):
"""Test command should run with mock provider."""
from smarttools.tool import save_tool
from cmdforge.tool import save_tool
tool = Tool(
name="test-me",
@@ -229,7 +229,7 @@ class TestTestCommand:
save_tool(tool)
# Provide stdin input for the test command
with patch('sys.argv', ['smarttools', 'test', 'test-me']):
with patch('sys.argv', ['cmdforge', 'test', 'test-me']):
with patch('sys.stdin', StringIO("test input")):
result = main()
@@ -240,17 +240,17 @@ class TestTestCommand:
class TestProvidersCommand:
"""Tests for 'smarttools providers' command."""
"""Tests for 'cmdforge providers' command."""
@pytest.fixture
def temp_providers_file(self, tmp_path):
providers_file = tmp_path / ".smarttools" / "providers.yaml"
with patch('smarttools.providers.PROVIDERS_FILE', providers_file):
providers_file = tmp_path / ".cmdforge" / "providers.yaml"
with patch('cmdforge.providers.PROVIDERS_FILE', providers_file):
yield providers_file
def test_providers_list(self, temp_providers_file, capsys):
"""List providers."""
with patch('sys.argv', ['smarttools', 'providers']):
with patch('sys.argv', ['cmdforge', 'providers']):
result = main()
assert result == 0
@@ -260,47 +260,47 @@ class TestProvidersCommand:
def test_providers_add(self, temp_providers_file, capsys):
"""Add a custom provider."""
with patch('sys.argv', ['smarttools', 'providers', 'add',
with patch('sys.argv', ['cmdforge', 'providers', 'add',
'custom', 'my-ai --prompt']):
result = main()
assert result == 0
from smarttools.providers import get_provider
from cmdforge.providers import get_provider
provider = get_provider('custom')
assert provider is not None
assert provider.command == 'my-ai --prompt'
def test_providers_remove(self, temp_providers_file, capsys):
"""Remove a provider."""
from smarttools.providers import add_provider, Provider
from cmdforge.providers import add_provider, Provider
add_provider(Provider('removeme', 'cmd'))
with patch('sys.argv', ['smarttools', 'providers', 'remove', 'removeme']):
with patch('sys.argv', ['cmdforge', 'providers', 'remove', 'removeme']):
result = main()
assert result == 0
from smarttools.providers import get_provider
from cmdforge.providers import get_provider
assert get_provider('removeme') is None
class TestRefreshCommand:
"""Tests for 'smarttools refresh' command."""
"""Tests for 'cmdforge refresh' command."""
@pytest.fixture
def temp_tools_dir(self, tmp_path):
with patch('smarttools.tool.TOOLS_DIR', tmp_path / ".smarttools"):
with patch('smarttools.tool.BIN_DIR', tmp_path / ".local" / "bin"):
with patch('cmdforge.tool.TOOLS_DIR', tmp_path / ".cmdforge"):
with patch('cmdforge.tool.BIN_DIR', tmp_path / ".local" / "bin"):
yield tmp_path
def test_refresh_creates_wrappers(self, temp_tools_dir, capsys):
"""Refresh should create wrapper scripts."""
from smarttools.tool import save_tool, get_bin_dir
from cmdforge.tool import save_tool, get_bin_dir
save_tool(Tool(name="wrapper-test"))
with patch('sys.argv', ['smarttools', 'refresh']):
with patch('sys.argv', ['cmdforge', 'refresh']):
result = main()
assert result == 0
@@ -310,17 +310,17 @@ class TestRefreshCommand:
class TestDocsCommand:
"""Tests for 'smarttools docs' command."""
"""Tests for 'cmdforge docs' command."""
@pytest.fixture
def temp_tools_dir(self, tmp_path):
with patch('smarttools.tool.TOOLS_DIR', tmp_path / ".smarttools"):
with patch('smarttools.tool.BIN_DIR', tmp_path / ".local" / "bin"):
with patch('cmdforge.tool.TOOLS_DIR', tmp_path / ".cmdforge"):
with patch('cmdforge.tool.BIN_DIR', tmp_path / ".local" / "bin"):
yield tmp_path
def test_docs_for_tool_with_readme(self, temp_tools_dir, capsys):
"""Docs should show README content when it exists."""
from smarttools.tool import save_tool, get_tools_dir
from cmdforge.tool import save_tool, get_tools_dir
tool = Tool(
name="documented",
@@ -332,7 +332,7 @@ class TestDocsCommand:
readme_path = get_tools_dir() / "documented" / "README.md"
readme_path.write_text("# Documented Tool\n\nThis is the documentation.")
with patch('sys.argv', ['smarttools', 'docs', 'documented']):
with patch('sys.argv', ['cmdforge', 'docs', 'documented']):
result = main()
assert result == 0
@@ -342,12 +342,12 @@ class TestDocsCommand:
def test_docs_no_readme(self, temp_tools_dir, capsys):
"""Docs without README should prompt to create one."""
from smarttools.tool import save_tool
from cmdforge.tool import save_tool
tool = Tool(name="no-docs")
save_tool(tool)
with patch('sys.argv', ['smarttools', 'docs', 'no-docs']):
with patch('sys.argv', ['cmdforge', 'docs', 'no-docs']):
result = main()
assert result == 1 # Returns 1 when no README
+7 -7
View File
@@ -7,7 +7,7 @@ from unittest.mock import patch, MagicMock
import pytest
import yaml
from smarttools.providers import (
from cmdforge.providers import (
Provider, ProviderResult,
load_providers, save_providers, get_provider,
add_provider, delete_provider,
@@ -122,8 +122,8 @@ class TestProviderPersistence:
@pytest.fixture
def temp_providers_file(self, tmp_path):
"""Create a temporary providers file."""
providers_file = tmp_path / ".smarttools" / "providers.yaml"
with patch('smarttools.providers.PROVIDERS_FILE', providers_file):
providers_file = tmp_path / ".cmdforge" / "providers.yaml"
with patch('cmdforge.providers.PROVIDERS_FILE', providers_file):
yield providers_file
def test_save_and_load_providers(self, temp_providers_file):
@@ -220,8 +220,8 @@ class TestCallProvider:
@pytest.fixture
def temp_providers_file(self, tmp_path):
"""Create a temporary providers file."""
providers_file = tmp_path / ".smarttools" / "providers.yaml"
with patch('smarttools.providers.PROVIDERS_FILE', providers_file):
providers_file = tmp_path / ".cmdforge" / "providers.yaml"
with patch('cmdforge.providers.PROVIDERS_FILE', providers_file):
yield providers_file
def test_call_mock_provider(self, temp_providers_file):
@@ -342,8 +342,8 @@ class TestProviderCommandParsing:
@pytest.fixture
def temp_providers_file(self, tmp_path):
providers_file = tmp_path / ".smarttools" / "providers.yaml"
with patch('smarttools.providers.PROVIDERS_FILE', providers_file):
providers_file = tmp_path / ".cmdforge" / "providers.yaml"
with patch('cmdforge.providers.PROVIDERS_FILE', providers_file):
yield providers_file
@patch('shutil.which')
+7 -7
View File
@@ -1,10 +1,10 @@
"""Integration tests for SmartTools Registry.
"""Integration tests for CmdForge Registry.
These tests verify the CLI and server work together correctly.
Run with: pytest tests/test_registry_integration.py -v
Note: These tests require the registry server to be running locally:
python -m smarttools.registry.app
python -m cmdforge.registry.app
"""
import json
@@ -18,17 +18,17 @@ import pytest
import yaml
# Test without requiring server for unit tests
from smarttools.config import Config, RegistryConfig, load_config, save_config
from smarttools.manifest import (
from cmdforge.config import Config, RegistryConfig, load_config, save_config
from cmdforge.manifest import (
Manifest, Dependency, ToolOverride,
load_manifest, save_manifest, create_manifest,
parse_version_constraint
)
from smarttools.resolver import (
from cmdforge.resolver import (
ToolSpec, ToolResolver, ResolvedTool,
resolve_tool, find_tool, list_installed_tools
)
from smarttools.registry_client import (
from cmdforge.registry_client import (
RegistryClient, RegistryError, PaginatedResponse,
ToolInfo, DownloadResult
)
@@ -238,7 +238,7 @@ class TestToolResolver:
def test_deterministic_owner_order(self, tmp_path):
"""Test that official namespace is preferred over others."""
# Create fake tool directories
tools_dir = tmp_path / ".smarttools"
tools_dir = tmp_path / ".cmdforge"
# Create alice/mytool
alice_tool = tools_dir / "alice" / "mytool"
+12 -12
View File
@@ -3,15 +3,15 @@
import pytest
from unittest.mock import patch, MagicMock
from smarttools.runner import (
from cmdforge.runner import (
substitute_variables,
execute_prompt_step,
execute_code_step,
run_tool,
create_argument_parser
)
from smarttools.tool import Tool, ToolArgument, PromptStep, CodeStep
from smarttools.providers import ProviderResult
from cmdforge.tool import Tool, ToolArgument, PromptStep, CodeStep
from cmdforge.providers import ProviderResult
class TestSubstituteVariables:
@@ -96,7 +96,7 @@ Line 3: {var1} again"""
class TestExecutePromptStep:
"""Tests for prompt step execution."""
@patch('smarttools.runner.call_provider')
@patch('cmdforge.runner.call_provider')
def test_successful_prompt(self, mock_call):
mock_call.return_value = ProviderResult(
text="This is the response",
@@ -119,7 +119,7 @@ class TestExecutePromptStep:
call_args = mock_call.call_args
assert "Some text to summarize" in call_args[0][1]
@patch('smarttools.runner.call_provider')
@patch('cmdforge.runner.call_provider')
def test_failed_prompt(self, mock_call):
mock_call.return_value = ProviderResult(
text="",
@@ -133,7 +133,7 @@ class TestExecutePromptStep:
assert success is False
assert output == ""
@patch('smarttools.runner.mock_provider')
@patch('cmdforge.runner.mock_provider')
def test_mock_provider_used(self, mock_mock):
mock_mock.return_value = ProviderResult(text="mock response", success=True)
@@ -143,7 +143,7 @@ class TestExecutePromptStep:
assert success is True
mock_mock.assert_called_once()
@patch('smarttools.runner.call_provider')
@patch('cmdforge.runner.call_provider')
def test_provider_override(self, mock_call):
mock_call.return_value = ProviderResult(text="response", success=True)
@@ -277,7 +277,7 @@ class TestRunTool:
output, exit_code = run_tool(tool, "", {"name": "Alice"})
assert output == "Hello, Alice!"
@patch('smarttools.runner.call_provider')
@patch('cmdforge.runner.call_provider')
def test_tool_with_prompt_step(self, mock_call):
mock_call.return_value = ProviderResult(text="Summarized!", success=True)
@@ -315,7 +315,7 @@ class TestRunTool:
assert exit_code == 0
assert output == "Words: 4"
@patch('smarttools.runner.call_provider')
@patch('cmdforge.runner.call_provider')
def test_tool_with_multiple_steps(self, mock_call):
mock_call.return_value = ProviderResult(text="AI response", success=True)
@@ -334,7 +334,7 @@ class TestRunTool:
assert exit_code == 0
assert "Result: AI response" in output
@patch('smarttools.runner.call_provider')
@patch('cmdforge.runner.call_provider')
def test_tool_dry_run(self, mock_call):
tool = Tool(
name="test",
@@ -351,7 +351,7 @@ class TestRunTool:
assert exit_code == 0
assert "DRY RUN" in output
@patch('smarttools.runner.call_provider')
@patch('cmdforge.runner.call_provider')
def test_tool_provider_override(self, mock_call):
mock_call.return_value = ProviderResult(text="response", success=True)
@@ -368,7 +368,7 @@ class TestRunTool:
# Should use override
assert mock_call.call_args[0][0] == "gpt4"
@patch('smarttools.runner.call_provider')
@patch('cmdforge.runner.call_provider')
def test_tool_prompt_failure(self, mock_call):
mock_call.return_value = ProviderResult(text="", success=False, error="API error")
+8 -8
View File
@@ -7,7 +7,7 @@ from unittest.mock import patch
import pytest
import yaml
from smarttools.tool import (
from cmdforge.tool import (
Tool, ToolArgument, PromptStep, CodeStep,
validate_tool_name, load_tool, save_tool, delete_tool,
list_tools, tool_exists, create_wrapper_script,
@@ -390,8 +390,8 @@ class TestToolPersistence:
@pytest.fixture
def temp_tools_dir(self, tmp_path):
"""Create a temporary tools directory."""
with patch('smarttools.tool.TOOLS_DIR', tmp_path / ".smarttools"):
with patch('smarttools.tool.BIN_DIR', tmp_path / ".local" / "bin"):
with patch('cmdforge.tool.TOOLS_DIR', tmp_path / ".cmdforge"):
with patch('cmdforge.tool.BIN_DIR', tmp_path / ".local" / "bin"):
yield tmp_path
def test_save_and_load_tool(self, temp_tools_dir):
@@ -457,14 +457,14 @@ class TestToolPersistence:
def test_create_wrapper_script(self, temp_tools_dir):
save_tool(Tool(name="wrapper-test"))
from smarttools.tool import get_bin_dir
from cmdforge.tool import get_bin_dir
wrapper_path = get_bin_dir() / "wrapper-test"
assert wrapper_path.exists()
content = wrapper_path.read_text()
assert "#!/bin/bash" in content
assert "wrapper-test" in content
assert "smarttools.runner" in content
assert "cmdforge.runner" in content
class TestLegacyFormat:
@@ -473,9 +473,9 @@ class TestLegacyFormat:
@pytest.fixture
def temp_tools_dir(self, tmp_path):
"""Create a temporary tools directory."""
with patch('smarttools.tool.TOOLS_DIR', tmp_path / ".smarttools"):
with patch('smarttools.tool.BIN_DIR', tmp_path / ".local" / "bin"):
yield tmp_path / ".smarttools"
with patch('cmdforge.tool.TOOLS_DIR', tmp_path / ".cmdforge"):
with patch('cmdforge.tool.BIN_DIR', tmp_path / ".local" / "bin"):
yield tmp_path / ".cmdforge"
def test_load_legacy_format(self, temp_tools_dir):
"""Test loading a tool in the old format."""