From 6678776518a4c59112a48da88cdbd4054585ac2e Mon Sep 17 00:00:00 2001
From: Himanshu Singh
Date: Sun, 4 Oct 2026 09:14:27 +0530
Subject: [PATCH] Make repo pages distinct and sitemap dates honest;
plain-language copy
Search Console showed 92 repo pages "crawled, not indexed" and 364
"discovered, not indexed". Two causes addressed here:
- lastmod moved on every request (static pages) or every rescore (repos),
so Google had no reason to trust it. A new repo.content_changed_at only
moves when the score or badge changes; sitemap, package URLs and
JSON-LD dateModified use it. The bundled DB is migrated and backfilled
from git history (the rescore job only commits when a score changes).
- Repo pages were ~70% identical text, mostly the nine per-agent
rationale paragraphs. Each page now has an "At a glance" summary
(rank matching the leaderboard, best/weakest agent, biggest fix), the
three checks each agent weighs most with this repo's result, and a
repo-specific meta description. The rationales stay on /methodology,
linked from every agent row. Shared text is now ~58%.
Visible copy across the site is rewritten in plain words; facts and
caveats are unchanged. Privacy, terms and the changelog are untouched.
Co-Authored-By: Claude Opus 5.5
---
AGENTS.md | 8 +-
README.md | 4 +-
app/about/page.tsx | 48 +++++-----
app/action/page.tsx | 64 +++++++-------
app/layout.tsx | 4 +-
app/llms.txt/route.ts | 4 +-
app/methodology/page.tsx | 90 +++++++++----------
app/not-found.tsx | 32 ++++---
app/opengraph-image.tsx | 10 +--
app/package/[registry]/[name]/page.tsx | 16 ++--
app/package/page.tsx | 21 +++--
app/page.tsx | 20 ++---
app/repo/[...slug]/page.tsx | 48 +++++++---
app/roadmap/page.tsx | 6 +-
app/score/[host]/[owner]/[name]/error.tsx | 4 +-
app/score/[host]/[owner]/[name]/page.tsx | 17 ++--
app/score/opengraph-image.tsx | 2 +-
app/score/page.tsx | 30 +++----
app/sitemap.ts | 23 ++---
app/skill/page.tsx | 89 +++++++++----------
components/ActionEmbed.tsx | 10 +--
components/AlternativesStrip.tsx | 6 +-
components/BadgeAdoptedTag.tsx | 2 +-
components/BadgeEmbed.tsx | 5 +-
components/HomeJsonLd.tsx | 10 +--
components/LiveScoreForm.tsx | 6 +-
components/ModelPills.tsx | 2 +-
components/ModelSuggestions.tsx | 6 +-
components/PerModelScores.tsx | 100 +++++++++++++++------
components/RecentScores.tsx | 2 +-
components/RepoSummary.tsx | 85 ++++++++++++++++++
components/SignalListCard.tsx | 4 +-
components/SortSelect.tsx | 2 +-
data/rank.db | Bin 966656 -> 966656 bytes
lib/db.ts | 43 +++++----
lib/skill-content.ts | 19 ++--
lib/types/db.ts | 5 ++
lib/utils/repo-summary.ts | 95 ++++++++++++++++++++
lib/version.ts | 2 +-
tests/repo-summary.test.ts | 103 ++++++++++++++++++++++
40 files changed, 709 insertions(+), 338 deletions(-)
create mode 100644 components/RepoSummary.tsx
create mode 100644 lib/utils/repo-summary.ts
create mode 100644 tests/repo-summary.test.ts
diff --git a/AGENTS.md b/AGENTS.md
index c8ae820..65d6344 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -66,7 +66,7 @@ app/
twitter-image.tsx # next/og convention — twitter:image, re-exports opengraph-image (auto-wired)
og/repo/[...slug]/route.tsx # per-repo OG image — a plain route, because the next/og file
# convention would sit at a path the catch-all page route also claims
- score/page.tsx # Live Score entry — URL form, past scores, FAQ
+ score/page.tsx # Live Score entry — URL form, visitor's recent scores + top leaderboard repos, FAQ
score/opengraph-image.tsx # next/og convention — Live Score OG image (auto-wired)
score/twitter-image.tsx # next/og convention — /score twitter:image, re-exports (auto-wired)
score/[host]/[owner]/[name]/page.tsx # live score; result cached 1h per repo (unstable_cache); redirects to the repo page when indexed
@@ -81,7 +81,7 @@ components/ # Tailwind-styled React components
HostPill.tsx, HostSelect.tsx, Medal.tsx, ModelPills.tsx,
MobileNav.tsx, Pagination.tsx, SearchBar.tsx, SelectMenu.tsx, SortSelect.tsx,
SignalRow.tsx, SuggestionItem.tsx, VersionPill.tsx,
- RepoHero.tsx, ScoreDeltaPopover.tsx, SignalListCard.tsx, ModelSuggestions.tsx, PerModelScores.tsx,
+ RepoHero.tsx, RepoSummary.tsx, ScoreDeltaPopover.tsx, SignalListCard.tsx, ModelSuggestions.tsx, PerModelScores.tsx,
AlternativesStrip.tsx, BreadcrumbJsonLd.tsx, HomeJsonLd.tsx, ExternalLink.tsx,
BadgeEmbed.tsx, ActionEmbed.tsx, PeerlistCard.tsx, PeerlistBadge.tsx, ProductHuntBadge.tsx,
CopySnippet.tsx, PackageLookupForm.tsx,
@@ -99,6 +99,7 @@ lib/
badge.ts # SVG badge renderer (used by /api/badge)
contact.ts # packageRequestIssueUrl — pre-filled GitHub issue link for unscored packages
repo-path.ts # repoPath / repoIdentity — the one place a repo URL is built or parsed
+ repo-summary.ts # per-repo rank, best/worst agent, key signals — the repo-specific text on repo pages
scoring/
signals/ # one file per signal + helpers + types + index
weights.ts # per-model weight tables
@@ -133,6 +134,7 @@ tests/
parse-repo-url.test.ts # GH / GL / BB parsing + edge cases
scorer.test.ts # scoreRepo, topImprovements
badge-adoption.test.ts # detectBadgeEmbed — README badge-embed detection
+ repo-summary.test.ts # summarizeRepo / keySignals — ranks, ties, best/worst agent
path-resolution.test.ts # firstExisting / resolveRelative / resolveAllRelative — case-insensitive lookup
live-score.test.ts # content-candidate coverage vs the signals, path traversal, host URLs
signals/ # one *.test.ts per signal
@@ -210,7 +212,7 @@ If either sibling isn't present locally, flag it; never silently skip the propag
1. Add a `ModelProfile` to `MODELS` in `lib/scoring/weights.ts` — weights for every signal.
2. Appears automatically in the leaderboard model pills, methodology weight-profile panel, and repo-page suggestions.
-3. Update the hard-coded agent lists/counts that **don't** derive from `MODELS`: `APP_DESCRIPTION` + `APP_KEYWORDS` in `lib/version.ts`, `lib/skill-content.ts`, the "Which agents" FAQ in `app/methodology/page.tsx`, `app/page.tsx`, `app/skill/page.tsx`, `app/action/page.tsx`, `app/terms/page.tsx`, `app/opengraph-image.tsx`, the footer strip in `app/og/repo/[...slug]/route.tsx`, the `generateMetadata` description + JSON-LD description in `app/repo/[...slug]/page.tsx`, the Dataset description in `components/HomeJsonLd.tsx`, and `README.md`. Grep the current count word (e.g. `eight`) and the trailing `OpenHands, Pi` to find them all — `tasks/` and `lib/changelog.ts` are historical records and stay as-shipped, and `.claude/skills/agent-friendly/SKILL.md` is an install artifact pinned by `skills-lock.json` (it refreshes via `npx skills add`, never by hand).
+3. Update the hard-coded agent lists/counts that **don't** derive from `MODELS`: `APP_DESCRIPTION` + `APP_KEYWORDS` in `lib/version.ts`, `lib/skill-content.ts`, the "Which agents" FAQ in `app/methodology/page.tsx`, `app/page.tsx`, `app/score/page.tsx`, `app/skill/page.tsx`, `app/action/page.tsx`, `app/terms/page.tsx`, `app/opengraph-image.tsx`, the footer strip in `app/og/repo/[...slug]/route.tsx`, the Dataset description in `components/HomeJsonLd.tsx`, and `README.md`. Grep the current count as a word and a digit (e.g. `nine`, `9`) and the trailing `OpenHands, Pi` to find them all — `tasks/` and `lib/changelog.ts` are historical records and stay as-shipped, and `.claude/skills/agent-friendly/SKILL.md` is an install artifact pinned by `skills-lock.json` (it refreshes via `npx skills add`, never by hand).
4. Existing repos keep their old per-model rows until rescored. `PerModelScores` renders the missing model as "—" (not 0), but **everything that reads `model_score` via a JOIN degrades silently to empty** until the backfill lands: the leaderboard (`listLeaderboard`, i.e. `/?model=`) and `getAlternatives` (the repo page's alternatives strip under `?model=`) both inner-join `model_score` and return **zero rows** for a model with no rows yet — no error, just an empty page. Reweighting an existing model has the same staleness problem in reverse: stored scores stay at the old weights until rescored. Either trigger `scheduled-rescore.yml` via `workflow_dispatch` at deploy time, or accept up to 6 hours of the empty/stale state until the cron runs.
5. **Mirror to both siblings**: copy the weights change into `../agent-friendly-action/src/scoring/weights.ts` **and** `../agent-friendly-skill/src/scoring/weights.ts`, and log under "Unreleased" in each sibling's `CHANGELOG.md`.
diff --git a/README.md b/README.md
index b4fc752..305ad8a 100644
--- a/README.md
+++ b/README.md
@@ -193,7 +193,7 @@ GitLab and Bitbucket are implemented and score identically to a clone, but ship
## Companion: PR-diff GitHub Action
-[`hsnice16/agent-friendly-action`](https://github.com/hsnice16/agent-friendly-action) runs the same scorer inside your CI and posts a per-PR score-delta comment — _"this PR drops your Claude Code score by 4.1 points because it removed CI config."_ Opt-in via an `AGENTS_BADGE_TOKEN` secret; falls through silently when unset. Each repo detail page on the dashboard ships a copy-paste workflow snippet under "Catch score regressions on every PR".
+[`hsnice16/agent-friendly-action`](https://github.com/hsnice16/agent-friendly-action) runs the same scorer inside your CI and posts a per-PR score-delta comment — _"this PR drops your Claude Code score by 4.1 points because it removed CI config."_ Opt-in via an `AGENTS_BADGE_TOKEN` secret; falls through silently when unset. Each repo detail page on the dashboard ships a copy-paste workflow snippet under "Check the score on every pull request".
## Companion: agent skill
@@ -204,7 +204,7 @@ GitLab and Bitbucket are implemented and score identically to a clone, but ship
Read-only JSON endpoints for external integrators (skills, hooks, browser overlays, third-party tools):
- `GET /api/score?host=&repo=/` — look up an indexed repo by host + owner/name. Returns `{ repo, signals, modelScores }` on 200; `{ error: "not_indexed" }` with status 404 when the repo isn't in our DB. The natural lookup endpoint for any tool that has a repo URL but not our internal id.
-- `GET /api/repos` — full leaderboard (id, owner, name, host, stars, overall_score, per-model scores).
+- `GET /api/repos` — full leaderboard (each repo row plus its per-model scores).
- `GET /api/repo/` — per-repo detail (signals, model scores, top improvements). Requires the internal id; use `/api/score` first if you only have host + owner/name.
- `GET /api/badge///.svg` — embeddable SVG badge. `?model=` for per-model variants.
- `GET /api/package//` — resolve npm / PyPI / Cargo package → source-repo score (or `unresolved` when the registry doesn't expose a repo URL).
diff --git a/app/about/page.tsx b/app/about/page.tsx
index 572da5f..a38f8a6 100644
--- a/app/about/page.tsx
+++ b/app/about/page.tsx
@@ -17,7 +17,7 @@ export const metadata: Metadata = {
url: "/about",
type: "article",
},
- description: `Who built ${APP_NAME}, why it exists, and what it isn't. Independent, MIT-licensed, no affiliation with any AI agent vendor.`,
+ description: `Who built ${APP_NAME}, why, and what it is not. Independent, MIT-licensed, and not tied to any AI agent company.`,
};
const ABOUT_JSON_LD = {
@@ -59,50 +59,47 @@ export default function AboutPage() {
About
-
- Who built {APP_NAME}, why it exists, and what it deliberately isn't.
-
+
Who built {APP_NAME}, why, and what it is not.
Who
- Built and maintained by Himanshu Singh.
- Independent project — no affiliation with Anthropic, OpenAI, Google, Cognition, Anysphere, or any of the agent
- vendors ranked here.
+ Built and maintained by Himanshu Singh. It is
+ an independent project, not tied to Anthropic, OpenAI, Google, Cognition, Anysphere, or any other company
+ whose agent is ranked here.
- Why this exists
+ Why it exists
- The gap between “repo with a README” and “repo that actually helps an AI coding agent ship
- code” keeps widening, and there's no public way to tell who's doing the work. {APP_NAME}{" "}
- tries to make that visible — per model, because the agents aren't interchangeable. Claude Code wants an
- AGENTS.md and a fast test loop; Cursor wants strong types and a skim-readable README; Devin wants a runnable
- dev environment with declared deps and tests. The same repository can score very differently across them,
- and a single overall number would hide that.
+ A repo that has a README is not the same as a repo that really helps an AI coding agent get work done. That
+ gap keeps growing, and there is no public way to see which repos have done the work. {APP_NAME} tries to
+ show it, for each agent separately, because the agents are not the same. Claude Code wants an AGENTS.md and
+ fast tests. Cursor wants strong types and a README that is easy to skim. Devin wants a dev setup it can run,
+ with its dependencies and tests listed. One repo can score very differently for each of them. A single
+ number would hide that.
- What it isn't
+ What it is not
- This is not a benchmark of agent performance. Today every score is derived from{" "}
- static signals — file existence and content-length checks on the
- cloned tree. No agent is actually run. Per-model rationales are derived from each agent's published
- documentation (sources are linked on the methodology page), but the weight values themselves are still
- pre-benchmark — not yet calibrated against measured agent success. Read the{" "}
+ It is not a test of how well agents perform. Every score comes from{" "}
+ simple file checks: does a file exist, and how long is it. No agent is
+ actually run. The reasons behind each agent's weights come from that agent's own docs (linked on
+ the methodology page). But the weight numbers are not yet tested against how agents really perform. Read the{" "}
methodology
{" "}
- for the full picture, including the production-cut plan to replace pre-benchmark weights with measured ones.
+ for the details, including the plan to replace these weights with measured ones.
@@ -111,9 +108,9 @@ export default function AboutPage() {
Open source
- MIT-licensed. The signal definitions, weight profiles, scoring code, seed list, and every score in the
- database are all in the source repository. If a repo's
- score looks wrong, file an issue with a link and the rubric to revisit; if a signal is missing, propose one.
+ MIT-licensed. The checks, the weights, the scoring code, the list of repos, and every score are all in the{" "}
+ source repository. If a score looks wrong, open an issue with a
+ link and say which rule to look at again. If a check is missing, suggest one.
@@ -123,7 +120,8 @@ export default function AboutPage() {
Contact
- Best signal: open an issue or discussion on GitHub.
+ The best way to reach me: open an issue or discussion on{" "}
+ GitHub.
diff --git a/app/action/page.tsx b/app/action/page.tsx
index c1b0a64..fbf5c8e 100644
--- a/app/action/page.tsx
+++ b/app/action/page.tsx
@@ -14,9 +14,9 @@ import {
TWITTER_DEFAULTS,
} from "@/lib/version";
-const PAGE_TITLE = "Agent Friendly Action — PR-diff GitHub Action for AI agent-friendliness";
+const PAGE_TITLE = "Agent Friendly Action — check the AI agent score on every pull request";
const PAGE_DESCRIPTION =
- "GitHub Action that scores your PR's head and base in CI and posts a single comment with the agent-friendliness delta — Claude Code, Cursor, Devin, GPT-5 Codex, Gemini CLI, Kimi CLI, Aider, OpenHands, Pi. Opt-in, Marketplace-listed, runs entirely inside your CI.";
+ "A GitHub Action that scores each pull request and posts one comment showing how it changes the score for Claude Code, Cursor, Devin, GPT-5 Codex, Gemini CLI, Kimi CLI, Aider, OpenHands, and Pi. You turn it on yourself, it's on the Marketplace, and it runs only in your CI.";
const PAGE_KEYWORDS = [
...APP_KEYWORDS,
@@ -49,24 +49,24 @@ export const metadata: Metadata = {
const FAQ = [
{
- q: "What does the action do on a PR?",
- a: "It runs inside your CI (no third-party server in the loop), checks out the PR head and the base ref, scores both trees with the bundled scorer, and posts a single PR comment with the overall delta plus per-signal changes. On the next push it edits that same comment instead of creating a new one.",
+ q: "What does the action do on a pull request?",
+ a: "It runs in your own CI, with no outside server involved. It scores the pull request and the branch it targets, then posts one comment showing how the overall score changed and which checks changed. On the next push it updates that same comment instead of adding a new one.",
},
{
q: "Why is AGENTS_BADGE_TOKEN required?",
- a: "It's an opt-in marker, not an API credential. Set it (any non-empty string) to enable the comment; leave it unset and the action exits silently. This lets template / starter repos ship the workflow without it firing for forks or unenrolled installs.",
+ a: "It's an on switch, not a password or API key. Set it to any text to turn the comment on. Leave it unset and the action quietly does nothing. This lets template and starter repos include the workflow without it running in every fork or copy.",
},
{
- q: "Does it talk to your web app?",
- a: "No. The scorer and weights are bundled into the action's dist via @vercel/ncc. If this dashboard goes offline, the action keeps producing PR comments unchanged. A future version will optionally fetch fresh weights from /api/weights once benchmark-derived weights ship (1.0.0).",
+ q: "Does it contact this website?",
+ a: "No. The scoring code and weights are packed into the action itself (its dist folder, built with @vercel/ncc). If this site goes offline, the action keeps working the same way. Once weights are based on real tests (version 1.0.0), a later version may optionally download fresh weights from /api/weights.",
},
{
q: "Does it work on private repos?",
- a: "Yes — it runs in your CI under your existing GITHUB_TOKEN. The action never sends repo contents anywhere; the scoring is local to the runner.",
+ a: "Yes. It runs in your CI with your existing GITHUB_TOKEN. The action never sends your code anywhere. All scoring happens on the CI machine.",
},
{
q: "Where is the source?",
- a: "github.com/hsnice16/agent-friendly-action. MIT-licensed, semver-tagged. Pin @v0 to track the latest 0.x release; pin @v0.1.0 to opt out of automatic minor / patch updates.",
+ a: "github.com/hsnice16/agent-friendly-action. It's MIT-licensed and uses version tags. Use @v0 to always get the latest 0.x release. Use @v0.1.0 to stay on one exact version with no automatic updates.",
},
];
@@ -127,12 +127,12 @@ export default function ActionPage() {
- Agent Friendly Action — score every PR
+ Agent Friendly Action: score every pull request
- A GitHub Action that comments the agent-friendliness delta on every pull request — “this PR drops your
- Claude Code score by 4.1 points because it removed CI config”. Runs entirely inside your CI, no
- third-party server, opt-in via a single secret.
+ A GitHub Action that comments on every pull request with how it changes your score. For example: “this
+ PR drops your Claude Code score by 4.1 points because it removed CI config”. It runs only in your CI,
+ with no outside server, and you turn it on with one secret.
@@ -146,7 +146,7 @@ export default function ActionPage() {
AGENTS_BADGE_TOKEN
{" "}
- is present — it's an opt-in marker, not a credential. Add it once and forget it:
+ is set. It's an on switch, not a password. Add it once and you're done:
@@ -161,19 +161,19 @@ export default function ActionPage() {
AGENTS_BADGE_TOKEN
- . Value: any non-empty string —{" "}
+ . Value: any text, for example{" "}
enabled
{" "}
- works. The string itself isn't checked.
+ . The value itself isn't checked.
- Click Add secret. The action fires on the next PR.
+ Click Add secret. The action runs on your next pull request.
- Authoritative reference:{" "}
+ GitHub's guide:{" "}
docs.github.com — encrypted secrets
@@ -190,15 +190,18 @@ export default function ActionPage() {
actions/checkout
{" "}
- with fetch-depth: 0 gives the action the PR head and access to base
- history.
+ with fetch-depth: 0 gives the action the pull request and the
+ history of the branch it targets.
-
The action fetches the base ref locally and checks it out into a worktree — no network clone.
-
Scores both trees with the bundled scorer and computes the overall + per-signal + per-model delta.
+
+ The action checks out the target branch from that history, fetching just that commit if it is missing. No
+ full clone.
+
+
It scores both versions and works out what changed: overall, for each check, and for each agent.
- Posts (or edits) a single PR comment marked with{" "}
+ It posts one comment on the pull request (or updates it), marked with{" "}
{""}
@@ -210,10 +213,10 @@ export default function ActionPage() {
- Self-contained by design
+ Works on its own
- The scorer and weights are bundled into{" "}
+ The scoring code and weights are packed into{" "}
dist/
{" "}
@@ -221,13 +224,13 @@ export default function ActionPage() {
@vercel/ncc
- . If {APP_NAME} goes offline tomorrow, the action keeps producing PR comments unchanged. Runtime weight
- refresh from a future{" "}
+ . If {APP_NAME} goes offline tomorrow, the action keeps working the same way. Downloading fresh weights from
+ a future{" "}
/api/weights
{" "}
- endpoint is deferred to v1.0.0 once benchmark-derived weights ship — until then both the head and the base
- are scored with the same bundled weights, so any drift cancels in the diff.
+ endpoint waits until v1.0.0, when weights will be based on real tests. Until then, both versions are scored
+ with the same weights, so the comparison stays fair.
@@ -255,7 +258,8 @@ export default function ActionPage() {
{ACTION_REPO_URL.replace(/^https:\/\//, "")}
{" "}
- — MIT-licensed, semver-tagged. Listed on the GitHub Marketplace under Code Quality / Continuous Integration.
+ — MIT-licensed, with version tags. Listed on the GitHub Marketplace under Code Quality / Continuous
+ Integration.
diff --git a/app/layout.tsx b/app/layout.tsx
index fa44ea4..026c151 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -180,8 +180,8 @@ export default function RootLayout({ children }: { children: React.ReactNode })