Skip to content

Commit aff95b8

Browse files
committed
feat(feedback): open GitHub new-issue page directly in browser
Replace the terminal-prompt + OAuth/HTTP-POST flow with a single webbrowser.open() call to the GitHub new-issue chooser URL. Users no longer need to type feedback in the terminal; /feedback opens the issue form in the browser immediately. Remove dead helpers (_feedback_github_config, _feedback_issue_title, _feedback_issue_body) and update tests to match the new behavior.
1 parent eb5af14 commit aff95b8

2 files changed

Lines changed: 30 additions & 555 deletions

File tree

src/pythinker_code/ui/shell/slash.py

Lines changed: 7 additions & 186 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)