Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"hooks": [
{
"type": "command",
"command": "printf '\\n📦 Agent Friendly Code — current release: 0.3.0\\n • Read AGENTS.md for conventions, CONTRIBUTING.md for the PR workflow.\\n • Roadmap: 0.4.0 (quick wins — history-aware signals + PR score-diff + Claude Code skill) → 0.5.0 (auto-refresh + smarter matching) → 0.6.0 (maintainer ownership + at-scale discovery) → 1.0.0 (production cut — Postgres + at-scale indexing + benchmark harness).\\n • Changelog rule: user-facing capabilities only. Codebase hygiene (CI / linter / tests / CONTRIBUTING) does NOT go in lib/changelog.ts.\\n'"
"command": "printf '\\n📦 Agent Friendly Code — current release: 0.4.0\\n • Read AGENTS.md for conventions, CONTRIBUTING.md for the PR workflow.\\n • Roadmap: 0.5.0 (quick wins — history-aware signals + PR score-diff + Claude Code skill) → 0.6.0 (auto-refresh + smarter matching — webhook rescoring + alternatives v2) → 0.7.0 (maintainer ownership + at-scale discovery — OAuth opt-out + package overlay at scale) → 1.0.0 (production cut — Postgres + at-scale indexing + benchmark harness).\\n • Changelog rule: user-facing capabilities only. Codebase hygiene (CI / linter / tests / CONTRIBUTING) does NOT go in lib/changelog.ts.\\n'"
}
]
}
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/code-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Only `@phosphor-icons/react`. Block Lucide, Heroicons, React Icons, inline SVG,
## Security

- Parameterised SQL only.
- `dangerouslySetInnerHTML` is allowed only for the existing server-built JSON-LD scripts (`app/layout.tsx`, `app/page.tsx`, `app/repo/[id]/page.tsx`, `app/package/[registry]/[name]/page.tsx`) with the `<` → `<` escape preserved. Reject any new use.
- `dangerouslySetInnerHTML` is allowed only for the existing server-built JSON-LD scripts (`app/layout.tsx`, `app/page.tsx`, `app/methodology/page.tsx`, `app/repo/[id]/page.tsx`, `app/package/[registry]/[name]/page.tsx`) with the `<` → `<` escape preserved. Reject any new use.
- External links include `rel="noopener noreferrer"`.
- Never execute code from a cloned repo.

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/quality-check/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Run the four checks below on any diff affecting UI or I/O. Report findings group
## Security

- **SQL parameterisation**: every query uses `?` placeholders. No string concatenation.
- **`dangerouslySetInnerHTML`** is allowed only for server-built JSON-LD (`app/layout.tsx`, `app/page.tsx`, `app/repo/[id]/page.tsx`, `app/package/[registry]/[name]/page.tsx`) and must keep the `<` → `<` escape. Any other use must be rejected.
- **`dangerouslySetInnerHTML`** is allowed only for server-built JSON-LD (`app/layout.tsx`, `app/page.tsx`, `app/methodology/page.tsx`, `app/repo/[id]/page.tsx`, `app/package/[registry]/[name]/page.tsx`) and must keep the `<` → `<` escape. Any other use must be rejected.
- **External URLs** in `<a target="_blank">` always include `rel="noopener noreferrer"`.
- **User input at every boundary** is validated: `parseRepoUrl` for repo URLs, `Number.isFinite` for numeric params, length caps on search strings.
- **Clone safety**: `git clone --depth 1 --single-branch`; never execute code from a clone (no `bun install`, no `npm install`, no post-clone scripts).
Expand Down
22 changes: 13 additions & 9 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,17 @@ app/
page.tsx # leaderboard
repo/[id]/page.tsx # repo detail with per-model suggestions (includes generateMetadata)
methodology/page.tsx # how the static scoring works
about/page.tsx # who built this and why (footer-linked, E-E-A-T)
roadmap/page.tsx # upcoming versions (from lib/roadmap.ts)
changelog/page.tsx # what's in this build (from lib/changelog.ts)
robots.ts # /robots.txt — allows "/", disallows "/api/"
sitemap.ts # /sitemap.xml — static routes + every repo detail page
robots.ts # /robots.txt — wildcard + explicit AI-crawler allows
sitemap.ts # /sitemap.xml — static routes + every repo detail page (priority scaled by score)
llms.txt/route.ts # /llms.txt — markdown manifest for LLM crawlers (Perplexity, Claude, ChatGPT search)
api/repos/route.ts
api/repo/[id]/route.ts
api/badge/[host]/[owner]/[name]/route.ts # SVG badge for README embeds (?model=<id> for per-model)
api/package/[registry]/[name]/route.ts # npm/PyPI/Cargo lookup → source-repo score
repo/[id]/opengraph-image.tsx # next/og convention — per-repo OG image (auto-wired)
package/page.tsx # explainer + try-it examples
package/[registry]/[name]/page.tsx # scored | not_scored | unresolved states
globals.css # Tailwind import + @theme tokens (no custom utilities)
Expand Down Expand Up @@ -81,7 +84,7 @@ lib/
changelog.ts # typed ChangelogEntry[]
roadmap.ts # typed RoadmapVersion[]
scripts/
init-db.ts, score.ts, seed.ts, seed-list.ts
init-db.ts, score.ts, seed.ts, seed-list.ts, seed-packages.ts (auto-runs after seed.ts)
tests/
_helpers.ts # makeFixture / removeFixture build synthetic trees under os.tmpdir()
format.test.ts # compactStars, relativeTime, hostLabel
Expand All @@ -93,9 +96,10 @@ tasks/
0.1.0/ # released — shipped record
0.2.0/ # released — dogfood complete (tests, self-score, row-click)
0.3.0/ # released — embeddable scores + broader coverage (badge, more agents, alternatives, package lookup)
0.4.0/ # planned — quick wins (history-aware signals + PR score-diff action + Claude Code skill)
0.5.0/ # planned — auto-refresh + smarter matching (webhook rescoring + alternatives v2)
0.6.0/ # planned — maintainer ownership + at-scale discovery (OAuth opt-out + package overlay at scale)
0.4.0/ # released — credible scores + discoverability (docs-cited rationales + agent-specific signals + About/llms.txt/OG)
0.5.0/ # planned — quick wins (history-aware signals + PR score-diff action + Claude Code skill)
0.6.0/ # planned — auto-refresh + smarter matching (webhook rescoring + alternatives v2)
0.7.0/ # planned — maintainer ownership + at-scale discovery (OAuth opt-out + package overlay at scale)
1.0.0/ # planned — production cut (Postgres + at-scale indexing + benchmark harness)
.claude/
settings.json # SessionStart + Stop hooks (Stop → hooks/stop-guard.sh)
Expand Down Expand Up @@ -176,9 +180,9 @@ Hooks docs: <https://docs.claude.com/en/docs/claude-code/hooks.html>.

- We `git clone --depth 1 --single-branch` arbitrary URLs — safe by default. We never run post-clone scripts, never `npm install`, never execute code from the clone.
- SQL: all queries parameterised. No interpolation.
- HTML: React auto-escapes. The only `dangerouslySetInnerHTML` is server-built JSON-LD with `<` escaped to `<` (`app/layout.tsx`, `app/page.tsx`, `app/repo/[id]/page.tsx`, `app/package/[registry]/[name]/page.tsx`); never feed user-controlled strings into it.
- HTML: React auto-escapes. The only `dangerouslySetInnerHTML` is server-built JSON-LD with `<` escaped to `<` (`app/layout.tsx`, `app/page.tsx`, `app/methodology/page.tsx`, `app/repo/[id]/page.tsx`, `app/package/[registry]/[name]/page.tsx`); never feed user-controlled strings into it.
- Local-path mode reads files; never writes outside `data/` and the clone workspace passed to `shallowClone`.
- No auth yet (read-only dashboard). When auth lands (`tasks/0.6.0/01-opt-out-claim-flow.md`), do it via OAuth and gate DB writes per user.
- No auth yet (read-only dashboard). When auth lands (`tasks/0.7.0/01-opt-out-claim-flow.md`), do it via OAuth and gate DB writes per user.

**Operational concerns** (not code-level security) worth flagging before public launch:

Expand All @@ -188,7 +192,7 @@ Hooks docs: <https://docs.claude.com/en/docs/claude-code/hooks.html>.

## Things to leave alone

- Per-model weights are illustrative. Don't tune without `tasks/1.0.0/03-benchmark-harness.md`.
- Per-model rationales are derived from each agent's published documentation (see `MODELS[].sources` in `lib/scoring/weights.ts`); the weights themselves are still pre-benchmark. Do not tune individual values without re-running the docs audit (see `tasks/0.4.0/01-sourced-agent-rationales.md`) or shipping the v1.0.0 benchmark harness.
- SQLite schema is intentionally simple. Flag before restructuring.
- The I/O boundary. Scoring stays pure; DB stays in `lib/db.ts`.
- `APP_VERSION` — don't bump without a release.
Expand Down
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Agent Friendly Code

[![Release](https://img.shields.io/badge/release-0.3.0-blue?style=flat-square)](./lib/changelog.ts)
[![Release](https://img.shields.io/badge/release-0.4.0-blue?style=flat-square)](./lib/changelog.ts)
[![License: MIT](https://img.shields.io/badge/license-MIT-green?style=flat-square)](./LICENSE)
[![Next.js 16](https://img.shields.io/badge/Next.js-16-black?style=flat-square)](https://nextjs.org)
[![Node ≥20.9](https://img.shields.io/badge/node-%E2%89%A520.9-43853d?style=flat-square&logo=node.js&logoColor=white)](https://nodejs.org)
Expand All @@ -9,7 +9,7 @@

**A public dashboard that ranks open-source repos by how friendly they are for AI coding agents — per model.**

Next.js 16 + SQLite (`better-sqlite3`), styled with Tailwind CSS 4. Spans GitHub, GitLab, and Bitbucket out of the box. Current release: **0.3.0**.
Next.js 16 + SQLite (`better-sqlite3`), styled with Tailwind CSS 4. Spans GitHub, GitLab, and Bitbucket out of the box. Current release: **0.4.0**.

![Agent Friendly Code — leaderboard](./public/demo/light.png)

Expand Down Expand Up @@ -60,9 +60,9 @@ Two audiences:

Not pretending the idea is free of risk:

- **Per-model scoring is the hardest part and the easiest to fake.** Today the weights are illustrative. Real "Claude ranks this higher than GPT-5" requires actually running each agent on each repo. That's `tasks/1.0.0/03-benchmark-harness.md`.
- **Per-model scoring is the hardest part and the easiest to fake.** Per-model rationales are now sourced from each agent's published docs (see `MODELS[].sources` in `lib/scoring/weights.ts`), but the weight values themselves are still pre-benchmark. Real "Claude ranks this higher than GPT-5" requires actually running each agent on each repo. That's `tasks/1.0.0/03-benchmark-harness.md`.
- **Factory.ai is already in this space.** Differentiation has to stay sharp.
- **Public-shaming risk.** Ranking #47,823 without consent invites angry maintainers. Planned via `tasks/0.6.0/01-opt-out-claim-flow.md`.
- **Public-shaming risk.** Ranking #47,823 without consent invites angry maintainers. Planned via `tasks/0.7.0/01-opt-out-claim-flow.md`.
- **Score gaming.** Once public, people add boilerplate `AGENTS.md` to pass the rubric without being useful. Dynamic (actually-run-an-agent) checks are the counter — see benchmark harness.
- **Freshness.** Scores decay with every push. Webhook-driven rescoring is roadmap.

Expand All @@ -84,14 +84,14 @@ Short answer: **low risk**. The app:
- Rate limiting the public API.
- Sandbox the cloner in a container (future-proofing against hypothetical git CVEs).

Auth and per-maintainer controls land with the opt-out / claim flow in v0.6.0.
Auth and per-maintainer controls land with the opt-out / claim flow in v0.7.0.

## Quickstart

```bash
bun install
bun run prepare-hooks # once — installs lefthook pre-commit (Biome + tsc + test + file-length)
bun run seed # score the curated set across GH / GL / BB
bun run seed # score the curated set across GH / GL / BB + cache popular package aliases
bun run dev # http://localhost:3000
```

Expand All @@ -110,7 +110,7 @@ Run the unit tests with `bun run test` (uses `node --test` + `tsx`; requires Nod

## Versioning

`lib/version.ts` and `package.json` carry the current release number (currently **0.3.0**). Bumps happen only when we actually cut a release — never when merging intermediate work. The version pill in the header surfaces the number directly; `/changelog` lists what each release shipped.
`lib/version.ts` and `package.json` carry the current release number (currently **0.4.0**). Bumps happen only when we actually cut a release — never when merging intermediate work. The version pill in the header surfaces the number directly; `/changelog` lists what each release shipped.

## Stack & rationale

Expand Down Expand Up @@ -172,9 +172,9 @@ See `/roadmap` in the running app or the per-version `tasks/` folders for the fu

Versions are sequenced cheap-first so the highest-impact small additions don't get gated on heavy infra:

- **0.4.0 — quick wins**: history-aware signals (maintenance recency, commit velocity, contributor activity) + a GitHub Action that comments the score delta on every PR + a Claude Code skill (with public `/api/score` lookup) that recommends a model for the active repo. No new infra.
- **0.5.0 — auto-refresh + smarter matching**: webhook-driven rescoring (keep scores fresh on every push) + alternatives via README embeddings (cross-language matches the v0.3.0 SQL heuristic misses).
- **0.6.0 — maintainer ownership + at-scale discovery**: OAuth opt-out / claim flow for maintainers + at-scale package overlay (per-registry leaderboards + userscript that renders the badge inline on npmjs.com / PyPI / crates.io).
- **0.5.0 — quick wins**: history-aware signals (maintenance recency, commit velocity, contributor activity) + a GitHub Action that comments the score delta on every PR + a Claude Code skill (with public `/api/score` lookup) that recommends a model for the active repo. No new infra.
- **0.6.0 — auto-refresh + smarter matching**: webhook-driven rescoring (keep scores fresh on every push) + alternatives via README embeddings (cross-language matches the v0.3.0 SQL heuristic misses).
- **0.7.0 — maintainer ownership + at-scale discovery**: OAuth opt-out / claim flow for maintainers + at-scale package overlay (per-registry leaderboards + userscript that renders the badge inline on npmjs.com / PyPI / crates.io).
- **1.0.0 — production cut**: Postgres migration for concurrent writers + auto-discovered crawl (target 10k repos) + benchmark harness that derives per-model weights from measured agent success. From here on, breaking API changes require a MAJOR bump.

## Defensibility
Expand Down
116 changes: 116 additions & 0 deletions app/about/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import type { Metadata } from "next";
import Link from "next/link";

import { Panel, PanelHeading } from "@/components/Panel";
import { APP_NAME, REPO_URL } from "@/lib/version";

export const metadata: Metadata = {
title: "About",
alternates: { canonical: "/about" },
twitter: { title: `About — ${APP_NAME}` },
openGraph: { title: `About — ${APP_NAME}`, url: "/about" },
description: `Who built ${APP_NAME}, why it exists, and what it isn't. Independent, MIT-licensed, no affiliation with any AI agent vendor.`,
};

export default function AboutPage() {
return (
<>
<section className="my-3 mb-7">
<h1 className="mb-2.5 text-[30px] font-bold leading-[1.18] tracking-tight">About</h1>
<p className="m-0 max-w-[72ch] text-[15.5px] text-ink-dim">
Who built {APP_NAME}, why it exists, and what it deliberately isn&apos;t.
</p>
</section>

<Panel>
<PanelHeading>Who</PanelHeading>
<p className="m-0 text-[14.5px] leading-relaxed text-ink-dim">
Built and maintained by{" "}
<a
target="_blank"
rel="noopener noreferrer"
href="https://github.com/hsnice16"
className="border-b border-dotted border-ink-dim/60 text-ink-dim hover:border-ink-soft hover:text-ink-soft"
>
Himanshu Singh
</a>
. Independent project — no affiliation with Anthropic, OpenAI, Google, Cognition, Anysphere, or any of the
agent vendors ranked here.
</p>
</Panel>

<div className="mt-3.5">
<Panel>
<PanelHeading>Why this exists</PanelHeading>
<p className="m-0 text-[14.5px] leading-relaxed text-ink-dim">
The gap between &ldquo;repo with a README&rdquo; and &ldquo;repo that actually helps an AI coding agent ship
code&rdquo; keeps widening, and there&apos;s no public way to tell who&apos;s doing the work. {APP_NAME}{" "}
tries to make that visible — per model, because the agents aren&apos;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.
</p>
</Panel>
</div>

<div className="mt-3.5">
<Panel>
<PanelHeading>What it isn&apos;t</PanelHeading>
<p className="m-0 text-[14.5px] leading-relaxed text-ink-dim">
This is not a benchmark of agent performance. Today every score is derived from{" "}
<strong className="text-ink">static signals</strong> — file existence and content-length checks on the
cloned tree. No agent is actually run. Per-model rationales are derived from each agent&apos;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{" "}
<Link
href="/methodology"
className="border-b border-dotted border-ink-dim/60 text-ink-dim hover:border-ink-soft hover:text-ink-soft"
>
methodology
</Link>{" "}
for the full picture, including the production-cut plan to replace pre-benchmark weights with measured ones.
</p>
</Panel>
</div>

<div className="mt-3.5">
<Panel>
<PanelHeading>Open source</PanelHeading>
<p className="m-0 text-[14.5px] leading-relaxed text-ink-dim">
MIT-licensed. The signal definitions, weight profiles, scoring code, seed list, and every score in the
database are all in the{" "}
<a
href={REPO_URL}
target="_blank"
rel="noopener noreferrer"
className="border-b border-dotted border-ink-dim/60 text-ink-dim hover:border-ink-soft hover:text-ink-soft"
>
source repository
</a>
. If a repo&apos;s score looks wrong, file an issue with a link and the rubric to revisit; if a signal is
missing, propose one.
</p>
</Panel>
</div>

<div className="mt-3.5">
<Panel>
<PanelHeading>Contact</PanelHeading>

<p className="m-0 text-[14.5px] leading-relaxed text-ink-dim">
Best signal: open an issue or discussion on{" "}
<a
target="_blank"
rel="noopener noreferrer"
href={`${REPO_URL}/issues`}
className="border-b border-dotted border-ink-dim/60 text-ink-dim hover:border-ink-soft hover:text-ink-soft"
>
GitHub
</a>
.
</p>
</Panel>
</div>
</>
);
}
Loading
Loading