Files
webber/webber-api/src/cli/commands/chat.py
T
jpmschweitzerandClaude Opus 4.5 3b58fa4f8b
Build and Push API / release (push) Successful in 3s
Build and Push API / build (push) Successful in 2m27s
refactor: reorganize into monorepo with separate subprojects
Structure webber into three independent subprojects:
- webber-api/: FastAPI backend server with all agent code
- webber-cli/: Standalone CLI client (renamed from cli/ to webber_cli/)
- webber-sandbox/: Test project for functional testing

Key changes:
- Each subproject has its own .venv (Python 3.12+)
- Added sandbox.sh for managing test project templates
- Created sandbox-templates/ with calculator-cli and empty starter
- Updated CI/CD for prefixed tags (api/v*, cli/v*)
- Added comprehensive AGENTS.md with operational instructions
- Added gitignore filtering to glob and grep tools
- Created pyproject.toml for each subproject

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 10:37:47 +01:00

129 lines
3.6 KiB
Python

"""
Chat command - interactive conversation mode.
"""
import asyncio
from pathlib import Path
import typer
from src.cli.theme import get_theme
from src.cli.ui.console import get_console
from src.cli.session.loop import AgenticLoop
from src.shared.logging import setup_logging
console = get_console()
def chat_command(
directory: str = typer.Option(
".",
"--directory",
"-d",
help="Working directory to explore",
),
verbose: bool = typer.Option(
False,
"--verbose",
"-V",
help="Show detailed output and debug logging",
),
) -> None:
"""
Start interactive chat session.
Enters a conversation loop where you can ask questions about the codebase.
The explore agent will search files, read code, and answer questions.
Examples:
webber chat
webber chat -d ./src
webber chat --verbose
"""
# Set up logging
log_level = "DEBUG" if verbose else "WARNING"
setup_logging(log_level)
# Resolve directory
working_dir = str(Path(directory).resolve())
if not Path(working_dir).exists():
console.print(f"[error]Error:[/] Directory not found: {working_dir}")
raise typer.Exit(1)
# Run the async chat loop
try:
asyncio.run(_chat_loop(working_dir, verbose))
except KeyboardInterrupt:
console.print("\n[dim]Goodbye![/]")
async def _chat_loop(working_dir: str, verbose: bool) -> None:
"""Async chat loop implementation."""
from src.domains.agents.explore import explore_agent
# Create the agentic loop
loop = AgenticLoop(
agent=explore_agent,
console=console,
working_dir=working_dir,
)
# Display welcome
loop.display_welcome()
# Main conversation loop
while True:
try:
# Get user input
user_input = console.input("[prompt]>[/] ").strip()
# Handle special commands
if not user_input:
continue
if user_input.lower() in ("exit", "quit", "/exit", "/quit"):
console.print("[dim]Goodbye![/]")
break
if user_input.lower() in ("clear", "/clear"):
loop.state.clear_history()
console.print("[info]History cleared.[/]")
continue
if user_input.lower() in ("status", "/status"):
loop.display_status()
continue
if user_input.lower().startswith("cd "):
new_dir = user_input[3:].strip()
new_path = Path(new_dir).resolve()
if new_path.exists() and new_path.is_dir():
loop.set_working_dir(str(new_path))
else:
console.print(f"[error]Directory not found:[/] {new_dir}")
continue
# Process with agent
theme = get_theme()
with console.status("[info]Thinking...[/]", spinner=theme.spinner):
response = await loop.run_turn(user_input)
# Display response
console.print()
loop.display_response(response)
console.print()
except KeyboardInterrupt:
console.print("\n[dim]Use 'exit' to quit or press Ctrl+C again.[/]")
try:
# Wait briefly for second Ctrl+C
await asyncio.sleep(0.5)
except KeyboardInterrupt:
console.print("\n[dim]Goodbye![/]")
break
except Exception as e:
console.print(f"[error]Error:[/] {e}")
if verbose:
console.print_exception()