feat(05-03): implement /next and /stop commands for round progression

Add /next to advance discussion with full context passed to models:
- Validates active discussion state
- Auto-completes when round limit reached
- Shows Round N/M progress indicator

Add /stop to end discussion early:
- Marks discussion as COMPLETED
- Clears session state
- Suggests /consensus for summarization

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Mikkel Georgsen 2026-01-16 19:45:12 +00:00
parent 104eceb246
commit 3ae08e9317
2 changed files with 107 additions and 1 deletions

View file

@ -7,7 +7,13 @@ project management, discussion commands, and export functionality.
from telegram.ext import Application, CommandHandler
from moai.bot.handlers.commands import help_command, start_command
from moai.bot.handlers.discussion import ask_command, discuss_command, open_command
from moai.bot.handlers.discussion import (
ask_command,
discuss_command,
next_command,
open_command,
stop_command,
)
from moai.bot.handlers.projects import project_command, projects_command
from moai.bot.handlers.status import status_command
@ -33,3 +39,5 @@ def register_handlers(app: Application) -> None:
app.add_handler(CommandHandler("ask", ask_command))
app.add_handler(CommandHandler("open", open_command))
app.add_handler(CommandHandler("discuss", discuss_command))
app.add_handler(CommandHandler("next", next_command))
app.add_handler(CommandHandler("stop", stop_command))

View file

@ -8,6 +8,7 @@ from moai.core.ai_client import MODEL_MAP, get_ai_client
from moai.core.models import DiscussionType, RoundType
from moai.core.orchestrator import query_models_parallel, run_discussion_round
from moai.core.services.discussion import (
complete_discussion,
create_discussion,
create_message,
create_round,
@ -245,3 +246,100 @@ async def discuss_command(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
except Exception as e:
await update.message.reply_text(f"Error: {e}")
async def next_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Handle /next command - advance to the next discussion round.
Requires an active discussion started with /discuss. Runs the next
sequential round with full context from prior rounds.
"""
# Check for active discussion state
state = context.user_data.get("discussion_state")
if state is None:
await update.message.reply_text("No active discussion. Start one with /open then /discuss.")
return
current_round = state["current_round"]
round_limit = state["round_limit"]
# Check if already at limit
if current_round > round_limit:
await update.message.reply_text(
f"Round limit ({round_limit}) reached. Start a new discussion with /open."
)
del context.user_data["discussion_state"]
return
# Show typing indicator
await update.message.chat.send_action("typing")
try:
# Load discussion with full context
discussion = await get_discussion(state["discussion_id"])
if discussion is None:
await update.message.reply_text("Discussion not found.")
del context.user_data["discussion_state"]
return
# Run the next round
responses = await run_discussion_round(
discussion=discussion,
models=state["models"],
project_name=state["project_name"],
round_number=current_round,
)
# Build response text
response_lines = [f"*Round {current_round}/{round_limit}:*\n"]
for model, response in responses.items():
response_lines.append(f"*{model.title()}:*\n{response}\n")
if current_round >= round_limit:
# Final round - complete discussion
await complete_discussion(state["discussion_id"])
response_lines.append(f"\n_Discussion complete ({round_limit} rounds)._")
del context.user_data["discussion_state"]
else:
response_lines.append(
f"\n_Round {current_round}/{round_limit} complete. Use /next or /stop._"
)
# Update state for next round
context.user_data["discussion_state"]["current_round"] = current_round + 1
await update.message.reply_text(
"\n".join(response_lines),
parse_mode="Markdown",
)
except Exception as e:
await update.message.reply_text(f"Error: {e}")
async def stop_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Handle /stop command - stop the current discussion early.
Completes the discussion at the current round and clears the session state.
"""
# Check for active discussion state
state = context.user_data.get("discussion_state")
if state is None:
await update.message.reply_text("No active discussion to stop.")
return
current_round = state["current_round"]
try:
# Complete the discussion in database
await complete_discussion(state["discussion_id"])
# Clear session state
del context.user_data["discussion_state"]
await update.message.reply_text(
f"_Discussion stopped at round {current_round - 1}. Use /consensus to summarize._",
parse_mode="Markdown",
)
except Exception as e:
await update.message.reply_text(f"Error: {e}")