From 1b0ba4cd232f9e68f2fd8dca1f38a4e82a3110a9 Mon Sep 17 00:00:00 2001 From: Joe Mou Date: Sun, 25 Jan 2026 18:01:53 -0500 Subject: CLAUDE.md --- cgithub/CLAUDE.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 cgithub/CLAUDE.md (limited to 'cgithub/CLAUDE.md') diff --git a/cgithub/CLAUDE.md b/cgithub/CLAUDE.md new file mode 100644 index 0000000..e258e55 --- /dev/null +++ b/cgithub/CLAUDE.md @@ -0,0 +1,42 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +cgithub is a lightweight GitHub alternative frontend that scrapes GitHub's server-rendered HTML instead of using the API. This approach avoids API rate limits entirely by extracting embedded JSON data from ` - - -
- -
git clone https://github.com/<%= it.repo.owner %>/<%= it.repo.name %>.git
-git clone git@github.com:<%= it.repo.owner %>/<%= it.repo.name %>.git
- -
- "> - - -
-
- - <%~ it.body %> - - - - - - diff --git a/cgithub/views/layouts/base.eta b/cgithub/views/layouts/base.eta new file mode 100644 index 0000000..fb644f5 --- /dev/null +++ b/cgithub/views/layouts/base.eta @@ -0,0 +1,17 @@ + + + + + + <%= it.title %> + + <% for (const src of it.scripts ?? []) { %> + + <% } %> + + + <%~ it.body %> + + + + diff --git a/cgithub/views/layouts/repo.eta b/cgithub/views/layouts/repo.eta new file mode 100644 index 0000000..e4f7880 --- /dev/null +++ b/cgithub/views/layouts/repo.eta @@ -0,0 +1,59 @@ +<% +layout("/layouts/base.eta", { + title: `${it.repo.owner}/${it.repo.name} - ${it.path || "/"}`, + scripts: ["/static/refs.js"], +}) +%> + +
+ +
git clone https://github.com/<%= it.repo.owner %>/<%= it.repo.name %>.git
+git clone git@github.com:<%= it.repo.owner %>/<%= it.repo.name %>.git
+ +
+ "> + + +
+
+ +<%~ it.body %> + + diff --git a/cgithub/views/owner.eta b/cgithub/views/owner.eta index e5667fb..b3304fb 100644 --- a/cgithub/views/owner.eta +++ b/cgithub/views/owner.eta @@ -1,65 +1,54 @@ - - - - - - <%= it.name ? `${it.name} (${it.login})` : it.login %> - - - -
- <% if (it.avatarUrl) { %> - - <% } %> -
<%= it.name || it.login %>
-
<%= it.login %><%= it.type === "organization" ? " (organization)" : "" %>
- <% if (it.location) { %> -
<%= it.location %>
- <% } %> - <% if (it.nav.length > 0) { %> - - <% } %> -
+<% layout("/layouts/base.eta", { title: it.name ? `${it.name} (${it.login})` : it.login }) %> - <% if (it.bio || it.links.length > 0 || it.followers || it.company) { %> -
- <% if (it.bio) { %> -
<%= it.bio %>
- <% } %> - <% for (const link of it.links) { %> - - <% } %> - <% if (it.followers) { %> -
<%= it.followers %> followers<%= it.following ? ` · ${it.following} following` : "" %>
- <% } %> - <% if (it.company) { %> -
<%= it.company %>
- <% } %> -
+
+ <% if (it.avatarUrl) { %> + <% } %> - - <% if (it.pinned.length > 0) { %> -
-

Pinned

- <% for (const repo of it.pinned) { %> - <%~ include("./_ownerRepo", { repo, login: it.login }) %> - <% } %> -
+
<%= it.name || it.login %>
+
<%= it.login %><%= it.type === "organization" ? " (organization)" : "" %>
+ <% if (it.location) { %> +
<%= it.location %>
<% } %> - - <% if (it.repos.length > 0) { %> -
-

Repositories

- <% for (const repo of it.repos) { %> - <%~ include("./_ownerRepo", { repo, login: it.login }) %> + <% if (it.nav.length > 0) { %> +
+ <% } %> +
+ +<% if (it.bio || it.links.length > 0 || it.followers || it.company) { %> +
+ <% if (it.bio) { %> +
<%= it.bio %>
+ <% } %> + <% for (const link of it.links) { %> + + <% } %> + <% if (it.followers) { %> +
<%= it.followers %> followers<%= it.following ? ` · ${it.following} following` : "" %>
+ <% } %> + <% if (it.company) { %> +
<%= it.company %>
+ <% } %> +
+<% } %> - - - +<% if (it.pinned.length > 0) { %> +
+

Pinned

+ <% for (const repo of it.pinned) { %> + <%~ include("./_ownerRepo", { repo, login: it.login }) %> + <% } %> +
+<% } %> + +<% if (it.repos.length > 0) { %> +
+

Repositories

+ <% for (const repo of it.repos) { %> + <%~ include("./_ownerRepo", { repo, login: it.login }) %> + <% } %> +
+<% } %> diff --git a/cgithub/views/pulls.eta b/cgithub/views/pulls.eta index 9ec5ca1..697f6b1 100644 --- a/cgithub/views/pulls.eta +++ b/cgithub/views/pulls.eta @@ -1,4 +1,4 @@ -<% layout("./layout"); it.searchType = "pullrequests" %> +<% layout("/layouts/repo.eta"); it.searchType = "pullrequests" %> <% for (const pull of it.pulls) { %> diff --git a/cgithub/views/release.eta b/cgithub/views/release.eta index 46dd5db..64cca35 100644 --- a/cgithub/views/release.eta +++ b/cgithub/views/release.eta @@ -1,3 +1,3 @@ -<% layout("./layout.eta") %> +<% layout("/layouts/repo.eta") %> <%~ include("./_release.eta", { repo: it.repo, release: it.release, linkToDetail: false }) %> diff --git a/cgithub/views/releases.eta b/cgithub/views/releases.eta index 840908c..4ede28b 100644 --- a/cgithub/views/releases.eta +++ b/cgithub/views/releases.eta @@ -1,4 +1,4 @@ -<% layout("./layout.eta") %> +<% layout("/layouts/repo.eta") %> <% for (const release of it.releases) { %> <%~ include("./_release.eta", { repo: it.repo, release, linkToDetail: true }) %> diff --git a/cgithub/views/repo.eta b/cgithub/views/repo.eta index 5dc6beb..60746df 100644 --- a/cgithub/views/repo.eta +++ b/cgithub/views/repo.eta @@ -1,4 +1,4 @@ -<% layout("./layout.eta") %> +<% layout("/layouts/repo.eta") %> <% if (it.info) { %>
diff --git a/cgithub/views/tree.eta b/cgithub/views/tree.eta index 41ded06..fabf5b9 100644 --- a/cgithub/views/tree.eta +++ b/cgithub/views/tree.eta @@ -1,3 +1,3 @@ -<% layout("./layout.eta"); it.searchType = "code" %> +<% layout("/layouts/repo.eta"); it.searchType = "code" %> <%~ include("./_tree.eta", it) %> diff --git a/cgithub/views/wiki.eta b/cgithub/views/wiki.eta index d6047e1..781caff 100644 --- a/cgithub/views/wiki.eta +++ b/cgithub/views/wiki.eta @@ -1,4 +1,4 @@ -<% layout("./layout.eta"); it.path = "wiki/" + it.title %> +<% layout("/layouts/repo.eta"); it.path = "wiki/" + it.title %> <%~ include("./_wikiNav.eta", it) %> diff --git a/cgithub/views/wikiPages.eta b/cgithub/views/wikiPages.eta index 58d6d21..eb90781 100644 --- a/cgithub/views/wikiPages.eta +++ b/cgithub/views/wikiPages.eta @@ -1,4 +1,4 @@ -<% layout("./layout.eta"); it.path = "wiki/_pages" %> +<% layout("/layouts/repo.eta"); it.path = "wiki/_pages" %> <%~ include("./_wikiNav.eta", it) %> -- cgit v1.3.1 From 95f4269f1a1c3b93ae2b060c7935b03c773e9f4a Mon Sep 17 00:00:00 2001 From: Joe Mou Date: Wed, 9 Sep 2026 14:47:12 -0400 Subject: Add feature list to README.md --- cgithub/CLAUDE.md | 3 ++- cgithub/README.md | 59 +++++++++++++++++++++++++++++-------------------------- 2 files changed, 33 insertions(+), 29 deletions(-) (limited to 'cgithub/CLAUDE.md') diff --git a/cgithub/CLAUDE.md b/cgithub/CLAUDE.md index 6c6dc60..ef7b133 100644 --- a/cgithub/CLAUDE.md +++ b/cgithub/CLAUDE.md @@ -41,4 +41,5 @@ pnpm test ## Instructions - Use `for (const ... of ...)` instead of `.forEach()` -- Update `scraper.test.ts` \ No newline at end of file +- Update `scraper.test.ts` +- When implementing a new feature update `README.md` diff --git a/cgithub/README.md b/cgithub/README.md index cfcdccd..533c88f 100644 --- a/cgithub/README.md +++ b/cgithub/README.md @@ -5,41 +5,40 @@ domain. ![Screenshot of cgithub repository page.](docs/screenshot.png) -## Development +## Features -To run the server locally: -``` -$ pnpm run dev -``` +Implemented: -To run tests: -``` -$ pnpm test -``` +- Repository overview +- Directory listing and file browsing (`tree`/`blob`) +- Commit history (but this often results in 429 Too Many Requests on Cloudflare) +- Issue and pull request lists and search +- Release lists and details +- Wiki +- User and organization profiles -## Deployment +Not implemented: -To deploy to Cloudflare Workers: -``` -$ pnpm run deploy -``` - -The worker name and other settings live in `wrangler.jsonc`. +- Issue, pull request, and commit details +- Issue and pull request status and labels +- Code search (GitHub requires signing in) +- Pagination (only the first page of issues, pull requests, commits, releases, + branches, and tags is shown) +- Blame, contributors, stargazer/fork lists, network graphs, and insights +- Actions, checks, projects, packages, discussions, and gists +- Profile tabs other than the overview (repositories, projects, packages, + people, sponsoring) +- Anything requiring an account: signing in, starring, commenting, editing, or + any other write action +- Private repositories ## Browser extension redirects -Pages cgithub can't render (marketing pages, code search, anything unrouted) are -redirected back to github.com. A browser extension that rewrites github.com URLs -to a cgithub instance with `declarativeNetRequest` will match those redirects -too and send the request straight back, looping forever. - -To avoid this, cgithub never answers with an HTTP redirect: it serves a small -page that navigates to github.com with a two-second ``. -Because that navigation is initiated by the cgithub origin, the extension can -let it through with `excludedInitiatorDomains`; without that, the user at least -lands on a page they can stop on, with a link, instead of an endless loop. - -A rule that redirects to a cgithub instance should therefore look like this: +cgithub redirects unsupported routes back to github.com. A browser extension +that rewrites github.com URLs to a cgithub instance with `declarativeNetRequest` +will match those redirects and send the request back, looping forever. To avoid +this, the extension should configure `excludedInitiatorDomains` with the cgithub +domain: ```jsonc { @@ -56,6 +55,10 @@ A rule that redirects to a cgithub instance should therefore look like this: } ``` +## Related + +- [GotHub](https://codeberg.org/gothub/gothub) + ## Disclosures AI coding assistants, in particular Claude, are used in the development process. -- cgit v1.3.1