fix(ralph): "Planifică" deschide modal/ForceReply când descrierea lipsește
Înainte: click pe 🧠 Planifică (Discord/Telegram) sau /plan <slug> fără descriere pe un proiect din workspace fără entry în approved-tasks.json → mesaj eroare "Adaugă mai întâi cu /p <slug> <descriere>" și user-ul trebuia să facă două operații. Acum: - Discord button "Planifică" cu descriere goală → deschide RalphPlanModal cu TextInput pentru descriere; on_submit pornește direct start_planning_session - Discord /plan <slug> fără description param și fără entry → același modal (response.send_modal ÎNAINTE de defer — Discord constraint) - Telegram callback "Planifică" cu descriere goală → set state STEP_INPUT_DESCRIPTION_THEN_PLAN + ForceReply; handle_message detectează step și pornește planning cu textul user-ului - ralph_flow.py: nou STEP_INPUT_DESCRIPTION_THEN_PLAN (alături de cel existent pentru propose-only) start_planning_session deja auto-creează entry în approved-tasks.json dacă proiectul lipsește, deci flow-ul e end-to-end: workspace → click → descriere → planning agent activ. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -684,12 +684,17 @@ async def callback_ralph(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
|
||||
description = p.get("description") or ""
|
||||
break
|
||||
if not description:
|
||||
# No description yet — set state and prompt with ForceReply.
|
||||
# Next message in this chat will start the planning session.
|
||||
ralph_flow.set_state(
|
||||
ADAPTER_NAME, chat_id, user_id,
|
||||
step=ralph_flow.STEP_INPUT_DESCRIPTION_THEN_PLAN,
|
||||
project=slug,
|
||||
)
|
||||
await context.bot.send_message(
|
||||
chat_id=int(chat_id),
|
||||
text=(
|
||||
f"Nu am descriere pentru `{slug}`. "
|
||||
f"Adaugă mai întâi cu `/p {slug} <descriere>`."
|
||||
),
|
||||
text=f"📝 Descriere pentru *{slug}* — pornesc planning după ce trimiți:",
|
||||
reply_markup=ForceReply(selective=True),
|
||||
parse_mode="Markdown",
|
||||
)
|
||||
return
|
||||
@@ -915,16 +920,39 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
|
||||
chat_id, text[:100],
|
||||
)
|
||||
|
||||
# Ralph multi-step state: if user is replying to a "Descriere pentru X" prompt,
|
||||
# route this text to _ralph_propose instead of Claude.
|
||||
# Ralph multi-step state: route the next message based on the recorded step.
|
||||
state = ralph_flow.get_state(ADAPTER_NAME, str(chat_id), str(user_id))
|
||||
if state and state.get("step") == ralph_flow.STEP_INPUT_DESCRIPTION:
|
||||
if state:
|
||||
step = state.get("step")
|
||||
slug = state.get("project")
|
||||
if slug:
|
||||
if slug and step == ralph_flow.STEP_INPUT_DESCRIPTION:
|
||||
ralph_flow.clear_state(ADAPTER_NAME, str(chat_id), str(user_id))
|
||||
result = await asyncio.to_thread(_ralph_propose, slug, text)
|
||||
await message.reply_text(result)
|
||||
return
|
||||
if slug and step == ralph_flow.STEP_INPUT_DESCRIPTION_THEN_PLAN:
|
||||
ralph_flow.clear_state(ADAPTER_NAME, str(chat_id), str(user_id))
|
||||
await message.reply_text(
|
||||
f"🧠 Pornesc planning pentru *{slug}*… (durează ~60s)",
|
||||
parse_mode="Markdown",
|
||||
)
|
||||
try:
|
||||
first = await asyncio.to_thread(
|
||||
start_planning_session, slug, text, str(chat_id), ADAPTER_NAME,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.exception("start_planning_session failed for %s", slug)
|
||||
await message.reply_text(f"Planning blocat: {e}")
|
||||
return
|
||||
for chunk in split_planning_chunks(first):
|
||||
await context.bot.send_message(chat_id=chat_id, text=chunk)
|
||||
await context.bot.send_message(
|
||||
chat_id=chat_id,
|
||||
text="Răspunde aici. Apasă _Continuă faza_ când ești gata să trec la următoarea.",
|
||||
reply_markup=_build_planning_active_keyboard(),
|
||||
parse_mode="Markdown",
|
||||
)
|
||||
return
|
||||
|
||||
# Show typing indicator
|
||||
await context.bot.send_chat_action(chat_id=chat_id, action=ChatAction.TYPING)
|
||||
|
||||
Reference in New Issue
Block a user