Skip to main content

ai-profile

Show the evidence behind your AI collaboration.

ai-profile turns explicit Git provenance across your local repositories into privacy-safe GitHub Profile cards and a provider-filterable dashboard with a provider ledger—without uploading source code or guessing attribution.

Explore the live dashboard → · Generate yours in four commands

PyPI tests license: MIT python 3.11–3.14

English · 繁體中文

  • Explicit evidence: attribution comes from AI-* trailers and verified AI co-author identities—not source-code style.
  • Local-first privacy: CLI scanning, aggregation, refresh, and rendering happen on your machine; repository identities stay out of public assets.
  • Profile-ready: one render produces theme-aware SVG cards, a self-contained dashboard, and a machine-readable public summary.

ai-profile is not an AI code detector. Commits with no explicit evidence stay unknown (shown as Unattributed in the UI); they are never silently counted as human or assigned to a provider. Add AI-* trailers to future commits when you want their AI participation recorded.

A real GitHub Profile example

This card contains the maintainer's real public aggregate data. Select it to open the provider-filterable dashboard; the card shows explicit provider participation and evidence totals when the Git history declares AI activity.

Open Wenyu Chiou's interactive AI collaboration dashboard

The card shows sustained AI collaboration (active AI days and an 84-day voxel collaboration mine — continuous 28-day landscape terraces with linear dual pillars for AI-attributed and Other commits, publishable dates only), breadth across AI providers, and the explicit evidence totals behind every number. It is a record of declared Git evidence, not a skill score.

Why ai-profile?

  • Generic GitHub statistics answer how active a developer is.
  • Line-level attribution tools answer which lines a tool changed.
  • ai-profile answers how AI participated and what evidence supports that claim across repositories, while keeping the analysis local.

It complements those tools; it does not replace them or infer historical AI use when Git evidence is absent.

Install

Requires Python 3.11 or newer and Git 2.17 or newer. The wheel-onboarding workflow tests Ubuntu, Windows, and macOS on Python 3.12; the full suite also tests Python 3.11–3.14 on Ubuntu. Repositories must use Git's SHA-1 object format.

python -m pip install --upgrade ai-profile-cli
aiprofile --version

The package name is ai-profile-cli; the installed command is aiprofile. If your shell has not refreshed its script path, use:

python -m aiprofile --version

Quickstart

Run these commands from the root of one of your repositories:

aiprofile init
aiprofile scan .
aiprofile aggregate
aiprofile render

Use python -m aiprofile in place of aiprofile for any command if needed.

  • init creates the local configuration and seeds your identity from git config user.email.
  • scan reads commits reachable from the repository's current HEAD and records commits authored by your configured identities.
  • aggregate prints the exact privacy-safe statistics eligible for publication.
  • render writes exactly eight files to dist/:
badge-dark.svg       heatmap-light.svg   summary-dark.svg
badge-light.svg      profile.json        summary-light.svg
dashboard.html       heatmap-dark.svg

Existing commits without explicit AI evidence will appear as unknown. Large unknown counts are an honest result, not a failed scan. Add trailers to future commits when you want their AI participation credited.

Configure identities and repository privacy

The configuration file is:

  • macOS/Linux: ~/.aiprofile/config.json
  • Windows: %USERPROFILE%\.aiprofile\config.json ($HOME\.aiprofile\config.json in PowerShell)
  • custom automation: $AIPROFILE_HOME/config.json

Only commits authored by an address in identities count. After init, close other aiprofile processes, open the file as UTF-8 JSON, and add every address you use—including GitHub noreply addresses:

{
  "identities": [
    "you@example.com",
    "12345678+username@users.noreply.github.com"
  ],
  "repositories": [],
  "salt": "keep-the-existing-generated-value"
}

Keep the generated salt unchanged. After a scan, each repository entry also contains path, repository_uid, and publication_level. Change only publication_level when adjusting privacy:

On Windows, save with an editor's UTF-8 (no BOM) option. Windows PowerShell 5.1's Set-Content -Encoding utf8 adds a BOM that strict JSON readers, including this Public Beta, reject. VS Code's default UTF-8 encoding is suitable; confirm the file still parses before replacing your backup.

{
  "path": "/local/path/created-by-scan",
  "repository_uid": "keep-the-existing-generated-value",
  "publication_level": "aggregate_only"
}

The allowed values are:

  • aggregate_only — default; include totals but withhold repository identity and daily dates.
  • full — include identity-redacted daily aggregate activity in date views.
  • excluded — omit that repository entirely.

aiprofile scan --full /path/to/repository is the supported opt-in from aggregate_only to full. It does not mean the repository is public on GitHub, and a later scan without --full does not downgrade it. To reduce publication, edit only the existing entry's publication_level, save valid UTF-8 JSON, then run:

aiprofile aggregate
aiprofile render

If configuration parsing fails, restore the previous valid JSON; do not delete or regenerate the salt, path, or repository_uid. Always review aggregate before publishing.

Automate daily updates

Choose one path. Keep private and aggregate_only repositories on your own machine; use GitHub Actions only when every source repository is already public.

# Refresh every configured, non-excluded repository now.
aiprofile refresh --out dist
aiprofile refresh --out dist --dry-run

# Or install one daily local job (05:37 in the machine's local time).
aiprofile schedule install --profile-repo /path/to/USERNAME --time 05:37
aiprofile schedule status
aiprofile schedule remove

Refresh every configured repository

refresh rescans each configured path once, then aggregates and writes the same eight files. Aliases do not cause duplicate scans, and excluded repositories remain excluded. A scan, configuration, privacy, or rendering failure publishes no new generation. --dry-run lists which of the eight files would change without changing configuration, publication policy, recorded database/WAL content, or output assets. It may create or use the advisory lock, and SQLite may update transient -shm coordination bytes while reading committed WAL content; neither is published data.

If two configuration entries resolve to the same path but carry different repository_uid values, refresh refuses the whole run before scanning or reading cached aggregates. Keep the UID created by scan; do not copy a UID between entries or invent a replacement.

Only one refresh can use an AIPROFILE_HOME at a time. On the rare filesystem failure where output rollback is incomplete, the CLI says that partial assets or recovery backups may remain; inspect the output before committing it.

Private or local repositories: native scheduler

schedule install creates an OS-native user job: Task Scheduler on Windows, launchd on macOS, or a systemd user timer on Linux. It refreshes <profile-repo>/dist daily and, by default, stages only the eight generated paths, commits only byte changes, and pushes through the repository's existing Git authentication only while the remote still equals the captured parent. Push mode requires one fetch destination and the same single push destination; multiple or different push URLs fail closed before refresh. The captured destination is bound to a fixed alias inside a private, isolated Git context; push and verification never put the URL in argv and cannot be redirected by later repository, global, insteadOf, or pushInsteadOf changes. Supported destinations are credential-free HTTPS, SSH/SCP, Git, file URLs, or local paths without query strings or fragments. Local paths are resolved from the Profile repository before the isolated context is created. Shallow and partial clones are rejected before refresh because scheduled publication requires a complete local Git history. Use a credential manager, askpass, or SSH agent; embedded passwords and authorization headers are intentionally unsupported, and ambient proxy variables are not forwarded. The tool does not persist or log credentials. Add --no-push to create and advance the local exact-eight commit without pushing it to the remote; use --dry-run to preview installation without changing scheduler state.

The user scheduler and machine must be available. Windows and systemd can recover missed runs according to their native settings; launchd does not replay a run missed while the machine was powered off. Detached branches, changed branch state, protected branches, and rejected pushes fail closed. Scheduler commits are mechanical and intentionally do not run user commit hooks or signing.

Before a push-capable run, a clean local checkout is safely fast-forwarded when the recorded remote branch is ahead and is a verified descendant of local HEAD; the fetch uses the same isolated destination transport as publication. Deliberate local commits, dirty checkouts, rewinds, deletions, and diverged or unstable remote tips still fail closed for manual synchronization. A failed push is retried from the same private pending commit only after an interrupted branch update and the exact-eight index are safely recovered, and while its branch, parent, tree, and remote remain unchanged. The actual push uses an exact-old lease tied to that parent and verifies the remote at the immutable commit before reporting success or clearing retry state. The private retry record binds a SHA-256 commitment of the destination, never the URL itself. Different homes targeting one Profile are serialized by that Profile's Git metadata lock, and generated bytes are verified against the completed refresh before the commit is created.

Install the scheduler from a Python environment that will remain in place. If you move, remove, or upgrade that interpreter or virtual environment, run schedule install again and confirm schedule status.

Public repositories: GitHub Actions

For a Profile sourced entirely from public repositories:

  1. Copy docs/templates/profile-refresh-caller.yml to .github/workflows/profile-refresh.yml in the Profile repository.
  2. Edit the explicit public owner/repo list. Add the identity-email payload as the repository secret AIPROFILE_IDENTITIES; never put it in with:. If you have individually confirmed old commits, optionally sync the complete private ledger as AIPROFILE_ATTESTATIONS.
  3. In Settings → Pages, choose GitHub Actions as the source, then run the workflow once with Actions → Daily ai-profile refresh → Run workflow.

The template runs daily at 05:37 UTC and also supports manual dispatch. It pins the reusable workflow to commit 62284cc9ffb6b1a05af677e8f20c73d33e4bc4c6, installs exactly ai-profile-cli==0.10.0, rejects non-public sources before scanning, and deploys Pages from the exact published-sha produced in that run. It uses only GITHUB_TOKEN; no PAT fallback is provided. GitHub-hosted automation is not local-first processing: it clones only the public repositories you list and treats them as full. Use the local scheduler if any source is private or aggregate_only.

Branch protection can reject the direct asset commit. Scheduled workflows in public repositories can be disabled after 60 days without repository activity, forks require Actions to be enabled, and organization Actions allowlists must permit the pinned actions and reusable workflow. A commit made with GITHUB_TOKEN does not trigger ordinary push workflows or a Pages build, so the caller performs an explicit, same-run Pages deployment. Use one caller, not a matrix of overlapping refresh jobs.

After personally confirming older commits, aiprofile reconcile sync-github --profile-repo OWNER/PROFILE --confirm-sync sends the complete private ledger to the optional AIPROFILE_ATTESTATIONS Actions secret through gh standard input. Sync again after every add/remove; missing or invalid secrets never make the workflow infer AI. The cloud caller must explicitly pass that secret to the reusable workflow. aiprofile sources suggest OWNER/REPO is a read-only comparison against configured sources; it never enrolls a repo.

Publish to your GitHub Profile

Run aiprofile render --out dist inside your USERNAME/USERNAME Profile repository, commit dist/, and place this clickable card in its README:

<a href="https://USERNAME.github.io/USERNAME/dist/dashboard.html">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="dist/summary-dark.svg">
    <img alt="Open my interactive AI collaboration dashboard"
         src="dist/summary-light.svg">
  </picture>
</a>

Add the heatmap with the same <picture> pattern using heatmap-{light,dark}.svg. GitHub READMEs do not execute JavaScript, so the SVG remains the Profile view and the link opens dashboard.html.

For manual publication, host the dashboard with GitHub Pages as follows. If you use the daily Action above, keep GitHub Actions as the Pages source and skip these branch-source steps.

  1. Push README.md and dist/ to the Profile repository's main branch.
  2. Open Settings → Pages.
  3. Under Build and deployment, choose Deploy from a branch.
  4. Select main, / (root), then Save.
  5. Open https://USERNAME.github.io/USERNAME/dist/dashboard.html.

A new Pages deployment can return 404 for a few minutes. Wait for the Pages workflow to finish, then reload the exact case-sensitive URL. If it still returns 404, confirm that Pages uses main and / (root) and that dist/dashboard.html exists in the pushed commit.

What gets generated

The synthetic previews below demonstrate the complete output family:

The summary and dashboard form an original voxel collaboration mine: thick orthographic terrain, blue AI-attributed pillars beside stone Other-record pillars, cubic explorers, provider workstations and an evidence archive. Pillar fronts use a shared linear scale; decoration never changes counts. Horizontal date bands remain separate from terrain and moving characters. The generation date is always a snapshot, never a real-time claim.

Synthetic AI collaboration summary card Synthetic commit-activity heatmap; intensity is commit volume and hue is AI share Synthetic AI-assisted share badge verified from Git provenance

The two summary SVGs include continuously looping original characters; heatmap and badge SVGs stay static. Reduced motion and print stop the animation. dashboard.html is a self-contained provider-filterable view with all dates in the selected range, pause/play, and light, dark, and system themes. It loads no external script, font, tracker, or API. profile.json contains the same validated public aggregate contract used by every renderer.

Declare AI participation

Known AI co-author identities are recognized automatically. For other tools, or richer attribution, add one contiguous AI-* trailer block after a blank line in the commit message:

feat: add aggregation service

AI-Provider: Anthropic
AI-Model: Claude-Sonnet
AI-Tool: Claude-Code
AI-Role: implementation, documentation
AI-Mode: AI-Assisted
AI-Reviewed-By: Human

Repeat AI-Provider: without a blank line between actor groups to declare another AI actor in the same commit. One commit can therefore equal one unique AI-attributed commit and multiple actor presences. Use AI-Mode: Human-Only only for an explicitly human-only commit.

Provider declarations are normalized to a canonical provider identity (typically company-oriented). The dashboard may show the more recognizable product display name instead: AI-Provider: Anthropic is shown as Claude. This is a label-only mapping; it does not change aggregation or counts.

When AI-Model is present, its canonical value is mapped to a small schema-owned family vocabulary (for example, Claude-Sonnet → Claude) and retained as validated, machine-readable evidence in profile.json. Family commit counts remain intentionally non-exclusive, while actor presences and active days remain separate measures. Missing model declarations stay Unknown; raw model strings never enter public assets.

v0.10 also accepts Assisted-By: LLM (Claude Code) and Generated-By: Codex CLI when they explicitly identify AI or a registered AI tool; a bare organization name is insufficient. Ambiguous values remain Unattributed. For a single staged commit, opt in with aiprofile provenance mark --provider OpenAI --tool "Codex CLI" --confirm-ai, then install optional, non-overwriting hooks with aiprofile provenance hook install. A mark is bound to the current HEAD and staged tree, is consumed only when the final commit retains the confirmed AI trailers, and can be removed with aiprofile provenance clear. aiprofile provenance doctor reports hook, pending-mark, and recent explicit-evidence status. Existing hooks require manual integration; opening an AI tool never creates a mark. If existing AI trailers cannot be kept in distinct actor groups, the hook stops the commit and leaves the mark for manual message correction.

For an older, individually confirmed reachable commit, use aiprofile reconcile add --repo PATH --sha FULL_SHA --provider OpenAI --confirm-ai, then aiprofile refresh. reconcile list and reconcile remove --repo PATH --sha FULL_SHA --confirm-remove manage the private ledger without rewriting Git history. Do not backfill a commit you have not personally verified. A proposed squash message can be checked with aiprofile provenance pr-check --base BASE --head HEAD --message-file MESSAGE; GitHub may replace source messages during squash, so confirm the final reachable commit as well. Optional PR check and squash-message templates are provided; neither automatically merges or asserts that AI participated.

Privacy

  • Scanning, aggregation, refresh, and rendering make no network calls and send no telemetry. The optional local scheduler may run git push through a credential manager, askpass, or SSH agent; ai-profile does not persist or log credentials.
  • The optional public Action runs on a GitHub-hosted runner and clones only explicit public repositories. Identity emails are passed as a secret and are not written to public assets or default workflow logs. An optional attestations secret holds the complete private reconciliation ledger; malformed, out-of-allowlist, or over-48 KB values are rejected.
  • Public assets contain the UTC generation date. They may also contain aggregate counts, public provider names, and evidence totals; repository activity dates appear only for full repositories.
  • Public assets never contain repository names or paths, organization names, branches, commit SHAs or messages, raw trailers, prompts, or email addresses.
  • aggregate is the publication preview. Review it before committing any generated asset.
  • Aggregate-only output is identity redaction, not anonymity. Repeated publication can reveal when totals changed.
  • Never publish or sync .aiprofile; it contains private paths, identities, repository identifiers, and a local salt.

Read the complete privacy model.

Metrics and current limitations

  • AI-attributed commits are unique commits with at least one explicit AI actor presence.
  • Actor presences count distinct provider/tool participation inside a commit. Provider totals can exceed unique commit totals.
  • Model-family rows in profile.json count explicit canonical AI-Model evidence. A commit can contribute to more than one family row; this does not inflate the unique AI-attributed commit headline.
  • Unknown remains separate from human.
  • Evidence quality is verified > declared > imported > inferred > unknown.
  • Scans cover commits reachable from the current HEAD, not every branch.
  • Git repositories using SHA-256 object format are not supported yet.
  • Bot-authored commits are outside the author-identity scan unless the bot address is deliberately configured; some bot-plus-human co-author histories may therefore be absent.
  • Historical AI use without explicit Git evidence stays unknown. Source-code style is never used to reconstruct attribution.

Help, contributing, and license

Run aiprofile --help or aiprofile COMMAND --help. Use GitHub Issues for bugs and feature requests; report vulnerabilities through the security policy.

Contributions are welcome; see CONTRIBUTING.md. Licensed under the MIT License; vendored icon notices are in THIRD_PARTY_NOTICES.md.

Metadata

Release files for ai-profile-cli 0.10.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ai-profile-cli 0.10.0
File Size Uploaded
ai_profile_cli-0.10.0.tar.gz 1.8 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for ai-profile-cli 0.10.0
File Interpreter ABI Platform
ai_profile_cli-0.10.0-py3-none-any.whl Python 3 none any Details

Total release size: 2.0 MB

Release files / ai_profile_cli-0.10.0.tar.gz

Download URL ai_profile_cli-0.10.0.tar.gz
Size 1.8 MB
Tags Source
SHA-256 checksum
How to use checksums
4c27b7d15fe81850a566b5e5f335cf9e52c8873122599169490cfb9bc32f5548
BLAKE2b-256 checksum
How to use checksums
32d029d87e0c72e346ade5adcf5ea505b13cf289a403cb92d471a586379e0033
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release files / ai_profile_cli-0.10.0-py3-none-any.whl

Download URL ai_profile_cli-0.10.0-py3-none-any.whl
Size 191.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f928f3cea716f80b005b1e126af8b774bbc63b6f9886b54cdb2748454ef4da4e
BLAKE2b-256 checksum
How to use checksums
3dc61f59cd5306de43326175eaea712418f5634a879d7ef9f6c0bb5c17a581a4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.10.0 This release

2 release files

0.9.0

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.2

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.9

2 release files

0.4.8

2 release files

0.4.7

2 release files

0.4.6

2 release files

0.4.5

2 release files

0.4.4

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page