walidsobhie-code
reorganize: consolidate root level to 20 folders
b8e3e42
#!/usr/bin/env python3
"""
Stack 2.9 - CLI Entry Point
A complete CLI and agent interface showcasing Stack 2.9 capabilities.
"""
import os
import sys
import cmd
import json
import argparse
import asyncio
import subprocess
from pathlib import Path
from typing import Any, Dict, List, Optional
from .agent import create_agent, StackAgent
from .context import create_context_manager
# ============================================================================
# UTILITIES
# ============================================================================
def print_banner():
"""Print the Stack 2.9 banner."""
banner = r"""
____ _ _ _
| _ \ ___ _ __ __| |_ __ ___ (_)_ __ | | __
| |_) / _ \ '_ \ / _` | '__/ _ \| | '_ \ | |/ /
| _ < __/ | | | (_| | | | (_) | | | | | | <
|_| \_\___|_| |_|\__,_|_| \___/|_|_| |_| |_|\_\
Stack 2.9 CLI & Agent Interface
"""
print(banner)
def print_colored(text: str, color: str = "white", bold: bool = False):
"""Print colored text."""
colors = {
"red": "\033[91m",
"green": "\033[92m",
"yellow": "\033[93m",
"blue": "\033[94m",
"magenta": "\033[95m",
"cyan": "\033[96m",
"white": "\033[97m",
"reset": "\033[0m"
}
if bold:
text = f"\033[1m{text}\033[0m"
sys.stdout.write(colors.get(color, "white"))
print(text)
sys.stdout.write(colors["reset"])
def format_output(data: Any, format: str = "text") -> str:
"""Format output for display."""
if format == "json":
return json.dumps(data, indent=2)
elif isinstance(data, dict):
return "\n".join(f" {k}: {v}" for k, v in data.items())
elif isinstance(data, list):
return "\n".join(f" - {item}" for item in data)
else:
return str(data)
# ============================================================================
# MODE 1: INTERACTIVE CHAT
# ============================================================================
class ChatMode(cmd.Cmd):
"""Interactive chat mode with the agent."""
intro = """
Welcome to Stack 2.9 Interactive Chat!
Type your queries or use commands:
/tools - List available tools
/schema - Show tool schemas
/context - Show current context
/history - Show conversation history
/clear - Clear chat history
/exit - Exit chat mode
Just type your question or task to get started!
"""
prompt = "\n[Stack]> "
def __init__(self, agent: StackAgent):
super().__init__()
self.agent = agent
self.history: List[Dict[str, Any]] = []
self.show_tool_calls = False
self.output_format = "text"
self.voice_input = False
def default(self, line: str):
"""Handle user input as a query to the agent."""
if line.startswith('/'):
return
print_colored("\nThinking...", "cyan")
try:
response = self.agent.process(line)
self.history.append({
"query": line,
"response": response.content,
"tool_calls": [tc.tool_name for tc in response.tool_calls],
"timestamp": "now"
})
# Display response
print("\n" + "="*50)
print_colored("Response:", "green", bold=True)
print(response.content)
# Show tool usage if enabled
if self.show_tool_calls and response.tool_calls:
print("\n" + "-"*50)
print_colored("Tools Used:", "yellow")
for tc in response.tool_calls:
status = "✓" if tc.success else "✗"
print(f" {status} {tc.tool_name}")
if not tc.success and tc.error:
print(f" Error: {tc.error}")
if response.needs_clarification:
print_colored(f"\nNote: {response.clarification_needed}", "yellow")
except KeyboardInterrupt:
print_colored("\nInterrupted.", "red")
except Exception as e:
print_colored(f"\nError: {e}", "red")
def do_tools(self, arg):
"""List all available tools."""
tools = self.agent.context_manager.session.tools_used
if tools:
print(f"\nUsed {len(tools)} tools this session:")
for tool in set(tools):
print(f" - {tool}")
else:
print("\nNo tools used yet this session.")
def do_schema(self, arg):
"""Show tool schemas for tool calling."""
schemas = self.agent.get_schemas()
print(f"\nTool Schemas ({len(schemas)} tools):")
for schema in schemas[:10]:
print(f"\n {schema['name']}: {schema['description']}")
print(f"\n ... and {len(schemas) - 10} more")
def do_context(self, arg):
"""Show current context."""
context = self.agent.get_context()
print("\n" + context)
def do_history(self, arg):
"""Show conversation history."""
print(f"\nChat History ({len(self.history)} messages):")
for i, entry in enumerate(self.history[-10:], 1):
print(f"\n{i}. Query: {entry['query'][:50]}...")
print(f" Tools: {', '.join(entry['tool_calls'])}")
def do_clear(self, arg):
"""Clear chat history."""
self.history.clear()
self.agent.context_manager.session.messages.clear()
print_colored("Chat history cleared.", "green")
def do_voice(self, arg):
"""Toggle voice input."""
self.voice_input = not self.voice_input
status = "enabled" if self.voice_input else "disabled"
print_colored(f"Voice input {status} (note: requires additional setup)", "cyan")
def do_exit(self, arg):
"""Exit chat mode."""
print_colored("Goodbye!", "green")
return True
def do_EOF(self, arg):
"""Handle Ctrl+D."""
print()
return self.do_exit(arg)
def postcmd(self, stop: bool, line: str) -> bool:
"""Save session after each command."""
return stop
def run(self):
"""Run the chat loop."""
print_banner()
print(self.intro)
try:
self.cmdloop()
except KeyboardInterrupt:
print("\nExiting...")
# ============================================================================
# MODE 2: COMMAND EXECUTION
# ============================================================================
class CommandMode:
"""Execute single commands."""
def __init__(self, agent: StackAgent):
self.agent = agent
self.context_manager = create_context_manager()
def execute(
self,
query: str,
output_file: Optional[str] = None,
output_format: str = "text"
) -> str:
"""Execute a query and return result."""
print(f"Executing: {query}")
response = self.agent.process(query)
result = format_output(response.content, output_format)
# Save to file if requested
if output_file:
with open(output_file, 'w') as f:
f.write(result)
print(f"Output saved to: {output_file}")
return result
def execute_tools(
self,
tools: List[str],
output_file: Optional[str] = None
) -> str:
"""Execute specific tools directly."""
print(f"Executing tools: {', '.join(tools)}")
response = self.agent.process_with_tools("", tools)
if output_file:
with open(output_file, 'w') as f:
f.write(response.content)
print(f"Output saved to: {output_file}")
return response.content
# ============================================================================
# MODE 3: VOICE INTERFACE (PLACEHOLDER)
# ============================================================================
class VoiceInterface:
"""Voice input/output interface (requires additional setup)."""
def __init__(self):
self.available = self._check_availability()
def _check_availability(self) -> bool:
"""Check if voice dependencies are available."""
try:
import speech_recognition as sr
import pyttsx3
return True
except ImportError:
return False
def listen(self) -> Optional[str]:
"""Listen for voice input."""
if not self.available:
print("Voice recognition not available. Install with: pip install SpeechRecognition pyttsx3 pyaudio")
return None
try:
import speech_recognition as sr
r = sr.Recognizer()
with sr.Microphone() as source:
print("Listening... (speak now)")
audio = r.listen(source, timeout=5)
text = r.recognize_google(audio)
print(f"Heard: {text}")
return text
except Exception as e:
print(f"Voice error: {e}")
return None
def speak(self, text: str):
"""Speak text output."""
if not self.available:
print(f"(TTS not available): {text}")
return
try:
import pyttsx3
engine = pyttsx3.init()
engine.say(text)
engine.runAndWait()
except Exception as e:
print(f"TTS error: {e}")
# ============================================================================
# MAIN CLI
# ============================================================================
class StackCLI:
"""Main CLI entry point."""
def __init__(self):
self.agent = create_agent()
self.chat_mode = ChatMode(self.agent)
self.command_mode = CommandMode(self.agent)
self.voice = VoiceInterface()
def run_interactive(self):
"""Run interactive chat mode."""
self.chat_mode.run()
def run_command(self, query: str, out_file: Optional[str] = None, fmt: str = "text"):
"""Run a single command."""
result = self.command_mode.execute(query, out_file, fmt)
print(result)
return result
def run_tools(self, tools: List[str], out_file: Optional[str] = None):
"""Run specific tools."""
result = self.command_mode.execute_tools(tools, out_file)
print(result)
return result
def run_eval(self, benchmark: str, provider: str = 'ollama', model: str = None):
"""Run evaluation benchmarks."""
print_colored(f"\n=== Running {benchmark} benchmark ===", "blue")
import sys
from pathlib import Path
eval_dir = Path(__file__).parent.parent / "stack-2.9-eval"
if eval_dir.exists():
sys.path.insert(0, str(eval_dir))
try:
if benchmark == 'mbpp':
from benchmarks.mbpp import MBPP
b = MBPP(model_provider=provider, model_name=model)
elif benchmark == 'human_eval':
from benchmarks.human_eval import HumanEval
b = HumanEval(model_provider=provider, model_name=model)
elif benchmark == 'gsm8k':
from benchmarks.gsm8k import GSM8K
b = GSM8K(model_provider=provider, model_name=model)
elif benchmark == 'all':
from benchmarks.mbpp import MBPP
from benchmarks.human_eval import HumanEval
from benchmarks.gsm8k import GSM8K
for name, Benchmark in [('MBPP', MBPP), ('HumanEval', HumanEval), ('GSM8K', GSM8K)]:
print_colored(f"\n--- {name} ---", "yellow")
b = Benchmark(model_provider=provider, model_name=model)
results = b.evaluate()
print(f" Accuracy: {results['accuracy']*100:.1f}%")
return
results = b.evaluate()
print_colored(f"\nResults:", "green")
print(f" Accuracy: {results['accuracy']*100:.1f}%")
print(f" Passed: {results['pass_at_1']}/{results['total_cases']}")
print(f" Model: {results['model']}")
except Exception as e:
print_colored(f"Error: {e}", "red")
def run_patterns(self, action: str):
"""Manage learned patterns."""
print_colored(f"\n=== Pattern Management ===", "blue")
import sys
from pathlib import Path
train_dir = Path(__file__).parent.parent / "stack-2.9-training"
if train_dir.exists():
sys.path.insert(0, str(train_dir))
try:
from pattern_miner import PatternMiner
miner = PatternMiner()
if action == 'list':
patterns = miner.get_relevant_patterns(limit=20)
print_colored(f"\nStored Patterns:", "yellow")
for p in patterns:
print(f" [{p.pattern_type}] {p.code_snippet[:50]}...")
elif action == 'stats':
stats = miner.get_statistics()
print_colored(f"\nStatistics:", "yellow")
print(f" Total Feedback: {stats['total_feedback']}")
print(f" Success Rate: {stats['success_rate']:.1%}")
print(f" Total Patterns: {stats['total_patterns']}")
except Exception as e:
print_colored(f"Error: {e}", "red")
def run_voice(self):
"""Run voice mode loop."""
if not self.voice.available:
print_colored("Voice interface not available.", "red")
return
print_banner()
print("Voice Mode - Say 'exit' to quit")
try:
while True:
text = self.voice.listen()
if not text:
continue
if "exit" in text.lower():
print("Exiting...")
break
# Process with agent
response = self.agent.process(text)
print(f"\nResponse: {response.content[:200]}")
# Speak response
self.voice.speak(response.content[:200])
except KeyboardInterrupt:
print("\nExiting...")
def main():
"""Main entry point."""
parser = argparse.ArgumentParser(
description="Stack 2.9 CLI and Agent Interface",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s # Interactive chat mode
%(prog)s -c "read README.md" # Execute single command
%(prog)s -t read write # Execute specific tools
%(prog)s -v # Voice mode
"""
)
parser.add_argument(
'-c', '--command',
help="Execute a single query/command"
)
parser.add_argument(
'-t', '--tools',
nargs='+',
help="Execute specific tools"
)
parser.add_argument(
'-o', '--output',
help="Output file for results"
)
parser.add_argument(
'-f', '--format',
choices=['text', 'json'],
default='text',
help="Output format"
)
parser.add_argument(
'-v', '--voice',
action='store_true',
help="Enable voice mode"
)
parser.add_argument(
'-w', '--workspace',
default="/Users/walidsobhi/.openclaw/workspace",
help="Workspace path"
)
# Evaluation options
parser.add_argument(
'-e', '--eval',
choices=['mbpp', 'human_eval', 'gsm8k', 'all'],
help="Run evaluation benchmark"
)
parser.add_argument(
'--eval-provider',
default='ollama',
choices=['ollama', 'openai', 'anthropic', 'together'],
help="Model provider for evaluation"
)
parser.add_argument(
'--eval-model',
type=str,
help="Model name for evaluation"
)
# Pattern management
parser.add_argument(
'--patterns',
choices=['list', 'stats', 'clear'],
help="Manage learned patterns"
)
# Training
parser.add_argument(
'--train',
action='store_true',
help="Run LoRA training"
)
args = parser.parse_args()
try:
# Create CLI with custom workspace if provided
cli = StackCLI()
# Handle evaluation
if args.eval:
cli.run_eval(args.eval, args.eval_provider, args.eval_model)
return
# Handle pattern management
if args.patterns:
cli.run_patterns(args.patterns)
return
# Handle training
if args.train:
cli.run_train()
return
if args.voice:
cli.run_voice()
elif args.tools:
cli.run_tools(args.tools, args.output)
elif args.command:
exit(0 if cli.run_command(args.command, args.output, args.format) else 1)
else:
# Interactive mode
cli.run_interactive()
except KeyboardInterrupt:
print("\nGoodbye!")
sys.exit(0)
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()