@@ -576,201 +576,22 @@ def _feedback_destination(soul: PythinkerSoul) -> tuple[str, dict[str, str]] | N
576576 return f"{ pythinker_platform .base_url .rstrip ('/' )} /feedback" , headers
577577
578578
579- def _feedback_github_config (soul : PythinkerSoul ) -> tuple [str , str ] | None :
580- """Return GitHub OAuth client_id and repo when direct user-owned issues are enabled."""
581- import os
582-
583- feedback_config = soul .runtime .config .feedback
584- client_id = os .getenv ("PYTHINKER_FEEDBACK_GITHUB_CLIENT_ID" , "" ).strip ()
585- if not client_id :
586- client_id = feedback_config .github_client_id .strip ()
587- repo = os .getenv ("PYTHINKER_FEEDBACK_GITHUB_REPO" , "" ).strip ()
588- if not repo :
589- repo = feedback_config .github_repo .strip ()
590- if not client_id or not repo :
591- return None
592- return client_id , repo
593-
594-
595- def _feedback_issue_title (payload : dict [str , str | None ]) -> str :
596- version = f" { payload ['version' ]} " if payload .get ("version" ) else ""
597- session = payload .get ("session_id" ) or ""
598- suffix = f" ({ session [:8 ]} )" if session else ""
599- return f"[Pythinker CLI] Feedback{ version } { suffix } "
600-
601-
602- def _feedback_issue_body (payload : dict [str , str | None ]) -> str :
603- return "\n " .join (
604- [
605- "## User submission" ,
606- "" ,
607- payload .get ("content" ) or "_(no comment)_" ,
608- "" ,
609- "## Context" ,
610- "" ,
611- "- Type: feedback" ,
612- f"- Session: { payload .get ('session_id' ) or 'unknown' } " ,
613- f"- Version: { payload .get ('version' ) or 'unknown' } " ,
614- f"- OS: { payload .get ('os' ) or 'unknown' } " ,
615- f"- Model: { payload .get ('model' ) or 'unknown' } " ,
616- ]
617- )
618-
619-
620579@registry .command
621580@shell_mode_registry .command
622- async def feedback (app : Shell , args : str ):
623- """Submit feedback to make Pythinker CLI better"""
624- import platform
581+ def feedback (app : Shell , args : str ):
582+ """Open a GitHub issue to submit feedback or report a bug"""
625583 import webbrowser
626584
627- import aiohttp
628-
629- from pythinker_code .constant import VERSION
630- from pythinker_code .ui .shell .oauth import current_model_key
631585 from pythinker_code .ui .theme import get_tui_tokens as _get_tok_fb
632- from pythinker_code .utils .aiohttp import new_client_session
633586
634587 _t_fb = _get_tok_fb ()
635588
636- ISSUE_URL = "https://github.com/TechMatrix-labs/pythinker-code/issues"
637-
638- def _fallback_to_issues ():
639- if not webbrowser .open (ISSUE_URL ):
640- console .print (f"Please submit feedback at [underline]{ ISSUE_URL } [/underline]." )
641-
642- soul = ensure_pythinker_soul (app )
643- if soul is None :
644- _fallback_to_issues ()
645- return
646-
647- github_config = _feedback_github_config (soul )
648- destination = None if github_config is not None else _feedback_destination (soul )
649- if github_config is None and destination is None :
650- _fallback_to_issues ()
651- return
652-
653- from prompt_toolkit import PromptSession
654-
655- prompt_session : PromptSession [str ] = PromptSession ()
656- try :
657- content = await prompt_session .prompt_async ("Enter your feedback: " )
658- except (EOFError , KeyboardInterrupt ):
659- console .print (f"[{ _t_fb .muted } ]Feedback cancelled.[/]" )
660- return
661-
662- content = content .strip ()
663- if not content :
664- console .print (f"[{ _t_fb .warning } ]Feedback cannot be empty.[/]" )
665- return
666-
667- payload = {
668- "session_id" : soul .runtime .session .id ,
669- "content" : content ,
670- "version" : VERSION ,
671- "os" : f"{ platform .system ()} { platform .release ()} " ,
672- "model" : current_model_key (soul ),
673- }
674-
675- if github_config is not None :
676- client_id , repo = github_config
677- from pythinker_code .auth .github_feedback import (
678- GitHubFeedbackError ,
679- create_github_issue ,
680- load_github_feedback_token ,
681- login_github_feedback ,
682- star_github_repo ,
683- )
684-
685- try :
686- token = load_github_feedback_token ()
687- if token is None :
688- console .print (f"[{ _t_fb .info } ]GitHub login required to create the issue as you.[/]" )
689- async for event in login_github_feedback (client_id ):
690- if event .type == "waiting" :
691- console .print (event .message , markup = False )
692- elif event .type in {"verification_url" , "success" , "error" }:
693- from rich .style import Style as _RichStyleFb
694-
695- _style_fb = None
696- if event .type == "success" :
697- _style_fb = _RichStyleFb (color = _t_fb .success )
698- elif event .type == "error" :
699- _style_fb = _RichStyleFb (color = _t_fb .error )
700- console .print (event .message , markup = False , style = _style_fb )
701- token = load_github_feedback_token ()
702- if token is None :
703- console .print (f"[{ _t_fb .error } ]GitHub login did not produce a usable token.[/]" )
704- return
705- with console .status (f"[{ _t_fb .info } ]Creating GitHub issue...[/]" ):
706- issue = await create_github_issue (
707- repo ,
708- token ,
709- title = _feedback_issue_title (payload ),
710- body = _feedback_issue_body (payload ),
711- )
712- from pythinker_code .telemetry import track
713-
714- track ("feedback_submitted" , destination = "github" )
715- if issue .html_url :
716- issue_url = _rich_escape (issue .html_url )
717- console .print (f"[{ _t_fb .success } ]GitHub issue created:[/] { issue_url } " )
718- else :
719- console .print (f"[{ _t_fb .success } ]GitHub issue created.[/]" )
720-
721- try :
722- star_answer = await prompt_session .prompt_async (
723- "Do you like Pythinker CLI? Star the GitHub repo? [y/N]: "
724- )
725- except (EOFError , KeyboardInterrupt ):
726- star_answer = ""
727- if star_answer .strip ().lower () in {"y" , "yes" }:
728- try :
729- with console .status (f"[{ _t_fb .info } ]Starring GitHub repo...[/]" ):
730- await star_github_repo (repo , token )
731- track ("github_repo_starred" )
732- console .print (f"[{ _t_fb .success } ]Thanks for starring the repo![/]" )
733- except (GitHubFeedbackError , TimeoutError , aiohttp .ClientError ) as e :
734- console .print (f"[{ _t_fb .warning } ]Could not star the repo: { _rich_escape (e )} [/]" )
735- except (GitHubFeedbackError , TimeoutError , aiohttp .ClientError ) as e :
736- console .print (f"[{ _t_fb .error } ]Failed to create GitHub issue: { _rich_escape (e )} [/]" )
737- _fallback_to_issues ()
738- return
739-
740- assert destination is not None
741- feedback_url , headers = destination
742-
743- with console .status (f"[{ _t_fb .info } ]Submitting feedback...[/]" ):
744- try :
745- async with (
746- new_client_session () as session ,
747- session .post (
748- feedback_url ,
749- json = payload ,
750- headers = headers ,
751- raise_for_status = True ,
752- ),
753- ):
754- pass
755- session_id = soul .runtime .session .id
756- from pythinker_code .telemetry import track
589+ ISSUE_URL = "https://github.com/TechMatrix-labs/pythinker-code/issues/new/choose"
757590
758- track ("feedback_submitted" )
759- console .print (
760- f"[{ _t_fb .success } ]Feedback submitted, thank you! "
761- f"Your session ID is: { session_id } [/]"
762- )
763- except TimeoutError :
764- console .print (f"[{ _t_fb .error } ]Feedback submission timed out.[/]" )
765- _fallback_to_issues ()
766- except aiohttp .ClientError as e :
767- status = getattr (e , "status" , None )
768- if status :
769- msg = f"Failed to submit feedback (HTTP { status } )."
770- else :
771- msg = "Network error, failed to submit feedback."
772- console .print (f"[{ _t_fb .error } ]{ msg } [/]" )
773- _fallback_to_issues ()
591+ if webbrowser .open (ISSUE_URL ):
592+ console .print (f"[{ _t_fb .success } ]Opening GitHub issues in your browser...[/]" )
593+ else :
594+ console .print (f"Please open: [underline]{ ISSUE_URL } [/underline]" )
774595
775596
776597@registry .command (aliases = ["report-error" , "report" ])
0 commit comments