- Minimal required arguments.
- DB path comes from
GHBB_DBor./ghbb.db; no--dboption. - Backup owner/org is derived from JSON; no
--orgoption. - Human output by default.
--jsonoutput for agents.- Errors should be concise and actionable.
Use Typer + Rich.
Import a backup into the current database.
Examples:
ghbb import C:\CodeBlocks\ggml-org-backup\backup
ghbb import ../backup
ghbb importBehavior:
- Resolve DB via
GHBB_DBor./ghbb.db. - Resolve backup root as described in
implementation-plan.md. - Rebuild imported tables for MVP.
- Always import attachment metadata.
- Never import attachment file contents.
- Rebuild FTS after import.
Useful options:
--quiet suppress progress output
--json print import summary as JSON
Avoid extra options until needed.
Human summary example:
Imported C:\CodeBlocks\ggml-org-backup\backup into C:\CodeBlocks\github-backup-browser\ghbb.db
Repositories: 2
Items:
issues: 8434
pulls: 12028
discussions: 3314
releases: 5952
Comments: 123456
Attachments: 8647 metadata rows
Release assets: 12345
FTS docs: 98765
Warnings: 0
Counts above are illustrative except where documented in testing.md.
Print database stats.
Examples:
ghbb stats
ghbb stats --jsonHuman output should include:
- DB path;
- backup root from last import;
- last import status/time;
- repositories;
- item counts by repo and kind;
- comment counts by kind;
- attachment metadata rows;
- release assets;
- FTS docs.
JSON should have stable keys:
{
"db_path": "...",
"last_import": {
"backup_root": "...",
"started_at": "...",
"finished_at": "...",
"status": "ok"
},
"repositories": [
{
"full_name": "ggml-org/llama.cpp",
"name": "llama.cpp",
"items": {
"issue": 7903,
"pull": 11236,
"discussion": 3177,
"release": 5941
}
}
],
"totals": {
"repositories": 2,
"items": 29728,
"attachments": 8647
}
}Search the FTS index.
Examples:
ghbb search "Fabrice Bellard"
ghbb search cuda graph memory leak
ghbb search "KV cache" --repo llama.cpp
ghbb search "ggml_backend_cuda" --repo ggml-org/llama.cpp --kind issue --state open
ghbb search "Metal" --author ggerganov --limit 10
ghbb search "quantization" --label enhancement
ghbb search "Fabrice Bellard" --jsonArguments/options:
QUERY... one or more words; join with spaces
--repo TEXT repo name or full name; e.g. llama.cpp or ggml-org/llama.cpp
--kind TEXT issue, pull, discussion, release
--state TEXT open, closed, published, prerelease, draft
--author TEXT item author or hit/comment author
--label TEXT item label
--limit INT default 20
--offset INT default 0
--json agent-friendly JSON
Human output should be a Rich table:
score repo kind key state title hit
0.12 llama.cpp issue 12345 open CUDA graph memory leak ... comment by user123
0.19 ggml issue 1 closed Implement gpt2tc/nncp with ggml item body
After the table, print a short snippet for each result, or use a compact multiline layout if snippets make the table too wide.
Each result must provide enough information for follow-up:
ghbb show llama.cpp issue 12345JSON result shape:
{
"query": "Fabrice Bellard",
"limit": 20,
"offset": 0,
"results": [
{
"search_doc_id": 1,
"item_id": 1,
"comment_id": null,
"repo": "ggml-org/ggml",
"repo_name": "ggml",
"kind": "issue",
"item_key": "1",
"number": 1,
"tag_name": null,
"state": "closed",
"title": "Implement gpt2tc/nncp with ggml",
"doc_type": "item",
"hit_author": "...",
"url": "https://github.com/ggml-org/ggml/issues/1",
"snippet": "..."
}
]
}Show an item/thread.
Examples:
ghbb show ggml issue 1
ghbb show ggml-org/ggml issue 1
ghbb show llama.cpp pull 10001
ghbb show ggml discussion 32
ghbb show llama.cpp release b1046
ghbb show ggml issue 1 --jsonArguments:
REPO repo name or full name. If a short repo name is ambiguous, fail with choices.
KIND issue, pull, discussion, release
KEY issue/PR/discussion number or release tag
Human output should show:
- repo/kind/key;
- title;
- URL;
- state;
- labels;
- author;
- created/updated/closed/published dates;
- body;
- comments/reviews/replies in chronological/import order;
- attachment metadata;
- release assets for releases.
For long bodies/comments, do not truncate by default. Agents need complete content. Add --limit-comments or --no-body later if needed.
JSON output should include:
{
"item": {
"id": 1,
"repo": "ggml-org/ggml",
"repo_name": "ggml",
"kind": "issue",
"item_key": "1",
"number": 1,
"tag_name": null,
"title": "...",
"body": "...",
"body_text": "...",
"state": "closed",
"labels": ["enhancement"],
"author": "...",
"created_at": "...",
"updated_at": "...",
"url": "..."
},
"comments": [
{
"id": 10,
"kind": "issue_comment",
"author": "...",
"body": "...",
"created_at": "...",
"url": "..."
}
],
"attachments": [
{
"original_filename": "...png",
"content_type": "image/png",
"size_bytes": 7326,
"success": true,
"original_url": "https://github.com/user-attachments/assets/...",
"local_path": "repositories/ggml/issues/attachments/1010/...png"
}
],
"release_assets": []
}Optional but useful.
Examples:
ghbb repos
ghbb repos --jsonShows repos and counts by kind.
Small utility for agents and debugging.
ghbb db-pathPrints resolved DB path. This command can be hidden from README if desired, but it is useful during implementation.
Run the local web app.
Examples:
ghbb serve
ghbb serve --host 127.0.0.1 --port 8765Options:
--host TEXT default 127.0.0.1
--port INT default 8765
--reload optional development convenience
Do not bind to 0.0.0.0 by default.
MVP should favor robustness over advanced syntax.
Suggested default query builder:
- Join
QUERY...into one string. - Preserve quoted phrases with
shlex.splitif possible. - Escape embedded double quotes.
- Convert tokens to quoted FTS terms/phrases.
- Join with spaces, which FTS5 treats as AND.
Example:
user query: KV cache
fts query: "KV" "cache"
user query: "Fabrice Bellard"
fts query: "Fabrice Bellard"
If FTS raises a syntax error, return a clear error. Later add --raw-fts if advanced FTS syntax is needed.
Use FastAPI with Jinja templates.
GET / home page with search box and repo summary
GET /search?q=... search results with filters
GET /repos repository list
GET /repos/{owner}/{repo} repo browse page
GET /items/{item_id} item/thread page
Repo names can contain dots, so route parameters should allow llama.cpp. FastAPI path params handle this.
Alternative item routes can be added later:
GET /repos/{owner}/{repo}/issues/{number}
GET /repos/{owner}/{repo}/pulls/{number}
GET /repos/{owner}/{repo}/discussions/{number}
GET /repos/{owner}/{repo}/releases/{tag}
For MVP, /items/{item_id} is enough and avoids route escaping issues for release tags containing slashes.
Agents should be able to use the web server without scraping HTML.
GET /api/stats
GET /api/repos
GET /api/search?q=...&repo=llama.cpp&kind=issue&state=open&limit=20&offset=0
GET /api/items/{item_id}
Response bodies should match CLI JSON as closely as possible.
Keep the UI dense, restrained, and documentation-like.
- Neutral background.
- One muted accent color.
- No gradients, emoji icons, decorative blobs, or oversized cards.
- Tables for repository and result listings.
- Clear monospace treatment for repo names, tags, paths, and code identifiers.
- Compact filters above search results.
- Thread page should prioritize readability of Markdown/comment content.
- Use local CSS only. Avoid CDN dependencies.
Suggested CSS direction:
:root {
--bg: #f7f7f4;
--surface: #ffffff;
--text: #1d1f23;
--muted: #626a73;
--border: #d8dcd6;
--accent: #355f7c;
--accent-strong: #26495f;
--code-bg: #f0f2ef;
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
}Layout:
base.html: top nav, main content width around 1180px, footer with DB path/import time.index.html: prominent but compact search form, repo summary table.search.html: filters + result list/table + snippets.repo.html: counts and paginated item table.item.html: metadata header + body + timeline.
Issue/PR/discussion content is untrusted.
Recommended helper:
from markdown_it import MarkdownIt
import bleach
md = MarkdownIt("commonmark", {"linkify": True, "breaks": False})
ALLOWED_TAGS = bleach.sanitizer.ALLOWED_TAGS | {
"p", "pre", "code", "blockquote", "hr", "br",
"h1", "h2", "h3", "h4", "h5", "h6",
"table", "thead", "tbody", "tr", "th", "td",
"ul", "ol", "li", "img"
}
ALLOWED_ATTRS = {
**bleach.sanitizer.ALLOWED_ATTRIBUTES,
"a": ["href", "title", "rel", "target"],
"img": ["src", "alt", "title"],
"code": ["class"],
}
def render_markdown(text: str) -> str:
html = md.render(text or "")
clean = bleach.clean(html, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRS, protocols=["http", "https", "mailto"])
return bleach.linkify(clean)For MVP, consider disallowing img or rewriting image src to original GitHub URLs only. Do not serve local attachment files.
If commands that require data run before import:
No ghbb database found at C:\...\ghbb.db.
Run: ghbb import C:\CodeBlocks\ggml-org-backup\backup
If DB exists but has no import:
Database exists but contains no imported backup.
Run: ghbb import [BACKUP_ROOT]