Implement core SmartTools functionality
- providers.py: Provider abstraction with mock support - tool.py: Tool loading, saving, and wrapper script generation - runner.py: Tool execution engine with prompt building - ui.py: Dialog-based UI for tool management - cli.py: CLI entry point with list/create/edit/delete/test commands ~430 lines of Python as designed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
"""Provider abstraction for AI CLI tools."""
|
||||
|
||||
import subprocess
|
||||
import shutil
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class ProviderResult:
|
||||
"""Result from a provider call."""
|
||||
text: str
|
||||
success: bool
|
||||
error: Optional[str] = None
|
||||
|
||||
|
||||
def call_provider(provider: str, args: str, prompt: str, timeout: int = 300) -> ProviderResult:
|
||||
"""
|
||||
Call an AI CLI tool with the given prompt.
|
||||
|
||||
Args:
|
||||
provider: The CLI command (e.g., 'codex', 'claude', 'gemini')
|
||||
args: Additional arguments for the provider (e.g., '-p')
|
||||
prompt: The prompt to send
|
||||
timeout: Maximum execution time in seconds
|
||||
|
||||
Returns:
|
||||
ProviderResult with the response text or error
|
||||
"""
|
||||
# Check if provider exists
|
||||
if not shutil.which(provider):
|
||||
return ProviderResult(
|
||||
text="",
|
||||
success=False,
|
||||
error=f"Provider '{provider}' not found. Is it installed and in PATH?"
|
||||
)
|
||||
|
||||
# Build command
|
||||
cmd = f"{provider} {args}".strip()
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
shell=True,
|
||||
input=prompt,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
return ProviderResult(
|
||||
text="",
|
||||
success=False,
|
||||
error=f"Provider exited with code {result.returncode}: {result.stderr}"
|
||||
)
|
||||
|
||||
return ProviderResult(text=result.stdout, success=True)
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
return ProviderResult(
|
||||
text="",
|
||||
success=False,
|
||||
error=f"Provider timed out after {timeout} seconds"
|
||||
)
|
||||
except Exception as e:
|
||||
return ProviderResult(
|
||||
text="",
|
||||
success=False,
|
||||
error=f"Provider error: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
def mock_provider(prompt: str) -> ProviderResult:
|
||||
"""
|
||||
Return a mock response for testing.
|
||||
|
||||
Args:
|
||||
prompt: The prompt (used for generating mock response)
|
||||
|
||||
Returns:
|
||||
ProviderResult with mock response
|
||||
"""
|
||||
lines = prompt.strip().split('\n')
|
||||
preview = lines[0][:50] + "..." if len(lines[0]) > 50 else lines[0]
|
||||
|
||||
return ProviderResult(
|
||||
text=f"[MOCK RESPONSE]\n"
|
||||
f"Prompt length: {len(prompt)} chars, {len(lines)} lines\n"
|
||||
f"First line: {preview}\n"
|
||||
f"\n"
|
||||
f"This is a mock response. Use a real provider for actual output.",
|
||||
success=True
|
||||
)
|
||||
|
||||
|
||||
def get_provider_func(provider: str):
|
||||
"""
|
||||
Get the appropriate provider function.
|
||||
|
||||
Args:
|
||||
provider: Provider name ('mock' for mock, anything else for real)
|
||||
|
||||
Returns:
|
||||
Callable that takes (prompt) or (provider, args, prompt)
|
||||
"""
|
||||
if provider.lower() == "mock":
|
||||
return lambda prompt, **kwargs: mock_provider(prompt)
|
||||
else:
|
||||
return lambda prompt, args="", **kwargs: call_provider(provider, args, prompt, **kwargs)
|
||||
Reference in New Issue
Block a user