README.md

July 7, 2026 · View on GitHub

Vibe Index

✨ How hard does your repo vibe? ✨

Vibe Index GitHub Marketplace Support on Boosty


Vibe Index reads your git history and scores how much of your repo was vibe‑coded by AI vs hand‑written by humans — then turns it into a badge you can flex. The higher the score, the harder it vibes: 0.0 = every line crafted by human hands, 10.0 = pure AI, top to bottom.

Vibe Index scale: 0 means hand-crafted by a human, 10 means fully written by AI

🚀 Usage

Two steps: drop the badge placeholder where you want it, then run the action.

1. Add the badge placeholder to your README

![Vibe Index](https://img.shields.io/badge/Indexing%20Vibe-6168e5?style=flat-square)

The action finds it by its Vibe Index alt text and swaps in the real badge on the first run — the stub above just renders an "Indexing Vibe" badge until then (an empty ![Vibe Index]() works too, but shows a broken image meanwhile). It works on its own line or inline inside a row of badges.

Prefer HTML comment markers?

Wrap the spot in markers instead — an explicit, comment-based anchor:

<!-- vibe-index:start --><!-- vibe-index:end -->

Between the markers the action writes HTML (so it renders in any context); an existing ![Vibe Index](...) image is updated as markdown. With badge-discovery: auto (the default) both styles are found; set it to markdown or markers to force one.

2. Run the action

name: Vibe Index

on:
  push:                # refresh after each merge to the default branch
  workflow_dispatch:   # ...and on demand

permissions:
  contents: write  # required to commit the refreshed badge

jobs:
  vibe-index:
    # Run only on the default branch — no branch name to hardcode.
    if: github.ref_name == github.event.repository.default_branch
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          fetch-depth: 0  # the action needs full git history

      - uses: roxblnfk/action-vibe-index@v1
        with:
          commit: true  # commit & push the refreshed badge in place
          push: true

This refreshes the badge whenever the default branch moves, with no branch name hardcoded. It's cheap: the action only commits when the score actually changes, and the GITHUB_TOKEN commit doesn't retrigger the workflow.

💡 Already have a release workflow? That's an even better place for it — add a single Vibe Index step (e.g. right after your version bump) so the badge is refreshed with every release. There, leave commit/push off and let your existing commit step pick up the changed file.

Want fewer runs instead? Trigger on a schedule (works on any default branch too):

on:
  schedule:
    - cron: '0 0 * * 0'  # weekly
  workflow_dispatch:

📍 Where push: true pushes

Good news: you're in control. push: true pushes to the branch you have checked out, not the branch that triggered the workflow. The target is resolved from git symbolic-ref HEAD — whatever actions/checkout left checked out — falling back to the runner env (GITHUB_HEAD_REF, else GITHUB_REF_NAME) only when HEAD is detached. So whichever branch you check out is where the badge lands. Two common setups:

➜ Straight to the default branch. This is the example above: a plain actions/checkout on a push to master leaves HEAD on master, so the refreshed badge is committed and pushed right back to master. Simplest setup, and the GITHUB_TOKEN commit won't retrigger the workflow. 👍

➜ Into a release PR (release-please & co). Prefer the badge to ride along with your next release instead of landing on master directly? Just check out the release PR branch before the action — then HEAD points at that branch and the badge is pushed neatly into the PR. Gate both steps on a PR actually existing so nothing runs on an empty release:

on:
  push:
    branches: [master]

permissions:
  contents: write

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: googleapis/release-please-action@v4
        id: release
        with:
          token: ${{ secrets.RELEASE_TOKEN }}

      # Check out the release PR branch so the badge lands *in the PR*.
      - uses: actions/checkout@v6
        if: ${{ steps.release.outputs.prs_created == 'true' }}
        with:
          fetch-depth: 0
          ref: ${{ fromJson(steps.release.outputs.pr).headBranchName }}
          token: ${{ secrets.RELEASE_TOKEN }}

      - uses: roxblnfk/action-vibe-index@v1
        if: ${{ steps.release.outputs.prs_created == 'true' }}
        with:
          commit: true
          push: true

Use a PAT (secrets.RELEASE_TOKEN) rather than the default GITHUB_TOKEN on the checkout if you want the badge commit to re-trigger the PR's checks.

🖥️ Run it locally (npx)

No workflow needed — score any repository straight from your terminal. The package ships the same analyzer as the action:

# the current repo
npx @roxblnfk/vibe-index

# any GitHub repo (cloned shallowly to a temp dir, removed afterwards)
npx @roxblnfk/vibe-index facebook/react

# a full URL, a specific branch, and the badge URL
npx @roxblnfk/vibe-index https://github.com/php/php-src --ref master --badge
Vibe Index: 5.0 / 10.0  (Balanced)
  AI code:     53.5%   |  Human code:    46.5%
  AI commits:  43.6%   |  Human commits: 56.4%
  Commits analyzed: 250

Common options (npx @roxblnfk/vibe-index --help for the full list):

OptionDefaultPurpose
-c, --commits <n>250Commits to analyze
-m, --co-author-multiplier <f>0.8AI share of a co-authored commit
-p, --extra-bot-patterns <re>Extra identity regex (repeatable)
--ref <branch>default branchBranch/tag to clone (remote sources)
--depth <n>commits + 50Clone depth (remote sources)
--token <token>$GITHUB_TOKENToken for private https clones
--jsonoffMachine-readable output
--badgeoffAlso print the badge URL & markdown

A local path is analyzed in place; an owner/repo shorthand or a URL is cloned to a temp dir and cleaned up when it's done. Private repos use --token or the GITHUB_TOKEN / GH_TOKEN environment variable.

⚙️ Configuration

Every option with its default — keep only what you need:

- uses: roxblnfk/action-vibe-index@v1
  with:
    # ─── analysis ───
    commits-count: '250'          # how many recent (non-merge) commits to scan
    co-author-multiplier: '0.8'   # AI share of a co-authored commit, 0..1 (0.8 = 80% AI / 20% human)
    extra-bot-patterns: |         # extra regexes (one per line) matched against the commit
      @my-company-bot\.com        # author and Co-Authored-By identities, merged on top of the
      \bInternalLLM\b             # built-in AI/bot signatures (Claude, Copilot, [bot], …)

    # ─── badge look ───
    include-message: 'Vibe Index' # left-hand label text
    badge-style: 'flat-square'    # flat | flat-square | plastic | for-the-badge | social
    badge-color: 'auto'           # 'auto' = color from the green→purple gradient by score, or a fixed hex / named color
    badge-logo: 'sparkles'        # built-in 'sparkles' ✨, a simple-icons slug (e.g. 'github'), or '' for none
    badge-link: ''                # URL the badge links to (defaults to this repo); '' = no link

    # ─── where to write the badge ───
    update-files: 'README.md'     # comma/newline list of files to update; '' to disable
    badge-discovery: 'auto'       # how to find the badge to replace:
                                  #   auto     = markers, then an existing ![Vibe Index](...) image
                                  #   markers  = only the <!-- vibe-index:start/end --> markers
                                  #   markdown = only an existing ![Vibe Index](...) image
    badge-output-file: ''         # also write the raw badge URL to this file (optional)

    # ─── auto-commit (needs permissions: contents: write) ───
    commit: false                 # commit the updated files
    push: false                   # push the commit to the current branch
    commit-message: 'chore: update Vibe Index badge'
    commit-user-name: 'github-actions[bot]'
    commit-user-email: '41898282+github-actions[bot]@users.noreply.github.com'

    # ─── quality gate ───
    assert-index: ''              # fail the run if the score is outside this range, e.g. '0.0-6.0' (≤ 60% AI)

    # ─── self-fetch (skip actions/checkout) ───
    fetch: false                  # clone the repo here instead of relying on a prior checkout (read-only)
    repository: ''                # 'owner/repo' or a git URL; defaults to the current repository
    ref: ''                       # branch/tag to clone; defaults to the repo default branch
    token: ''                     # token for private clones; defaults to the GITHUB_TOKEN env var

Skip actions/checkout with fetch: true

By default the action analyzes the repository checked out by a prior actions/checkout step (which needs fetch-depth: 0). Set fetch: true and it clones the repo itself — into a temp dir, scoped to the analysis window, removed afterwards — so a checkout step isn't needed at all:

permissions:
  contents: read
steps:
  - uses: roxblnfk/action-vibe-index@v1
    with:
      fetch: true            # no actions/checkout needed
      update-files: ''       # read-only: just compute the score & badge URL
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}  # only needed for a private repo

fetch mode is read-only: it computes the score, the badge-url and the other outputs, but doesn't touch files or commit (the temp clone is discarded). Use it to score another repo (repository: owner/other-repo) or to comment the score on a PR without a checkout. To rewrite a README in place, use the default checkout-based flow.

Outputs

OutputDescription
vibe-indexScore 0.010.0
ai-percentage / human-percentageShare of code lines by AI / by humans
ai-commits-percentage / human-commits-percentageShare of commits by AI / by humans
badge-urlGenerated shields.io badge URL
badge-markdownReady-to-paste markdown for the badge
badge-htmlReady-to-paste HTML for the badge (<img>, wrapped in <a> when badge-link is set)

Recipe: comment the score on every pull request

- uses: roxblnfk/action-vibe-index@v1
  id: vibe
  with:
    update-files: ''  # don't touch files, just compute the score

- uses: actions/github-script@v7
  with:
    script: |
      github.rest.issues.createComment({
        issue_number: context.issue.number,
        owner: context.repo.owner,
        repo: context.repo.repo,
        body: `Vibe Index: ${{ steps.vibe.outputs.vibe-index }} / 10`
      });

🎨 Badge styles

A few looks and the options that produce them (score 7.0 shown):

OptionsBadge
(defaults)badge
badge-style: for-the-badgebadge
badge-style: plasticbadge
badge-style: flatbadge
badge-style: socialbadge
badge-color: '8a2be2'badge
badge-logo: '' (no logo)badge
badge-logo: 'github'badge
include-message: 'AI ratio'badge

🧠 How it works

Detecting AI authorship

Detection is based on commit identities — the author Name <email> and any Co-Authored-By: / Assisted-By: identities — matched against a curated list of signatures (vendor email domains like @anthropic.com, GitHub App [bot] accounts, the Copilot agent identity, …). It deliberately does not scan the free-text message, so a human who merely mentions an AI tool, or who happens to be named "Claude", is never misclassified.

The built-in list lives in src/bot-signatures.js and grows in new releases. Add tools specific to your team via extra-bot-patterns (one regex per line) — they are merged on top of the built-in signatures.

Classifying each commit

Merge commits are ignored. Every other commit falls into exactly one bucket:

  • AI — the commit author identity is an AI/bot → counted fully as AI.
  • Co-authored — a human author with an AI Co-Authored-By: / Assisted-By: identity → credit is split by co-author-multiplier, applied to both lines and the commit itself.
  • Human — neither the author nor any co-author matches a signature.

Example with the default co-author-multiplier: 0.8:

100 changed lines
Co-Authored-By: Claude <noreply@anthropic.com>

→ 80 lines counted as AI, 20 as human; and the commit counts as 0.8 AI / 0.2 human.

The score

Vibe Index = (ai_code_ratio × 0.6 + ai_commits_ratio × 0.4) × 10

Code lines weigh 60%, commit authorship 40%. Both ratios are in 0..1, so the score is always 0.010.0:

AI codeAI commitsVibe Index
0%0%0.0 — fully hand-written
30%30%3.0 — human-focused
50%50%5.0 — balanced
100%100%10.0 — fully AI

Badge color

With badge-color: auto (the default) the color is interpolated along a green → festive purple ramp (#27ae60 → #1abc9c → #3498db → #6c5ce7 → #8a2be2) — green for hand-crafted, purple for fully vibe-coded. The color tracks the displayed score (quantized to the same 0.1 step), so it only changes when the number does. Set a fixed hex or named color to opt out.

🐛 Troubleshooting

  • "Failed to get git commits" — set fetch-depth: 0 on actions/checkout, so the action sees the full history.
  • The score is unexpectedly low (often 0.0) — the repository is most likely shallow, so the action only sees a slice of history. Set fetch-depth: 0 on actions/checkout, and make sure no earlier step runs git fetch --depth=… — that re-shallows even a full clone and starves this step of history. The action warns when it detects a shallow repo.
  • The badge doesn't update / isn't committed — enable commit: true (and push: true) and grant permissions: contents: write, or commit the changed file with your own step.
  • The badge image doesn't render — keep the marker pair on its own line; a line that starts with <!-- is treated as raw HTML by GitHub, so the action writes the badge on its own line there.