feat(cli): add openkb feedback to file a prefilled GitHub issue#53
Conversation
Submitting feedback from a CLI tool with no backend is awkward — auto-creating issues requires a maintainer-owned token (security nightmare to ship in source), running an OpenKB-owned API server is overkill for an OSS CLI, and asking users to authenticate with their own gh CLI excludes anyone who hasn't installed it. Workaround: build a GitHub issue URL with title / body / labels prefilled in query params, and open the user's browser. The user goes through GitHub's normal flow with their own account — no backend, no secrets, no auth dance. Usage: openkb feedback # interactive (stdin) openkb feedback "openkb add hangs" # one-liner openkb feedback --type bug "..." # tags 'bug' label openkb feedback --print-url "..." # SSH / no-browser env openkb feedback --no-diagnostics "..." # skip env info The auto-collected diagnostics are deliberately minimal — openkb version, Python version, OS, whether a KB is initialised in cwd. No paths, no env vars, no API keys. Users can disable with --no-diagnostics if even that's too much. Implementation is ~90 lines in cli.py plus 17 regression tests covering URL shape, type→label mapping, title truncation, the --no-diagnostics body shape, and the webbrowser-not-called contract for --print-url.
| from unittest.mock import patch | ||
| from urllib.parse import parse_qs, urlparse | ||
|
|
||
| import pytest |
Code reviewFound 3 issues.
Generated with Claude Code. |
1. **webbrowser.open return value silently dropped on headless boxes** The command printed "Opening GitHub in your browser..." and exited 0 even when webbrowser.open returned False (no GUI, no $BROWSER, CI runner, container without DISPLAY). Now we capture the return value, surface "no browser available — copy the URL above" to stderr, and only print "Opened GitHub in your browser." after a confirmed True return. 2. **_openkb_version was the third copy of the same logic** openkb/__init__.py exports __version__ via importlib.metadata with fallback "0.0.0+unknown"; openkb/agent/chat.py wraps it as _openkb_version(); cli.py was re-implementing it with fallback "unknown" — already drifted. Replaced with the same one-liner used in chat.py: `from openkb import __version__; return __version__`. Now all three call sites agree. 3. **type prompt hung in non-TTY contexts (regression of PR #48)** PR #48 introduced _stdin_is_tty() specifically because adding a prompt without a TTY check broke automation pipelines. PR #53's second prompt (asking for feedback type) repeated the same pattern. Now it skips the prompt when stdin isn't a TTY and falls through to `--type other`. Pipes / CI work without flags now: echo "..." | openkb feedback "msg" # works, type=other, no label 4 new regression tests: - test_feedback_skips_type_prompt_when_stdin_is_not_a_tty - test_feedback_warns_when_webbrowser_open_returns_false - test_feedback_confirms_when_webbrowser_open_succeeds - test_openkb_version_helper_matches_package_version 331 tests pass (327 prior + 4 new).
The two flags added complexity without changing the day-one user experience: - --print-url: URL is already printed before webbrowser.open is attempted (the "Copy this URL..." line), so SSH/sandbox users can copy it from the terminal regardless of whether the auto-open succeeds. The flag was redundant. - --no-diagnostics: the diagnostics block is intentionally tiny (openkb version, Python, OS, kb_initialised yes/no) — no paths, usernames, env vars, or keys. Maintainers benefit from having it on every issue without exception, and the privacy cost is negligible. Surface shrinks to one flag (--type) plus the positional message. Help text and README updated. 19 feedback tests still pass (329 total).
Summary
Adds
openkb feedback— opens a prefilled GitHub issue in the user's browser. No backend, no maintainer-owned token, noghCLI dependency, one flag.The auto-attached Diagnostics block is intentionally tiny —
openkbversion, Python version, OS, and whether a KB is initialised in cwd. No paths, env vars, or keys.Why this shape
ghCLI authgh auth login; also skips the natural "review before Submit" step that a browser flow givesPrefilled URL → browser is the standard pattern for "file a bug" CLI commands (rustup, cargo, etc.). GitHub renders the issue form with our title / body / label, the user reviews and clicks Submit with their own account.
Behaviour details
--typeif not given on the command line, then opens browser. URL is also printed for copy-fallback if the browser can't auto-open (headless box, no$BROWSER).other. Browser open may fail silently — the printed URL is the fallback.webbrowser.openreturns False: surfaced via stderr (no browser available — copy the URL above), exits 0. Tested via mock.Files
openkb/cli.py—feedbackcommand + 3 helpers (_openkb_version,_collect_feedback_diagnostics,_build_feedback_url)tests/test_feedback.py— 19 regression tests: URL shape, type → label mapping, title truncation/prefix rules, empty-dict diagnostics shape, thewebbrowser.open=Falseheadless path, the non-TTY--typeskip path, version-helper consistency across cli/chat/__init__README.md— one row in the Commands tableSelf-review fixes applied during PR
After the initial commit I ran the code-review skill against my own PR and addressed 3 findings:
webbrowser.openreturn value was discarded — silent success on headless boxes. Now captured and reported._openkb_versionduplicated logic in 3 places with drifted fallback strings — collapsed to onefrom openkb import __version__; return __version__that matchesopenkb/agent/chat.py._stdin_is_tty()helper to default tootherin pipes / CI.Then per discussion the original
--print-urland--no-diagnosticsflags were removed:--print-urlwas redundant — the URL is always printed for copy-fallback beforewebbrowser.openis attempted.--no-diagnosticswas an over-correction — the diagnostics block is small enough and useful enough for maintainers that there's no real privacy reason to opt out.Final surface: one flag (
--type) plus the positional message argument.Test plan
openkb feedback --type bug "msg"opens browser with title/body/label correct (verified by mock + manual run)echo "" | openkb feedback "X"doesn't hang on the type prompt; URL is correct (manual run)cli._openkb_version(),chat._openkb_version(), and__init__.__version__all return the same string (asserted intest_openkb_version_helper_matches_package_version)