{"id":51143728,"url":"https://github.com/ajmeese7/github-spy","last_synced_at":"2026-06-26T01:02:15.400Z","repository":{"id":362130225,"uuid":"1212033426","full_name":"ajmeese7/github-spy","owner":"ajmeese7","description":"Monitor all publicly available event information for a GitHub account.","archived":false,"fork":false,"pushed_at":"2026-06-02T19:12:32.000Z","size":149,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-06-02T21:15:41.763Z","etag":null,"topics":["account-monitoring","cybersecurity-tools","github","osint"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ajmeese7.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-16T02:02:26.000Z","updated_at":"2026-06-02T19:12:34.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ajmeese7/github-spy","commit_stats":null,"previous_names":["ajmeese7/github-spy"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/ajmeese7/github-spy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ajmeese7%2Fgithub-spy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ajmeese7%2Fgithub-spy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ajmeese7%2Fgithub-spy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ajmeese7%2Fgithub-spy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ajmeese7","download_url":"https://codeload.github.com/ajmeese7/github-spy/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ajmeese7%2Fgithub-spy/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34798183,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-25T02:00:05.521Z","response_time":101,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["account-monitoring","cybersecurity-tools","github","osint"],"created_at":"2026-06-26T01:02:13.634Z","updated_at":"2026-06-26T01:02:15.361Z","avatar_url":"https://github.com/ajmeese7.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# github-spy\n\n**Archive GitHub users' ephemeral public activity into durable, queryable local history.**\n\n[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)\n[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/demo.gif\" alt=\"github-spy demo: snapshot, stats, inspect, and diff\" width=\"800\"\u003e\n\u003c/p\u003e\n\n---\n\nGitHub's public data is ephemeral. Events disappear after 90 days. Stars, followers, and repos are mutable with no built-in changelog. If someone unstars a repo, unfollows you, or deletes a project, that information is gone.\n\n**github-spy** polls GitHub's REST API on a schedule and stores everything locally in SQLite, creating a persistent, queryable archive of what changed and when.\n\n## What it tracks\n\n| Data | What's detected |\n|------|----------------|\n| **Events** | Push, PR, issue, star, fork, and all other public event types — each row gets a derived `url` (compare URL for pushes, PR URL, issue URL, etc.) and a `details` one-liner (e.g. `\"pushed 3 commits to main — fix flaky test\"`) so the archive is actionable, not just a dump |\n| **Stars** | Which repos a user has starred, plus star/unstar changes over time |\n| **Followers** | Who follows the user, plus follow/unfollow changes |\n| **Following** | Who the user follows, plus follow/unfollow changes |\n| **Repos** | Public repositories owned by the user, plus creation/deletion |\n| **Profile** | Field-level change tracking (bio, company, location, follower counts, etc.) |\n\n## Install\n\n```bash\nuv tool install github-spy\n# or\npipx install github-spy\n```\n\nOr clone and run directly:\n\n```bash\ngit clone https://github.com/ajmeese7/github-spy.git\ncd github-spy\nuv sync\nuv run github-spy --help\n```\n\n## Quick start\n\n```bash\n# Set your token (optional but recommended for higher rate limits)\nexport GH_TOKEN=ghp_...\n\n# Take a snapshot of a user's public activity\ngithub-spy snapshot ajmeese7\n\n# Monitor multiple users continuously (every 15 minutes)\ngithub-spy watch ajmeese7 torvalds --interval 900\n\n# View what you've collected\ngithub-spy stats ajmeese7\n```\n\n## Example output\n\n### Snapshot\n\n```\n╭──────────────────────── ajmeese7 (Aaron Meese) ──────────────────────────────╮\n│ 2026-04-17T14:21:31Z                                                         │\n│   111 repos / 217 followers / 146 following                                  │\n│   profile:                                                                   │\n│   events: +198 new                                                           │\n│     + 2026-04-17T02:28:47Z PushEvent meese-enterprises/uptime-monitor        │\n│     + 2026-04-17T02:01:08Z PushEvent meese-enterprises/uptime-monitor        │\n│     + 2026-04-16T23:33:59Z PushEvent meese-enterprises/uptime-monitor        │\n│     + 2026-04-16T21:28:54Z WatchEvent Hona/temple-oc                         │\n│     + 2026-04-16T15:02:14Z WatchEvent zonelessdev/zoneless                   │\n│     ... and 193 more                                                         │\n│   stars: 1000 total 1000 changes                                             │\n│   followers: 217 total 217 changes                                           │\n│   following: 146 total 146 changes                                           │\n│   repos: 111 total 111 changes                                               │\n│   API quota: 4985/5000 remaining                                             │\n╰──────────────────────────────────────────────────────────────────────────────╯\n```\n\n### Stats\n\n```\nStats for ajmeese7\n\nEvent Types\n  PushEvent          ████████████████████████████████████████ 95\n  WatchEvent         ███████████████ 36\n  IssuesEvent        ████████████ 30\n  IssueCommentEvent  ██████ 15\n  PullRequestEvent   █████ 14\n  CreateEvent        ██ 6\n  ForkEvent           2\n\nStarred Repos by Language\n  Python      ████████████████████████████████████████ 290\n  TypeScript  ██████████████████ 137\n  Go          █████████ 70\n  JavaScript  █████████ 67\n  C           ███████ 55\n  Shell       ██████ 47\n  Rust        ██████ 45\n  C++         ██████ 44\n  HTML        ████ 36\n  Java        ██ 19\n\n              Most Active Repos\n┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━┓\n┃ Repository                       ┃ Events ┃\n┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━┩\n│ meese-enterprises/uptime-monitor │    120 │\n│ ajmeese7/hermes-agent            │      8 │\n│ ajmeese7/fog-libvirt             │      7 │\n│ LetsFG/LetsFG                    │      6 │\n│ ajmeese7/summarize               │      6 │\n│ neo4j-labs/neo4rs                │      5 │\n│ NousResearch/hermes-agent        │      4 │\n└──────────────────────────────────┴────────┘\n\nDaily Activity (last 30 days)\n  2026-03-30 ▃▆▆▅▂▁▁▂▄▁▃▅█▁▁▁▄▆  2026-04-17\n  198 events total, peak 24/day\n```\n\n### Users\n\n```\n                  Tracked Users\n┏━━━━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━┳━━━━━━━┓\n┃ Username ┃ Events ┃ Stars ┃ Followers ┃ Repos ┃\n┡━━━━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━╇━━━━━━━┩\n│ ajmeese7 │    198 │  1000 │       217 │   111 │\n└──────────┴────────┴───────┴───────────┴───────┘\n```\n\n## Commands\n\n| Command | Description |\n|---------|-------------|\n| `snapshot \u003cusers...\u003e` | One-time collection of public activity |\n| `watch \u003cusers...\u003e` | Continuous polling on an interval |\n| `inspect \u003cuser\u003e` | View locally stored data (no API calls) |\n| `show \u003cevent_id\u003e` | Print a single event's full detail (URL, summary, raw payload) |\n| `stats \u003cuser\u003e` | Terminal-rendered analytics and charts |\n| `export \u003cuser\u003e` | Export data as CSV, JSON, or JSONL |\n| `diff \u003cuser\u003e` | Show changes between two points in time (includes events) |\n| `users` | List all tracked users in the database |\n\nAll commands support `--json` for machine-readable output.\n\n```bash\n# Snapshot only specific data types\ngithub-spy snapshot ajmeese7 --collect events,stars\n\n# Export events as CSV for spreadsheet analysis — includes url, details, payload_json\ngithub-spy export ajmeese7 --type events --format csv --output events.csv\n\n# See what changed since last week (stars, followers, repos, profile, AND events)\ngithub-spy diff ajmeese7 --since 2026-04-10\n\n# Inspect recent follower changes\ngithub-spy inspect ajmeese7 --type followers --limit 50\n\n# Filter events by type, repo, or date range\ngithub-spy inspect ajmeese7 --type events --event-type PullRequestEvent,IssuesEvent\ngithub-spy inspect ajmeese7 --type events --repo meese-enterprises/uptime-monitor\ngithub-spy inspect ajmeese7 --type events --since 2026-04-10 --until 2026-04-17\n\n# Drill into a single event — renders URL, summary, and the full payload JSON\ngithub-spy show 52345678901\n```\n\nRun `github-spy \u003ccommand\u003e --help` for full options.\n\n## Use cases\n\n- **Track star/unstar patterns** as a signal for repo interest or hiring activity\n- **Monitor open source maintainers** to understand contribution patterns\n- **Build a personal activity archive** before GitHub's 90-day event window expires\n- **Detect bulk follow/unfollow** activity (bot detection, growth tracking)\n- **Track when repos appear or disappear** from a user's profile\n- **Feed data into external tools** via CSV/JSON export for custom analysis\n\n## Storage\n\nData lives in a single SQLite database at `~/.local/share/github-spy/github-spy.db` by default. Override with `--db /path/to/file.db`.\n\n## Poking at the SQLite directly\n\nThe `export` and `inspect` commands cover the common paths, but the DB is the authoritative artifact. Everything is plain SQLite. Open it in [TablePlus](https://tableplus.com/), [DB Browser for SQLite](https://sqlitebrowser.org/), [DBeaver](https://dbeaver.io/), or [sqlite-utils](https://sqlite-utils.datasette.io/), or just shell out:\n\n```bash\nsqlite3 ~/.local/share/github-spy/github-spy.db\n```\n\n### Table map\n\n| Table | What it holds |\n|---|---|\n| `events` | Every ingested public event with `url`, `details`, and the full `payload_json` |\n| `stars_current` / `star_history` | Current starred repos / add + remove history |\n| `followers_current` / `follower_history` | Current followers / follow + unfollow history |\n| `following_current` / `following_history` | Current followees / follow + unfollow history |\n| `repos_current` / `repo_history` | Current public repos / create + delete history |\n| `profiles` / `profile_history` | Latest profile JSON / field-level change log |\n| `api_cache` | ETag / Last-Modified caches (only used for events now) |\n| `metadata` | Schema and migration bookkeeping |\n\n### Useful queries\n\n```sql\n-- Most recent PR activity with URLs you can paste into a browser\nSELECT created_at, action, repo_name, details, url\nFROM events\nWHERE username = 'ajmeese7'\n  AND event_type = 'PullRequestEvent'\nORDER BY created_at DESC\nLIMIT 20;\n\n-- Breakdown of event volume by type\nSELECT event_type, COUNT(*) AS n\nFROM events\nWHERE username = 'ajmeese7'\nGROUP BY event_type\nORDER BY n DESC;\n\n-- Stars gained / lost in the last 30 days\nSELECT event_kind, full_name, detected_at\nFROM star_history\nWHERE username = 'ajmeese7'\n  AND detected_at \u003e DATE('now', '-30 days')\nORDER BY detected_at DESC;\n\n-- Follower churn summary\nSELECT event_kind, COUNT(*) AS n\nFROM follower_history\nWHERE username = 'ajmeese7'\nGROUP BY event_kind;\n\n-- Profile field changes over time\nSELECT field_name, old_value, new_value, detected_at\nFROM profile_history\nWHERE username = 'ajmeese7'\nORDER BY detected_at DESC;\n\n-- Daily event velocity for the last 30 days\nSELECT DATE(created_at) AS day, COUNT(*) AS n\nFROM events\nWHERE username = 'ajmeese7'\n  AND created_at \u003e DATE('now', '-30 days')\nGROUP BY day\nORDER BY day DESC;\n\n-- Commit messages extracted from the raw payload\nSELECT\n  created_at,\n  repo_name,\n  json_extract(payload_json, '$.payload.commits[0].message') AS first_commit_message\nFROM events\nWHERE username = 'ajmeese7'\n  AND event_type = 'PushEvent'\nORDER BY created_at DESC\nLIMIT 10;\n```\n\n## Token setup\n\nA GitHub personal access token is optional but recommended. Without one, you're limited to 60 API requests/hour. With a token, you get 5,000/hour.\n\nCreate a [fine-grained personal access token](https://github.com/settings/personal-access-tokens/new) with **Public Repositories (read-only)** access. No other permissions needed.\n\n```bash\n# Option 1: Environment variable\nexport GH_TOKEN=ghp_...\n\n# Option 2: .env file\necho \"GH_TOKEN=ghp_...\" \u003e .env\n\n# Option 3: Direct flag (not recommended for scripts)\ngithub-spy snapshot torvalds --token ghp_...\n```\n\n## Rate limiting\n\ngithub-spy automatically tracks your API quota and:\n- Warns when requests are running low\n- Sleeps with a progress bar when rate limited\n- Extends polling intervals in watch mode when quota is low\n- Retries transient server errors with exponential backoff\n- Uses HTTP conditional requests (ETag/Last-Modified) on the events feed to skip unchanged pages. Full-state endpoints (stars, followers, following, repos) deliberately bypass the cache: a partial 304 would leave the collector with an incomplete list and fabricate phantom unfollow / delete events. The full sweep for a typical user is ~30 requests against a 5,000/hour quota, so the tradeoff is trivial.\n\n## License\n\n[GPLv3](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fajmeese7%2Fgithub-spy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fajmeese7%2Fgithub-spy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fajmeese7%2Fgithub-spy/lists"}