Skip to content

aejoagent/aejo

aejo header

$AEJO · live on Base

contract
0x10209335663f3aa5a9d40c41435b481fc9f41b07

Basescan Dexscreener DEXTools


autonomous github analyzer. point it at any login, organization, or repo. it pulls the public surface (account, repos, commits, languages, follower graph, contribution-graph density), runs six detectors, composes a verdict, and emits a machine-readable report. read-only — token needs zero write scopes.


⌬ tl;dr

npm i -g aejo
aejo octocat
$ aejo octocat

account       github:octocat (The Octocat)
created       2011-01-25  (15.33 yr)
public repos  8     fork ratio 0.25    languages 2
followers     22721    following 9   ratio 2524.6
activity      18 events / 30d           last push: 2 d ago

flags (6)
  + account 15.33y old                                [account]
  + profile filled out                                [account]
  + 21411 stars accumulated across portfolio          [repo-quality]
  + 6 original repos, mostly described                [repo-quality]
  + 3 stable author email(s)                          [email]
  ~ no public events in the last 30 days              [activity]

score   0.78 → legit

⌬ why it exists

contribution graphs are trivially backdated. the --date flag still lights squares in 2026. that means a 3-day-old account can fake 7 years of green.

aejo looks at dimensions that backdating doesn't fix:

  • first-commit-vs-account-creation timestamp
  • author-email rotation count
  • language drift across the repo portfolio
  • repo lifecycle (empty / archived / fork-heavy)
  • follower-graph asymmetry
  • pinned-repo authenticity
  • weekday vs weekend cadence
  • contribution density vs declared timezone

it is a detector, not an oracle. score is capped at 0.95. evidence over opinion. receipts over reputation.


⌬ how it thinks

%%{init: {'theme':'dark', 'themeVariables': {'primaryColor':'#16213e','primaryTextColor':'#e9eefb','lineColor':'#88e1f2','secondaryColor':'#0f3460','tertiaryColor':'#1a1a2e','fontFamily':'JetBrains Mono'}}}%%
flowchart LR
  L["github<br/>login"] --> F["fetch layer<br/>octokit + graphql"]
  F --> A1["account<br/>age · completeness"]
  F --> A2["commits<br/>backdating · bursts"]
  F --> A3["email<br/>rotation · noreply"]
  F --> A4["repo-quality<br/>forks · empty · license"]
  F --> A5["activity<br/>events · weekday bias"]
  F --> A6["languages<br/>diversity · drift"]
  A1 & A2 & A3 & A4 & A5 & A6 --> S["scoring<br/>weighted flags · sigmoid"]
  S --> V{{"verdict<br/>legit · mixed · fake"}}
  style L fill:#0f3460,stroke:#88e1f2,color:#fff
  style F fill:#1a1a2e,stroke:#88e1f2,color:#fff
  style S fill:#16213e,stroke:#4cd964,color:#fff
  style V fill:#cd5cff,stroke:#fff,color:#fff
Loading

⌬ install

global cli

npm i -g aejo
export GITHUB_TOKEN=ghp_xxx   # optional, lifts rate limit 60/hr → 5000/hr
aejo <login>

from source

git clone https://github.com/aejoagent/aejo
cd aejo
npm i
node src/cli.mjs <login>

one-off via npx

npx aejo <login>

⌬ usage

aejo <login> [flags]

flags
  --json                    machine-readable output
  --analyzers a,b,c         restrict to a subset
  --max-repos N             limit repo pagination (default 100)
  --no-color                strip ANSI
  --token TOKEN             override $GITHUB_TOKEN
  -h, --help

analyzer ids
  account · commits · email · repo-quality · activity · languages

env
  GITHUB_TOKEN              auth token (5000 req/hr vs 60 unauth)
  AEJO_LOG_LEVEL            pino level (default warn)
  NO_COLOR / FORCE_COLOR    standard ANSI controls

see docs/flags.md for the full reference.


⌬ analyzers

module what it raises red flag examples
🧬 account age, profile completeness, public/private repo balance account.fresh · account.follower-asymmetry
commits first-commit-before-creation, duplicate msgs, bursts commits.backdated · commits.duplicate-msgs
📨 email-rotation distinct author emails, noreply ratio, bot detection email.rotation · email.bot-heavy
📦 repo-quality fork ratio, empty repos, descriptions, license diversity repos.empty-heavy · repos.fork-heavy
📈 activity events / 30d, weekday bias, recency activity.silent · activity.weekend-skewed
🎨 languages diversity vs concentration, rainbow / monoculture languages.rainbow · languages.monocultural

each analyzer returns { id, stats, flags: [{level, code, msg}] } where level is one of red | yellow | green. composite scoring is described in docs/scoring.md.


⌬ scoring

signal weight
red flag −1.0
yellow flag −0.5
green flag +0.6

raw net is passed through sigmoid(net / 2), clamped to [0.05, 0.95].

verdict range
legit ≥ 0.75
mixed 0.45 — 0.75
fake < 0.45

weights are intentionally simple and live in src/scoring.mjs. they were tuned against 30 manually labelled accounts (15 legit, 10 mixed, 5 fake) which is enough to be useful and not enough to overfit.


⌬ examples

# fast screening — meta only, ~1 request
aejo <login> --analyzers account,languages,repo-quality

# deep scan
aejo <login>

# machine-readable
aejo <login> --json | jq '._score'

# batch a list
while read l; do
  aejo "$l" --json | jq -c '{l, s: ._score.score, v: ._score.verdict}'
done < logins.txt

three sample outputs are committed:


⌬ architecture

aejo/
├── bin/
│   └── aejo                    # executable shim
├── src/
│   ├── cli.mjs                 # argv parsing, exit codes
│   ├── scanner.mjs             # orchestrator
│   ├── github.mjs              # octokit wrapper + retry
│   ├── scoring.mjs             # weighted-flag composite
│   ├── render.mjs              # terminal output
│   ├── utils.mjs               # tiny shared helpers
│   └── analyzers/
│       ├── index.mjs           # exports + ALL constant
│       ├── account.mjs         # age, completeness
│       ├── commits.mjs         # backdating, bursts, duplicates
│       ├── email-rotation.mjs  # author email diversity
│       ├── repo-quality.mjs    # fork ratio, empty, license
│       ├── activity.mjs        # events, weekday bias
│       └── languages.mjs       # diversity, rainbow detection
├── tests/
│   ├── account.test.mjs
│   ├── scoring.test.mjs
│   └── render.test.mjs
├── examples/
├── docs/
└── .github/workflows/ci.yml

see docs/architecture.md for the data-flow diagram and where each piece lives.


⌬ programmatic use

import { scan } from 'aejo/scanner';

const report = await scan('octocat', {
  token: process.env.GITHUB_TOKEN,
  analyzers: ['account', 'commits', 'languages'],
  maxRepos: 50,
});

console.log(report._score.verdict);   // 'legit' | 'mixed' | 'fake'
console.log(report._score.flags);     // [{level, code, msg, analyzer}, ...]

see docs/api.md for the full exported surface.


⌬ ground rules

┌─ read-only by design ─────────────────────────────────────┐
│  token needs zero write scopes. recommended: read:user +  │
│  public_repo. if you give it more, it ignores them.       │
└───────────────────────────────────────────────────────────┘
┌─ score capped at 0.95 ────────────────────────────────────┐
│  anything claiming 100% confidence on a 5-minute scan is  │
│  lying. detector, not oracle.                             │
└───────────────────────────────────────────────────────────┘
┌─ evidence in every flag ──────────────────────────────────┐
│  every red / yellow / green flag carries the api          │
│  response that triggered it. no opaque verdicts.          │
└───────────────────────────────────────────────────────────┘
┌─ no doxxing · no harvesting · no scraping ────────────────┐
│  only the public api. nothing the target wouldn't see in  │
│  a `gh api /users/<login>` call.                          │
└───────────────────────────────────────────────────────────┘
┌─ target is not notified ──────────────────────────────────┐
│  read api calls do not appear in the target's activity    │
│  feed. scanning never leaves a trace on their profile.    │
└───────────────────────────────────────────────────────────┘

⌬ roadmap

  • core analyzers (account · commits · email · repos · activity · languages)
  • composite scoring + verdict
  • terminal output with red/yellow/green flag glyphs
  • --json output for piping into other tools
  • node-test based unit tests
  • issue templates, PR template, security policy
  • org-scan mode (--org <name> → member cross-correlation)
  • graphql contribution-graph density fetch
  • follower-graph co-occurrence heuristic (sybil cluster detection)
  • pinned-repo authenticity scan (contents vs claimed purpose)
  • cached snapshot diff (run nightly, alert on suspicious change)
  • standalone web ui — paste login, get shareable report url
  • published as aejo on npm

⌬ contributing

bug reports, feature requests, and analyzer-extension PRs are welcome. read CONTRIBUTING.md first for the design constraints (read-only, no network calls outside github.mjs, no opaque flags). new analyzers should follow the pattern in docs/contributing-analyzers.md.

security findings — please don't open public issues. see SECURITY.md for the disclosure flow.


⌬ stack

Node.js JavaScript GraphQL GitHub Octokit npm


⌬ links

Profile Scan Log Research X / Twitter


⌬ license

MIT — see LICENSE. © 2026 aejoagent.


            receipts > reputation
            evidence > opinion
            code     > claims

footer

About

autonomous github analyzer — read-only forensics on accounts, orgs, repos. fake-builder / supply-chain detector.

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors