Skip to content

Commit 5dbce4a

Browse files
hsnice16claude
andauthored
feat: version 0.5.0 (#5)
* feat: 0.5.0 task 02 - GitHub Action for score * feat: 0.5.0 task 03 - agent skill Ship the portable agent skill that scores the user's current repo locally and recommends a model class. Self-contained: vendored scorer, no service dependency, works offline — same property as task 02's PR-diff Action. - Sibling repo `agent-friendly-skill` (v0.1.1, floats `v0`) ships SKILL.md + ncc-bundled dist/index.js. Vendored into this repo via `npx skills add hsnice16/agent-friendly-skill#v0`; skills-lock.json pins ref + content hash so the bundle is reproducible. - New /skill SEO landing page: install command, score → model mapping, optional SessionStart hook snippets for Claude Code + Codex, FAQ, and JSON-LD (BreadcrumbList + SoftwareApplication + FAQPage). - README "Public API" section documents /api/score for external integrators (siblings vendor the scorer; they don't call this at runtime). - AGENTS.md "Sibling repos" mirror discipline now covers both action and skill; SIBLING_VERSION in lib/version.ts is the single-source pin that drives ACTION_USES, SKILL_INSTALL_CMD, and the #v0 ref in skills-lock.json. - Layout footer reorganised into Primary + Tools columns; nav surfaces /action and /skill; sitemap and /llms.txt include /skill. - Panel + CopySnippet gain a tone vocabulary (warn / info / tip) so the install card, hook snippets, and per-page CTAs share styling. - Bundled along: gitignore-aware size signal (uses ignore@7.0.5), tests for both .gitignore patterns and the baseline ignore set. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: version 0.5.0 Release prep: bump version (lib/version.ts, package.json, README badge), add changelog entry summarising the score-diff GitHub Action and the portable agent skill, mark tasks/0.5.0 done and drop the descoped history-aware-signals task, prune 0.5.0 from the live roadmap. Also lands SEO breadcrumbs on About/Changelog/Methodology/Packages/Roadmap via a shared BreadcrumbJsonLd component, extends sitemap with package routes, surfaces top scored packages per registry in /llms.txt, and refreshes the SessionStart hook + cross-repo mirroring note on the feature-request template. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent feda27d commit 5dbce4a

21 files changed

Lines changed: 162 additions & 102 deletions

File tree

.claude/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"hooks": [
77
{
88
"type": "command",
9-
"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 + agent 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'"
9+
"command": "printf '\\n📦 Agent Friendly Code — current release: 0.5.0\\n • Read AGENTS.md for conventions, CONTRIBUTING.md for the PR workflow.\\n • Roadmap: 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'"
1010
}
1111
]
1212
}

.github/ISSUE_TEMPLATE/feature_request.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ _Tick whichever applies:_
2828

2929
_Tick whichever applies:_
3030

31-
- [ ] **New signal** — see "Adding a signal" in [AGENTS.md](../../AGENTS.md). Mirrors to the sibling `agent-friendly-action` repo.
32-
- [ ] **New model** — see "Adding a model" in [AGENTS.md](../../AGENTS.md). Mirrors to the sibling repo.
31+
- [ ] **New signal** — see "Adding a signal" in [AGENTS.md](../../AGENTS.md). Mirrors to **both** sibling repos (`agent-friendly-action` and `agent-friendly-skill`).
32+
- [ ] **New model** — see "Adding a model" in [AGENTS.md](../../AGENTS.md). Mirrors to **both** sibling repos.
3333
- [ ] **New host** — see "Adding a host" in [AGENTS.md](../../AGENTS.md).
3434
- [ ] **New / changed API route**`/api/...`. Note breaking-change implications for action + agent-skill consumers.
3535
- [ ] **New page or UI surface**.

AGENTS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ tasks/
105105
0.2.0/ # released — dogfood complete (tests, self-score, row-click)
106106
0.3.0/ # released — embeddable scores + broader coverage (badge, more agents, alternatives, package lookup)
107107
0.4.0/ # released — credible scores + discoverability (docs-cited rationales + agent-specific signals + About/llms.txt/OG)
108-
0.5.0/ # planned — quick wins (history-aware signals + PR score-diff action + agent skill)
108+
0.5.0/ # released — quick wins (PR score-diff action + agent skill)
109109
0.6.0/ # planned — auto-refresh + smarter matching (webhook rescoring + alternatives v2)
110110
0.7.0/ # planned — maintainer ownership + at-scale discovery (OAuth opt-out + package overlay at scale)
111111
1.0.0/ # planned — production cut (Postgres + at-scale indexing + benchmark harness)
@@ -212,7 +212,7 @@ Hooks docs: <https://docs.claude.com/en/docs/claude-code/hooks.html>.
212212

213213
- 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.
214214
- SQL: all queries parameterised. No interpolation.
215-
- HTML: React auto-escapes. The only `dangerouslySetInnerHTML` is server-built JSON-LD with `<` escaped to `<` (`app/layout.tsx`, `app/page.tsx`, `app/action/page.tsx`, `app/skill/page.tsx`, `app/methodology/page.tsx`, `app/repo/[id]/page.tsx`, `app/package/[registry]/[name]/page.tsx`); never feed user-controlled strings into it.
215+
- HTML: React auto-escapes. The only `dangerouslySetInnerHTML` is server-built JSON-LD with `<` escaped to `<` (`app/layout.tsx`, `app/page.tsx`, `app/about/page.tsx`, `app/action/page.tsx`, `app/skill/page.tsx`, `app/methodology/page.tsx`, `app/package/[registry]/[name]/page.tsx`, `app/repo/[id]/page.tsx`, plus the `BreadcrumbJsonLd` component used by About / Changelog / Methodology / Packages / Roadmap); never feed user-controlled strings into it.
216216
- Local-path mode reads files; never writes outside `data/` and the clone workspace passed to `shallowClone`.
217217
- 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.
218218

README.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Agent Friendly Code
22

3-
[![Release](https://img.shields.io/badge/release-0.4.0-blue?style=flat-square)](./lib/changelog.ts)
3+
[![Release](https://img.shields.io/badge/release-0.5.0-blue?style=flat-square)](./lib/changelog.ts)
44
[![License: MIT](https://img.shields.io/badge/license-MIT-green?style=flat-square)](./LICENSE)
55
[![Next.js 16](https://img.shields.io/badge/Next.js-16-black?style=flat-square)](https://nextjs.org)
66
[![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)
@@ -9,7 +9,7 @@
99

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

12-
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**.
12+
Next.js 16 + SQLite (`better-sqlite3`), styled with Tailwind CSS 4. Spans GitHub, GitLab, and Bitbucket out of the box. Current release: **0.5.0**.
1313

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

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

111111
## Versioning
112112

113-
`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.
113+
`lib/version.ts` and `package.json` carry the current release number (currently **0.5.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.
114114

115115
## Stack & rationale
116116

@@ -121,7 +121,7 @@ Run the unit tests with `bun run test` (uses `node --test` + `tsx`; requires Nod
121121
| **Tailwind CSS 4** | Zero-config via `@theme` tokens, no `tailwind.config.*` needed. Tight bundle output. | Would only leave for something with a stronger design-system story. |
122122
| **`better-sqlite3`** | Single file, inspectable, zero ops overhead. Node-native so Vercel's serverless runtime can load it directly. | Postgres when concurrent writers / access control arrive (`tasks/1.0.0/01-postgres-migration.md`). |
123123
| **Server-first; client islands where needed** | Cheap, fast, SEO-friendly. Client components only where interactivity demands — mobile nav, search, selects, copy, back-to-top. | When client islands grow past presentational interactivity → reach for client state mgmt. |
124-
| **Shallow git clones** (`--depth 1 --single-branch`) | Bandwidth + speed. Current signals don't need history. | History-aware signals → host APIs or `--filter=blob:none` partial clones. |
124+
| **Shallow git clones** (`--depth 1 --single-branch`) | Bandwidth + speed. Current signals don't need history. | If commit-history ever enters scoring → host APIs or `--filter=blob:none` partial clones. |
125125
| **Exact-pinned deps** | Deterministic scoring across environments. | Never. |
126126
| **One file per signal** | Each signal is a small, independent concern — keeps `git log` and code review focused. | When we bundle signals into dynamic checks (then the unit becomes the bundle). |
127127

@@ -193,7 +193,6 @@ See `/roadmap` in the running app or the per-version `tasks/` folders for the fu
193193

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

196-
- **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 portable agent skill (profiles 8 agents — Claude Code, Cursor, Devin, GPT-5 Codex, Gemini CLI, Aider, OpenHands, Pi; installs into any vercel-labs/skills host) that scores the user's current repo locally and recommends a model. Skill ships as a sibling repo with the scorer vendored — same self-contained property as the action. No new infra.
197196
- **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).
198197
- **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).
199198
- **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.

app/about/page.tsx

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,55 @@
11
import { ArrowUpRight } from "@phosphor-icons/react/dist/ssr";
22
import type { Metadata } from "next";
33
import Link from "next/link";
4+
import { BreadcrumbJsonLd } from "@/components/BreadcrumbJsonLd";
45
import { Panel, PanelHeading } from "@/components/Panel";
5-
import { APP_NAME, REPO_URL } from "@/lib/version";
6+
import { APP_NAME, APP_URL, REPO_URL } from "@/lib/version";
67

78
export const metadata: Metadata = {
89
title: "About",
910
alternates: { canonical: "/about" },
1011
twitter: { title: `About — ${APP_NAME}` },
11-
openGraph: { title: `About — ${APP_NAME}`, url: "/about" },
12+
openGraph: { title: `About — ${APP_NAME}`, url: "/about", type: "article" },
1213
description: `Who built ${APP_NAME}, why it exists, and what it isn't. Independent, MIT-licensed, no affiliation with any AI agent vendor.`,
1314
};
1415

16+
const ABOUT_JSON_LD = {
17+
"@context": "https://schema.org",
18+
"@graph": [
19+
{
20+
"@id": `${APP_URL}/about#author`,
21+
"@type": "Person",
22+
name: "Himanshu Singh",
23+
jobTitle: "Software Engineer",
24+
url: "https://github.com/hsnice16",
25+
worksFor: { "@id": `${APP_URL}/#org` },
26+
sameAs: ["https://github.com/hsnice16", "https://github.com/sponsors/hsnice16"],
27+
},
28+
{
29+
"@id": `${APP_URL}/about#page`,
30+
"@type": "AboutPage",
31+
url: `${APP_URL}/about`,
32+
name: `About — ${APP_NAME}`,
33+
author: { "@id": `${APP_URL}/about#author` },
34+
isPartOf: { "@id": `${APP_URL}/#site` },
35+
mainEntity: { "@id": `${APP_URL}/about#author` },
36+
},
37+
],
38+
};
39+
1540
export default function AboutPage() {
1641
return (
1742
<>
43+
<BreadcrumbJsonLd current={{ name: "About", path: "/about" }} />
44+
45+
<script
46+
type="application/ld+json"
47+
// biome-ignore lint/security/noDangerouslySetInnerHtml: server-built JSON-LD; `<` is escaped
48+
dangerouslySetInnerHTML={{
49+
__html: JSON.stringify(ABOUT_JSON_LD).replace(/</g, "\\u003c"),
50+
}}
51+
/>
52+
1853
<section className="my-3 mb-7">
1954
<h1 className="mb-2.5 text-[30px] font-bold leading-[1.18] tracking-tight">About</h1>
2055
<p className="m-0 max-w-[72ch] text-[15.5px] text-ink-dim">

app/changelog/page.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
11
import type { Metadata } from "next";
22
import Link from "next/link";
33

4+
import { BreadcrumbJsonLd } from "@/components/BreadcrumbJsonLd";
45
import { Panel } from "@/components/Panel";
56
import { CHANGELOG } from "@/lib/changelog";
67

78
export const metadata: Metadata = {
89
title: "Changelog",
910
twitter: { title: "Changelog" },
1011
alternates: { canonical: "/changelog" },
11-
openGraph: { title: "Changelog", url: "/changelog" },
12+
openGraph: { title: "Changelog", url: "/changelog", type: "article" },
1213
description:
1314
"What's shipped in each release of Agent Friendly Code — user-facing capabilities, not internal churn. Every bullet corresponds to a roadmap item that landed.",
1415
};
1516

1617
export default function ChangelogPage() {
1718
return (
1819
<>
20+
<BreadcrumbJsonLd current={{ name: "Changelog", path: "/changelog" }} />
21+
1922
<section className="my-3 mb-7">
2023
<h1 className="mb-2.5 text-[30px] font-bold leading-[1.18] tracking-tight">Changelog</h1>
2124

@@ -41,10 +44,13 @@ export default function ChangelogPage() {
4144
<span className="tabular-nums text-sm text-muted">{entry.date}</span>
4245
</div>
4346

44-
<ul className="m-0 pl-5 text-[14.5px] text-ink-dim">
47+
<ul className="m-0 list-none p-0 text-[14.5px] text-ink-dim">
4548
{entry.highlights.map((h) => (
46-
<li key={h} className="my-1">
47-
{h}
49+
<li key={h} className="my-1 flex gap-2">
50+
<span aria-hidden="true" className="select-none text-line">
51+
·
52+
</span>
53+
<span>{h}</span>
4854
</li>
4955
))}
5056
</ul>

app/llms.txt/route.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { REGISTRIES } from "@/lib/clients/registries";
2+
import { getTopPackagesByRegistry } from "@/lib/db";
13
import { MODELS } from "@/lib/scoring/weights";
24
import {
35
ACTION_REPO_URL,
@@ -16,7 +18,22 @@ const HEADERS = {
1618
"Cache-Control": "public, max-age=3600, s-maxage=86400",
1719
};
1820

21+
const LLMS_TOP_PACKAGES_PER_REGISTRY = 10;
22+
1923
export function GET(): Response {
24+
const topPackagesSection = REGISTRIES.map((registry) => {
25+
const rows = getTopPackagesByRegistry(registry, LLMS_TOP_PACKAGES_PER_REGISTRY);
26+
if (rows.length === 0) {
27+
return "";
28+
}
29+
const lines = rows.map(
30+
(p) => `- [${registry}/${p.name}](${APP_URL}/package/${registry}/${p.name}): ${p.score.toFixed(1)} / 100`,
31+
);
32+
return `### ${registry}\n\n${lines.join("\n")}`;
33+
})
34+
.filter(Boolean)
35+
.join("\n\n");
36+
2037
const body = `# ${APP_NAME}
2138
2239
> ${APP_DESCRIPTION}
@@ -52,6 +69,10 @@ ${MODELS.map((m) => `- ${m.label} — ${m.rationale}`).join("\n")}
5269
- [Agent Friendly Action](${ACTION_REPO_URL}): GitHub Action that posts a per-PR score-delta comment — runs entirely inside the maintainer's CI, opt-in via \`AGENTS_BADGE_TOKEN\`, listed on the GitHub Marketplace
5370
- [Agent Friendly Skill](${SKILL_REPO_URL}): Portable agent skill (Claude Code, Codex, Cursor, Cline, Copilot, …) installable via \`${SKILL_INSTALL_CMD}\` — scores the user's current repo locally, no service dependency
5471
72+
## Top scored packages
73+
74+
${topPackagesSection || "_No packages indexed yet._"}
75+
5576
## Source
5677
5778
- [Repository](${REPO_URL}): MIT-licensed Next.js app

app/methodology/page.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ArrowUpRight } from "@phosphor-icons/react/dist/ssr";
22
import type { Metadata } from "next";
33
import Link from "next/link";
44

5+
import { BreadcrumbJsonLd } from "@/components/BreadcrumbJsonLd";
56
import { Panel, PanelHeading } from "@/components/Panel";
67
import { SIGNALS } from "@/lib/scoring/signals";
78
import { MODELS } from "@/lib/scoring/weights";
@@ -10,7 +11,7 @@ export const metadata: Metadata = {
1011
title: "Methodology",
1112
twitter: { title: "Methodology" },
1213
alternates: { canonical: "/methodology" },
13-
openGraph: { title: "Methodology", url: "/methodology" },
14+
openGraph: { title: "Methodology", url: "/methodology", type: "article" },
1415
description:
1516
"How scores are computed today: the signals checked, the per-model weight profiles, the scoring formula, and what the static-heuristic approach deliberately doesn't measure yet.",
1617
};
@@ -67,13 +68,15 @@ const FAQ_JSON_LD = {
6768
export default function MethodologyPage() {
6869
return (
6970
<>
71+
<BreadcrumbJsonLd current={{ name: "Methodology", path: "/methodology" }} />
7072
<script
7173
type="application/ld+json"
7274
// biome-ignore lint/security/noDangerouslySetInnerHtml: server-built JSON-LD; `<` is escaped
7375
dangerouslySetInnerHTML={{
7476
__html: JSON.stringify(FAQ_JSON_LD).replace(/</g, "\\u003c"),
7577
}}
7678
/>
79+
7780
<section className="my-3 mb-7">
7881
<h1 className="mb-2.5 text-[30px] font-bold leading-[1.18] tracking-tight">Methodology</h1>
7982

@@ -217,12 +220,8 @@ improvement = closing a gap unlocks (1 - pass) × weight / Σweight × 100
217220
<code className="mx-1 rounded border border-line bg-surface-2 px-1 py-0.5 font-mono text-xs">
218221
--depth 1 --single-branch
219222
</code>
220-
which fetches the whole working tree at HEAD of the default branch, but no history. Closing this gap is
221-
planned as v0.5.0 on the{" "}
222-
<Link href="/roadmap" className="text-ink-dim underline-offset-4 hover:text-ink-soft hover:underline">
223-
roadmap
224-
</Link>
225-
.
223+
which fetches the whole working tree at HEAD of the default branch, but no history. These describe repo
224+
health more than agent behavior, so they sit outside the score for now.
226225
</li>
227226

228227
<li>How agents actually perform on the repo — that&apos;s the v1.0.0 benchmark harness.</li>

app/package/[registry]/[name]/page.tsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,39 @@ import { ArrowUpRight } from "@phosphor-icons/react/dist/ssr";
22
import type { Metadata } from "next";
33
import Link from "next/link";
44
import { notFound } from "next/navigation";
5+
import { cache } from "react";
56
import { HostPill } from "@/components/HostPill";
67
import { Panel, PanelHeading } from "@/components/Panel";
78
import { ScoreNumber } from "@/components/ScoreNumber";
8-
import { isRegistry } from "@/lib/clients/registries";
9+
import { isRegistry, type Registry } from "@/lib/clients/registries";
910
import { lookupPackage } from "@/lib/package-lookup";
1011
import { APP_URL } from "@/lib/version";
1112

13+
const cachedLookup = cache((registry: Registry, name: string) => lookupPackage(registry, name));
14+
1215
export async function generateMetadata({
1316
params,
1417
}: {
1518
params: Promise<{ registry: string; name: string }>;
1619
}): Promise<Metadata> {
1720
const { registry, name } = await params;
1821
const title = `${registry}/${name}`;
22+
const description = `Agent-friendliness score for the ${registry} package "${name}" and its source repo.`;
23+
24+
if (!isRegistry(registry)) {
25+
return { title, description, robots: { index: false, follow: true } };
26+
}
27+
28+
const result = await cachedLookup(registry, name);
29+
const isThin = result.status !== "scored";
1930

2031
return {
2132
title,
22-
twitter: { title },
33+
description,
34+
twitter: { title, description },
2335
alternates: { canonical: `/package/${registry}/${name}` },
24-
openGraph: { title, url: `/package/${registry}/${name}`, type: "article" },
25-
description: `Agent-friendliness score for the ${registry} package "${name}" and its source repo.`,
36+
openGraph: { title, description, url: `/package/${registry}/${name}`, type: "article" },
37+
...(isThin ? { robots: { index: false, follow: true } } : {}),
2638
};
2739
}
2840

@@ -33,7 +45,7 @@ export default async function Page({ params }: { params: Promise<{ registry: str
3345
notFound();
3446
}
3547

36-
const result = await lookupPackage(registry, name);
48+
const result = await cachedLookup(registry, name);
3749

3850
const jsonLd = {
3951
"@context": "https://schema.org",

0 commit comments

Comments
 (0)