File structured bug reports and feature requests to GitHub, with automatic redaction of secrets and deduplication.
A standalone Python package for any application to file issues to GitHub:
- ✅ Works standalone — just a GitHub token and repo
- ✅ Automatic secret redaction (API keys, tokens, home paths, etc.)
- ✅ Duplicate detection with fingerprinting
- ✅ Supports bug reports, feature requests, and general feedback
- ✅ Zero monorepo dependencies — pure Python + requests
- ✅ Fully typed, comprehensive tests
pip install awreportOr install from source:
git clone <repo-url>
cd awreport
pip install -e .from awreport import ReportClient
# Initialize client with your GitHub token and target repo
client = ReportClient(
token="ghp_...",
repo="owner/repo"
)
# Verify access
result = client.verify_access()
if result.status.value != "success":
print(f"Error: {result.message}")
exit(1)
# Report a bug
result = client.report_bug(
title="Login fails with special characters",
description="When using email+tag@example.com, login is rejected",
environment={"os": "Linux", "python": "3.10"},
stack_trace="Traceback...",
)
if result.is_success():
print(f"✓ Issue #{result.issue_number} filed")
print(f" {result.issue_url}")
elif result.was_duplicate:
print(f"✓ Duplicate of issue #{result.duplicate_issue_number}")
else:
print(f"✗ Error: {result.message}")
if result.error_details:
print(f" {result.error_details}")
# Report a feature request
feature_result = client.report_feature(
title="Add dark mode",
description="Users have requested a dark mode toggle"
)
# Send general feedback
feedback_result = client.report_feedback(
message="Great app! Love the new dashboard.",
tags=["praise", "dashboard"]
)# Verify access
awreport --token ghp_... --repo owner/repo verify
# Report a bug (with optional stack trace file)
awreport --token ghp_... --repo owner/repo bug \
"Login fails with special characters" \
"Cannot log in with email+tag@example.com" \
--env-json '{"os":"Linux","python":"3.10"}' \
--stack-trace-file /path/to/traceback.txt
# Report a feature request
awreport --token ghp_... --repo owner/repo feature \
"Add dark mode" \
"Users want a dark mode toggle"
# Send feedback
awreport --token ghp_... --repo owner/repo feedback \
"Great app! Love the UI." \
--tags praise,uiOr use environment variables:
export GITHUB_TOKEN="ghp_..."
export GITHUB_REPO="owner/repo"
awreport verify
awreport bug "Title" "Description"All feedback is automatically redacted before filing to GitHub. Secrets detected and redacted include:
- API Keys: Anthropic (
sk-ant-*), OpenAI (sk-*), AWS (AKIA*) - Tokens: GitHub PAT (
ghp_*), Slack (xoxb-*,xoxp-*), Bearer tokens - Paths: Home directory paths (
/home/user/,C:\Users\...) - High-entropy strings: Base64-like tokens >40 characters
- Other patterns:
api_key=,password=, etc.
Redaction is fail-closed: if redaction cannot complete safely, the report is NOT filed:
from awreport import RedactionError, ReportClient
client = ReportClient(token="...", repo="...")
try:
result = client.report_bug(title, description, environment, stack_trace)
except RedactionError:
print("Could not safely redact sensitive data. Report not filed.")
# Application should log this and possibly alert a humanFeedback is fingerprinted and compared against existing issues:
- Title normalization: lowercase, strip, remove extra whitespace
- Stack trace hashing: last 5 frames, SHA-256 hash (first 16 chars)
- Existing issue search: GitHub API search with filters
- Match logic:
- Bugs: must match title + stack trace hash (strong match)
- Features/feedback: title match alone is sufficient
If a duplicate is found:
- The existing issue number is returned in
ReportResult.duplicate_issue_number - A comment is added to the existing issue with new environment info
- Status is
ReportStatus.DUPLICATEbutis_success()still returnsTrue
result = client.report_bug(title, description)
if result.was_duplicate:
print(f"Already reported as issue #{result.duplicate_issue_number}")Every report returns a ReportResult with:
status:SUCCESS,DUPLICATE,ERROR, orREDACTION_FAILEDmessage: Human-readable status messageissue_number: GitHub issue number (if successful)issue_url: GitHub issue URL (if successful)was_duplicate:Trueif a duplicate was foundduplicate_issue_number: Number of duplicate issue (if applicable)error_details: Error details (ifstatus == ERROR)
result = client.report_bug(...)
if result.is_success():
# SUCCESS or DUPLICATE
print(f"Issue: {result.issue_url}")
else:
# ERROR or REDACTION_FAILED
print(f"Failed: {result.message}")
print(f"Details: {result.error_details}")All public functions are fully typed for IDE autocomplete:
from awreport import ReportClient, ReportResult, FeedbackKind
def handle_bug(client: ReportClient, title: str, desc: str) -> ReportResult:
return client.report_bug(title, desc)
# IDE provides full autocomplete and type checkingRun tests:
pip install -e ".[dev]"
pytest tests/Tests include:
- Redaction: secrets are redacted, normal text is preserved
- GitHub integration: API calls are mocked, various scenarios tested
- Deduplication: fingerprints match similar issues, exact duplicates detected
- CLI: argument parsing, error handling
All tests use positive assertions — they verify that successful operations produce the expected output:
def test_report_bug_creates_issue():
"""Test report_bug creates an issue."""
result = client.report_bug(title, description)
assert result.status == ReportStatus.SUCCESS
assert result.issue_number == 42
assert result.issue_url is not None- Standalone: No monorepo imports, no internal dependencies. Just GitHub token + repo.
- Fail-closed: If redaction fails, do NOT send. If verification fails, report it.
- Fully typed: Every public function has type hints. Mypy-clean.
- Comprehensive redaction: Better to over-redact than under-redact.
- Dedup prevents spam: Same issue = one thread with new comments, not N issues.
- Graceful degradation: Search failure doesn't block issue creation. Comment failure doesn't block duplicate detection.
GITHUB_TOKEN: GitHub personal access token (required if--tokennot provided)GITHUB_REPO: Repository in formatowner/repo(required if--reponot provided)
Install dev dependencies:
pip install -e ".[dev]"Run linting:
ruff check awreport/ tests/
mypy awreport/Format code:
black awreport/ tests/Apache-2.0
Contributions welcome! Please:
- Write tests for new functionality
- Run
pytestand linting before submitting - Keep redaction comprehensive — err on the side of over-redacting
- Maintain zero monorepo dependencies