-
Notifications
You must be signed in to change notification settings - Fork 53
feat: route onboarding flow through MCP-backed GitHub toolkit #137
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: route onboarding flow through MCP-backed GitHub toolkit #137
Conversation
WalkthroughImplements a comprehensive onboarding workflow for new Discord users, featuring multi-stage state machine logic (INTRO → AWAITING_CHOICE → ENCOURAGE_VERIFICATION → VERIFIED_CAPABILITIES → COMPLETED), integrating messaging, Discord views, agent node handlers, authentication callbacks, and configuration settings to welcome users, prompt GitHub verification, and advertise agent capabilities. Changes
Sequence DiagramsequenceDiagram
actor User as Discord User
participant Bot as Discord Bot
participant OnboardingCog
participant OnboardingWorkflow
participant AgentHandler
participant AuthCallback
participant Discord as Discord DM
User->>Bot: Joins Server
Bot->>OnboardingCog: on_member_join(member)
OnboardingCog->>OnboardingCog: get_or_create_user_by_discord()
alt User Verified
OnboardingCog->>OnboardingWorkflow: run_onboarding_flow(state, ..., is_verified=True)
OnboardingWorkflow->>OnboardingWorkflow: INTRO → VERIFIED_CAPABILITIES
else User Not Verified
OnboardingCog->>OnboardingWorkflow: run_onboarding_flow(state, ..., is_verified=False)
OnboardingWorkflow->>OnboardingWorkflow: INTRO → AWAITING_CHOICE
end
OnboardingWorkflow-->>OnboardingCog: OnboardingFlowResult + state_update
OnboardingCog->>OnboardingCog: _build_welcome_embed()
OnboardingCog->>Discord: Send DM with OnboardingView
alt User Clicks "Verify GitHub"
User->>Discord: Click verify button
Discord->>AuthCallback: POST /auth/github/callback?session=...
AuthCallback->>AuthCallback: Verify GitHub OAuth
AuthCallback->>Discord: send_final_handoff_dm(user)
Discord->>User: Final capabilities embed
else User Clicks "Skip"
User->>Discord: Click skip button
Discord->>Discord: send_final_handoff_dm(user)
Discord->>User: Final capabilities embed
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Rationale: This PR introduces a multi-stage onboarding state machine with dense logic, non-trivial state transitions, and verification/routing conditions. Changes span ten files across agent nodes, Discord integration, authentication, and state management—heterogeneous in nature, requiring separate reasoning for workflow orchestration, state flow synchronization, Discord UX interactions, and auth callback modifications. The interconnected dependencies between workflow stages, node handlers, and Discord views demand careful validation of state consistency and control flow correctness. Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (2)
backend/app/agents/devrel/github/tools/general_github_help.py (1)
11-12
: Update docstring to document the hint parameter.The function signature correctly adds the optional
hint
parameter, but the docstring should be updated to explain its purpose and behavior.Apply this diff to enhance the docstring:
async def handle_general_github_help(query: str, llm, hint: Optional[str] = None) -> Dict[str, Any]: - """Execute general GitHub help with web search and LLM knowledge""" + """ + Execute general GitHub help with web search and LLM knowledge. + + Args: + query: The user's GitHub-related question + llm: The language model to use for generating responses + hint: Optional assistant hint to augment the prompt context + + Returns: + Dict containing status, response, and metadata + """backend/app/agents/devrel/onboarding/messages.py (1)
65-74
: Consider sanitizing the github_username parameter for markdown safety.The
github_username
parameter is wrapped in backticks for markdown formatting (lines 69, 92) without validation. If a username contains backticks or other markdown special characters, it could break the formatting or potentially cause injection issues in the rendered message.Consider adding a helper function to sanitize usernames before embedding them in formatted strings:
def _sanitize_username(username: str) -> str: """Escape markdown special characters in username.""" # Escape backticks and other markdown characters return username.replace("`", "\\`").replace("*", "\\*").replace("_", "\\_")Then use it when building messages:
def build_verified_welcome(github_username: Optional[str] = None) -> str: """Welcome copy for returning verified contributors.""" greeting = "👋 Welcome back to the Devr.AI community!" if github_username: - greeting += f" I see `{github_username}` is already linked, which is great." + greeting += f" I see `{_sanitize_username(github_username)}` is already linked, which is great."Also applies to: 88-98
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
backend/app/agents/devrel/github/tools/general_github_help.py
(3 hunks)backend/app/agents/devrel/nodes/handlers/onboarding.py
(1 hunks)backend/app/agents/devrel/nodes/react_supervisor.py
(1 hunks)backend/app/agents/devrel/onboarding/messages.py
(1 hunks)backend/app/agents/devrel/onboarding/workflow.py
(1 hunks)backend/app/agents/devrel/tool_wrappers.py
(1 hunks)backend/app/agents/state.py
(1 hunks)backend/app/api/v1/auth.py
(5 hunks)backend/app/core/config/settings.py
(1 hunks)backend/app/integrations/mcp/client.py
(1 hunks)backend/integrations/discord/cogs.py
(2 hunks)backend/integrations/discord/views.py
(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (7)
backend/app/agents/devrel/tool_wrappers.py (1)
backend/app/agents/devrel/nodes/react_supervisor.py (1)
add_tool_result
(98-114)
backend/app/agents/devrel/github/tools/general_github_help.py (1)
backend/app/services/embedding_service/service.py (1)
llm
(55-68)
backend/integrations/discord/cogs.py (5)
backend/app/agents/devrel/onboarding/messages.py (4)
build_encourage_verification_message
(77-85)build_new_user_welcome
(51-62)build_verified_capabilities_intro
(88-98)build_verified_welcome
(65-74)backend/app/services/auth/management.py (1)
get_or_create_user_by_discord
(10-41)backend/app/services/auth/supabase.py (1)
login_with_github
(28-30)backend/app/services/auth/verification.py (1)
create_verification_session
(33-61)backend/integrations/discord/views.py (3)
OAuthView
(36-49)OnboardingView
(52-120)build_final_handoff_embed
(11-24)
backend/app/agents/devrel/onboarding/workflow.py (2)
backend/app/agents/state.py (1)
AgentState
(18-76)backend/app/agents/devrel/onboarding/messages.py (5)
build_verified_capabilities_intro
(88-98)render_capabilities_text
(39-48)build_new_user_welcome
(51-62)build_encourage_verification_message
(77-85)build_verified_welcome
(65-74)
backend/integrations/discord/views.py (2)
backend/app/services/auth/management.py (1)
get_or_create_user_by_discord
(10-41)backend/app/models/database/supabase.py (1)
User
(7-69)
backend/app/api/v1/auth.py (6)
backend/app/database/supabase/client.py (1)
get_supabase_client
(9-13)backend/app/services/auth/verification.py (2)
find_user_by_session_and_verify
(63-135)get_verification_session_info
(156-176)backend/app/services/github/user/profiling.py (2)
profile_user_from_github
(301-333)request
(77-79)backend/app/core/dependencies.py (1)
get_app_instance
(7-12)backend/integrations/discord/views.py (1)
send_final_handoff_dm
(27-34)backend/main.py (1)
DevRAIApplication
(26-87)
backend/app/agents/devrel/nodes/handlers/onboarding.py (2)
backend/app/agents/devrel/onboarding/workflow.py (2)
OnboardingStage
(12-19)run_onboarding_flow
(87-276)backend/app/agents/state.py (1)
AgentState
(18-76)
🪛 Ruff (0.13.3)
backend/integrations/discord/cogs.py
228-228: String contains ambiguous ’
(RIGHT SINGLE QUOTATION MARK). Did you mean ``` (GRAVE ACCENT)?
(RUF001)
264-265: try
-except
-pass
detected, consider logging the exception
(S110)
264-264: Do not catch blind exception: Exception
(BLE001)
280-281: try
-except
-pass
detected, consider logging the exception
(S110)
280-280: Do not catch blind exception: Exception
(BLE001)
293-294: try
-except
-pass
detected, consider logging the exception
(S110)
293-293: Do not catch blind exception: Exception
(BLE001)
300-300: Consider moving this statement to an else
block
(TRY300)
303-303: Do not catch blind exception: Exception
(BLE001)
304-304: Use logging.exception
instead of logging.error
Replace with exception
(TRY400)
308-309: try
-except
-pass
detected, consider logging the exception
(S110)
308-308: Do not catch blind exception: Exception
(BLE001)
backend/app/integrations/mcp/client.py
8-8: Do not catch blind exception: Exception
(BLE001)
31-31: Avoid specifying long messages outside the exception class
(TRY003)
43-43: Avoid specifying long messages outside the exception class
(TRY003)
47-47: Within an except
clause, raise exceptions with raise ... from err
or raise ... from None
to distinguish them from errors in exception handling
(B904)
47-47: Avoid specifying long messages outside the exception class
(TRY003)
50-50: Within an except
clause, raise exceptions with raise ... from err
or raise ... from None
to distinguish them from errors in exception handling
(B904)
50-50: Avoid specifying long messages outside the exception class
(TRY003)
50-50: Use explicit conversion flag
Replace with conversion flag
(RUF010)
backend/integrations/discord/views.py
32-34: try
-except
-pass
detected, consider logging the exception
(S110)
32-32: Do not catch blind exception: Exception
(BLE001)
73-73: Unused method argument: button
(ARG002)
84-84: Do not catch blind exception: Exception
(BLE001)
102-103: try
-except
-pass
detected, consider logging the exception
(S110)
102-102: Do not catch blind exception: Exception
(BLE001)
111-111: Unused method argument: button
(ARG002)
118-120: try
-except
-pass
detected, consider logging the exception
(S110)
118-118: Do not catch blind exception: Exception
(BLE001)
backend/app/api/v1/auth.py
21-21: Unused function argument: request
(ARG001)
24-24: Do not perform function call Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
98-98: Do not catch blind exception: Exception
(BLE001)
backend/app/agents/devrel/nodes/handlers/onboarding.py
31-32: try
-except
-pass
detected, consider logging the exception
(S110)
31-31: Do not catch blind exception: Exception
(BLE001)
🔇 Additional comments (8)
backend/app/agents/devrel/github/tools/general_github_help.py (1)
1-1
: LGTM!The
Optional
import is correctly added to support the newhint
parameter's type annotation.backend/app/agents/state.py (1)
29-31
: Onboarding state persistence looks good.Defaulting to an empty dict matches the rest of the state model and cleanly unlocks the new workflow.
backend/app/core/config/settings.py (1)
42-48
: Configuration surface extension is consistent.The new fields follow existing conventions, so wiring them through env vars should be seamless.
backend/app/agents/devrel/tool_wrappers.py (1)
33-44
: Onboarding wrapper propagation looks solid.State updates now carry onboarding_state and force the next tool when present—nice alignment with the supervisor shortcut.
backend/app/agents/devrel/nodes/react_supervisor.py (1)
20-37
: Force-next-tool shortcut is clean.The auto-route path removes the flag, records the decision, and bumps the iteration counter—exactly what the flow needs.
backend/app/agents/devrel/onboarding/messages.py (3)
39-48
: LGTM!The capability text rendering logic is clear and produces well-formatted output. The function correctly iterates through sections and formats examples with proper spacing.
12-12
: Verify repository name: ensure the hardcoded'Devr.AI-backend'
matches the actual GitHub repository’s name (including exact capitalization, punctuation, and hyphenation) to avoid confusion.
58-60
: Referenced Discord commands exist and are registered
All commands (/verify_github, /verification_status, /help) are defined in backend/integrations/discord/cogs.py.
if hint: | ||
help_prompt = GENERAL_GITHUB_HELP_PROMPT.format( | ||
query=f"{query}\n\nAssistant hint: {hint}", | ||
search_context=search_context | ||
) | ||
else: | ||
help_prompt = GENERAL_GITHUB_HELP_PROMPT.format( | ||
query=query, | ||
search_context=search_context | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Eliminate code duplication in prompt construction.
The GENERAL_GITHUB_HELP_PROMPT.format()
call is duplicated in both branches, violating the DRY principle. Construct the augmented query first, then format once.
Apply this diff to refactor the conditional logic:
- if hint:
- help_prompt = GENERAL_GITHUB_HELP_PROMPT.format(
- query=f"{query}\n\nAssistant hint: {hint}",
- search_context=search_context
- )
- else:
- help_prompt = GENERAL_GITHUB_HELP_PROMPT.format(
- query=query,
- search_context=search_context
- )
+ augmented_query = f"{query}\n\nAssistant hint: {hint}" if hint else query
+ help_prompt = GENERAL_GITHUB_HELP_PROMPT.format(
+ query=augmented_query,
+ search_context=search_context
+ )
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
if hint: | |
help_prompt = GENERAL_GITHUB_HELP_PROMPT.format( | |
query=f"{query}\n\nAssistant hint: {hint}", | |
search_context=search_context | |
) | |
else: | |
help_prompt = GENERAL_GITHUB_HELP_PROMPT.format( | |
query=query, | |
search_context=search_context | |
) | |
augmented_query = f"{query}\n\nAssistant hint: {hint}" if hint else query | |
help_prompt = GENERAL_GITHUB_HELP_PROMPT.format( | |
query=augmented_query, | |
search_context=search_context | |
) |
🤖 Prompt for AI Agents
In backend/app/agents/devrel/github/tools/general_github_help.py around lines 26
to 35, the GENERAL_GITHUB_HELP_PROMPT.format() call is duplicated; refactor by
first building an augmented query string (include the hint when present, e.g.,
augmented_query = f"{query}\n\nAssistant hint: {hint}" if hint else query) and
then call GENERAL_GITHUB_HELP_PROMPT.format(query=augmented_query,
search_context=search_context) once to eliminate duplication.
async with aiohttp.ClientSession() as session: | ||
try: | ||
async with session.post(self.server_url, headers=headers, json=payload, timeout=timeout) as resp: | ||
text = await resp.text() | ||
if resp.status >= 400: | ||
raise MCPClientError(f"MCP HTTP {resp.status}: {text}") | ||
try: | ||
data = json.loads(text) | ||
except json.JSONDecodeError: | ||
raise MCPClientError("MCP returned invalid JSON") | ||
return data | ||
except asyncio.TimeoutError as e: | ||
raise MCPClientError(f"MCP timeout: {str(e)}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrap aiohttp transport errors in MCPClientError
.
Right now any aiohttp.ClientError
(DNS failure, refused connection, etc.) bubbles up directly, so callers stop seeing the unified MCPClientError
your docstring implies. Please catch those and re-raise as MCPClientError
(ideally using raise … from e
) to keep error handling consistent.
Apply this diff:
async with aiohttp.ClientSession() as session:
try:
async with session.post(self.server_url, headers=headers, json=payload, timeout=timeout) as resp:
text = await resp.text()
if resp.status >= 400:
raise MCPClientError(f"MCP HTTP {resp.status}: {text}")
try:
data = json.loads(text)
except json.JSONDecodeError as e:
- raise MCPClientError("MCP returned invalid JSON")
+ raise MCPClientError("MCP returned invalid JSON") from e
return data
- except asyncio.TimeoutError as e:
- raise MCPClientError(f"MCP timeout: {str(e)}")
+ except asyncio.TimeoutError as e:
+ raise MCPClientError(f"MCP timeout: {e}") from e
+ except aiohttp.ClientError as e:
+ raise MCPClientError(f"MCP transport error: {e}") from e
Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Ruff (0.13.3)
43-43: Avoid specifying long messages outside the exception class
(TRY003)
47-47: Within an except
clause, raise exceptions with raise ... from err
or raise ... from None
to distinguish them from errors in exception handling
(B904)
47-47: Avoid specifying long messages outside the exception class
(TRY003)
50-50: Within an except
clause, raise exceptions with raise ... from err
or raise ... from None
to distinguish them from errors in exception handling
(B904)
50-50: Avoid specifying long messages outside the exception class
(TRY003)
50-50: Use explicit conversion flag
Replace with conversion flag
(RUF010)
🤖 Prompt for AI Agents
In backend/app/integrations/mcp/client.py around lines 38 to 50, transport-level
aiohttp.ClientError exceptions (DNS failures, connection refused, etc.) are not
being caught and thus escape as raw aiohttp errors; catch aiohttp.ClientError in
the existing try/except block (or add an except clause) and re-raise as an
MCPClientError using "raise MCPClientError(... ) from e" so callers always
receive MCPClientError while preserving the original error context.
it was committed by mistake ,i was experimenting with MCPs
Any updates @Dharya4242 ? |
Hey @smokeyScraper |
cool man, actually i was preferring to close all the current issues by working on them myself, but you can give it a try and align it as per your way and then can share your branch's access to me as a collaborator so that I can make commits on top of yours if I feel like a few changes over yours. This way you too would learn from your mistakes in a better way (if any) as I'd be correcting them out. Thanks! |
got it , sounds great. Really appreciate the opportunity to learn and your guidance |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (7)
backend/app/agents/devrel/nodes/gather_context.py (2)
28-46
: Discord avatar URL construction, PII logging, and exception scope
author['avatar']
is a hash, not a URL. Build the CDN URL when only a hash is available.- Redact
discord_id
in logs.- Consider narrowing the catch or documenting why a broad catch is acceptable (to address BLE001), and optionally throttle DB calls if profile is already fresh.
- avatar_url = author.get("avatar") or author.get("avatar_url") + avatar_hash = author.get("avatar") + avatar_url = author.get("avatar_url") + if not avatar_url and avatar_hash and discord_id: + # Discord CDN avatar URL format; default to 256px + avatar_url = f"https://cdn.discordapp.com/avatars/{discord_id}/{avatar_hash}.png?size=256" @@ - if discord_id: - try: + if discord_id: + try: user = await get_or_create_user_by_discord( @@ ) profile_data = user.model_dump() - except Exception as exc: # pragma: no cover - graceful degradation - logger.warning("Failed to refresh Discord user profile for %s: %s", discord_id, exc) + except Exception as exc: # noqa: BLE001 # Graceful degradation; service raises heterogeneous errors + redacted = str(discord_id) + if len(redacted) > 6: + redacted = f"{redacted[:2]}***{redacted[-2:]}" + logger.warning("Failed to refresh Discord user profile for id=%s: %s", redacted, exc)Optional (future): cache a
last_profile_refresh
timestamp instate.context
and skip refresh within N minutes to reduce DB load.
55-63
: Minor: timezones for last_interaction_time (optional)If/when the codebase moves to TZ-aware datetimes, switch to
datetime.now(tz=timezone.utc)
for consistency; current usage matches AgentState defaults.backend/app/agents/devrel/onboarding/workflow.py (4)
39-46
: Broaden intent detection to cover common phrasingsImprove coverage for “verify/verification/gh” and skip phrasings.
-_INTENT_VERIFIED = re.compile(r"\b(i\s*(have)?\s*)?(linked|connected|verified)\b.*github", re.IGNORECASE) +_INTENT_VERIFIED = re.compile(r"\b(i\s*(have|am)?\s*)?(verify|verified|link(?:ed)?|connect(?:ed)?)\b.*\b(github|gh)\b", re.IGNORECASE) -_INTENT_SKIP = re.compile(r"\b(skip|later|not\s+now)\b", re.IGNORECASE) +_INTENT_SKIP = re.compile(r"\b(skip|later|not\s+now|skip\s+for\s+now)\b", re.IGNORECASE) -_INTENT_HELP = re.compile(r"\b(how|help|can't|cannot|stuck)\b.*verify", re.IGNORECASE) +_INTENT_HELP = re.compile(r"\b(how|help|can.?t|cannot|stuck)\b.*verif(y|ication|y(ing)?)", re.IGNORECASE)
107-127
: DRY up verified-capabilities/completed branchesFour nearly identical blocks construct verified responses. Extract a small helper to build the result to reduce maintenance risk.
+def _verified_response(github_username: Optional[str], capability_sections) -> OnboardingFlowResult: + return OnboardingFlowResult( + stage=OnboardingStage.VERIFIED_CAPABILITIES, + status="completed", + welcome_message=messages.build_verified_capabilities_intro(github_username), + final_message=messages.render_capabilities_text(), + actions=_exploration_suggestions(), + is_verified=True, + capability_sections=capability_sections, + route_hint="onboarding", + handoff="github_toolkit", + next_tool="github_toolkit", + ) @@ - intro = messages.build_verified_capabilities_intro(github_username) - return ( - OnboardingFlowResult( - stage=OnboardingStage.VERIFIED_CAPABILITIES, - status="completed", - welcome_message=intro, - final_message=messages.render_capabilities_text(), - actions=_exploration_suggestions(), - is_verified=True, - capability_sections=capability_sections, - route_hint="onboarding", - handoff="github_toolkit", - next_tool="github_toolkit", - ), - onboarding_state, - ) + return (_verified_response(github_username, capability_sections), onboarding_state)Apply similarly to the other verified transitions.
Also applies to: 145-165, 204-224, 245-261
226-241
: Cap reminders to avoid unbounded growthConsider a
max_reminders
cap (e.g., 3) and stop incrementing, to prevent ever-growing counters in persisted state.- onboarding_state["reminders_sent"] = reminders_sent + 1 + max_reminders = 3 + onboarding_state["reminders_sent"] = min(reminders_sent + 1, max_reminders)
22-37
: Consider addingto_dict()
as defensive serialization, though current handler already convertsstage.value
Verification shows the handler at
backend/app/agents/devrel/nodes/handlers/onboarding.py:43
already convertsflow_result.stage.value
to string. The serialization concern is already addressed. Addingto_dict()
to the dataclass would be a defensive measure to prevent future bugs if the handler pattern changes or ifOnboardingFlowResult
is used elsewhere without explicit serialization. Current code is safe as-is.backend/app/agents/devrel/tool_wrappers.py (1)
33-46
: Gate complete_after_forced_tool to supported toolThe stage value is already serialized as a string (
.value
inhandle_onboarding_node
), so the type check is safe. However, scopecomplete_after_forced_tool
to the specific tool where it's cleared to avoid leaving stale routing flags. Only set it whennext_tool == "github_toolkit"
.next_tool = tool_result.get("next_tool") if next_tool: context["force_next_tool"] = next_tool - if tool_result.get("stage") in {"verified_capabilities", "completed"}: - context["complete_after_forced_tool"] = next_tool + if next_tool == "github_toolkit" and tool_result.get("stage") in {"verified_capabilities", "completed"}: + context["complete_after_forced_tool"] = next_tool
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
backend/app/agents/devrel/github/tools/general_github_help.py
(1 hunks)backend/app/agents/devrel/nodes/gather_context.py
(2 hunks)backend/app/agents/devrel/nodes/react_supervisor.py
(1 hunks)backend/app/agents/devrel/onboarding/workflow.py
(1 hunks)backend/app/agents/devrel/tool_wrappers.py
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- backend/app/agents/devrel/nodes/react_supervisor.py
- backend/app/agents/devrel/github/tools/general_github_help.py
🧰 Additional context used
🧬 Code graph analysis (3)
backend/app/agents/devrel/tool_wrappers.py (1)
backend/app/agents/devrel/nodes/react_supervisor.py (1)
add_tool_result
(114-130)
backend/app/agents/devrel/nodes/gather_context.py (2)
backend/app/agents/state.py (1)
AgentState
(18-76)backend/app/services/auth/management.py (1)
get_or_create_user_by_discord
(10-41)
backend/app/agents/devrel/onboarding/workflow.py (2)
backend/app/agents/state.py (1)
AgentState
(18-76)backend/app/agents/devrel/onboarding/messages.py (5)
build_verified_capabilities_intro
(88-98)render_capabilities_text
(39-48)build_new_user_welcome
(51-62)build_encourage_verification_message
(77-85)build_verified_welcome
(65-74)
🪛 Ruff (0.14.0)
backend/app/agents/devrel/nodes/gather_context.py
44-44: Do not catch blind exception: Exception
(BLE001)
🔇 Additional comments (3)
backend/app/agents/devrel/nodes/gather_context.py (2)
26-27
: Good: initialize profile_data from persisted stateKeeps prior profile fields when refresh fails.
48-48
: Context default for user_profile is sensibleFallback ensures downstream code always has minimal profile metadata.
backend/app/agents/devrel/tool_wrappers.py (1)
70-78
: Verify whether the additional clearing is necessary given existing supervisor-level logicThe script output shows
force_next_tool
is already being cleared inreact_supervisor.py:31
after the supervisor processes a forced action. The suggested addition intool_wrappers.py
would clear it earlier, but the existing mechanism in the supervisor node already handles removal.To confirm if the suggested refactor is necessary or if the current flow already prevents re-routing loops, verify:
- Whether the supervisor clears
force_next_tool
before re-evaluating on the next iteration (confirming the existing logic is sufficient), or- Whether there's a race condition where
force_next_tool
persists long enough to cause unwanted re-routing despite the completion flag being setWithout tracing the full state machine execution order between tool completion and supervisor re-evaluation, the necessity of the refactor is unclear.
Closes #116
📝 Description
Graph-based onboarding flow that branches on GitHub verification and routes exploration into the GitHub toolkit (MCP-backed). Centralizes onboarding copy, persists minimal onboarding state, and aligns Discord DM onboarding with the same messages and stages.
Goals:
github_toolkit
so MCP-powered repo/org queries answer user prompts.🔧 Changes Made
Onboarding state machine and copy
backend/app/agents/devrel/onboarding/messages.py
intro
,awaiting_choice
,encourage_verification
,verified_capabilities
,completed
):backend/app/agents/devrel/onboarding/workflow.py
next_tool
, andcapability_sections
:backend/app/agents/devrel/nodes/handlers/onboarding.py
State + routing integration
onboarding_state
inAgentState
:backend/app/agents/state.py
force_next_tool
:backend/app/agents/devrel/tool_wrappers.py
force_next_tool
to jump straight togithub_toolkit
:backend/app/agents/devrel/nodes/react_supervisor.py
Discord UX alignment
Reuse shared messages; verified users get capability intro + final handoff:
backend/integrations/discord/cogs.py
Capability embed mirrors shared sections; add “I’ve verified” button to re-check linkage:
backend/integrations/discord/views.py
Onboarding now hands off to
github_toolkit
, which integrates with the MCP-backed GitHub service viagithub_support
.🤝 Collaboration
Guidance from: @smokeyScraper
✅ Checklist
Summary by CodeRabbit
Release Notes
New Features
Chores