Skip to content

Commit 0c78c4a

Browse files
committed
feat: agent friendly code
0 parents  commit 0c78c4a

114 files changed

Lines changed: 51832 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/hooks/stop-guard.sh

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/bin/sh
2+
# Stop-hook guard for agent-friendly-code.
3+
#
4+
# If the turn changed any watched source file AND the model has not yet
5+
# been redirected this cycle, block the stop and tell the model to invoke
6+
# `/post-change-check`. On the second Stop (`stop_hook_active: true`) the
7+
# guard steps aside — the documented escape hatch that prevents infinite
8+
# re-entry.
9+
10+
set -eu
11+
12+
input="$(cat)"
13+
14+
# Anti-loop: let the turn end if we already blocked once this cycle.
15+
if printf '%s' "$input" | grep -qE '"stop_hook_active"[[:space:]]*:[[:space:]]*true'; then
16+
exit 0
17+
fi
18+
19+
paths="app components lib scripts .claude biome.json lefthook.yml package.json tsconfig.json next.config.ts"
20+
modified="$(git diff --name-only HEAD -- $paths 2>/dev/null || true)"
21+
untracked="$(git ls-files --others --exclude-standard -- $paths 2>/dev/null || true)"
22+
changed="$(printf '%s\n%s' "$modified" "$untracked" | sed '/^$/d')"
23+
24+
if [ -z "$changed" ]; then
25+
printf '\n✓ Turn finished. No source-file changes detected.\n'
26+
exit 0
27+
fi
28+
29+
file_list="$(printf '%s\n' "$changed" | sed 's/^/ • /')"
30+
31+
# Build the reason text, then let python3 produce safely-escaped JSON.
32+
printf '%s\n' \
33+
"Source files changed this turn:" \
34+
"$file_list" \
35+
"" \
36+
"Invoke the /post-change-check skill NOW (docs-sync audit + /code-review + /quality-check)." \
37+
"Do not close the turn until it has run. Continue and call the skill via the Skill tool." \
38+
| python3 -c 'import json,sys; print(json.dumps({"decision":"block","reason":sys.stdin.read().rstrip()}))'

.claude/settings.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"$schema": "https://json.schemastore.org/claude-code-settings.json",
3+
"hooks": {
4+
"SessionStart": [
5+
{
6+
"hooks": [
7+
{
8+
"type": "command",
9+
"command": "printf '\\n📦 Agent Friendly Code — current release: 0.1.0\\n • Read AGENTS.md for conventions, CONTRIBUTING.md for the PR workflow.\\n • Roadmap: 0.2.0 (dogfood) → 0.3.0 (benchmark harness) → 0.4.0 (ecosystem integration) → 0.5.0 (alternatives) → 0.6.0 (registry overlay) → 0.7.0 (history-aware signals) → 1.0.0 (production stability) → 1.1.0 (at-scale GitHub indexing).\\n • Changelog rule: user-facing capabilities only. Codebase hygiene (CI / linter / tests / CONTRIBUTING) does NOT go in lib/changelog.ts.\\n'"
10+
}
11+
]
12+
}
13+
],
14+
"Stop": [
15+
{
16+
"hooks": [
17+
{
18+
"type": "command",
19+
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/stop-guard.sh"
20+
}
21+
]
22+
}
23+
]
24+
}
25+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
---
2+
name: code-review
3+
description: Reviews code changes for repo-specific best practices — Tailwind usage, constants/utils extraction, I/O boundary, React Server Components, and component reuse. Triggers during PR review or after non-trivial edits to app/, components/, or lib/.
4+
---
5+
6+
# Code review — repo best practices
7+
8+
Run through this checklist on any diff. Flag violations with the specific line and the suggested fix.
9+
10+
## Tailwind
11+
12+
- **Prefer utilities over custom classes.** If a pattern can be expressed with existing Tailwind utilities, use them. Don't add a custom class in `app/globals.css` unless the pattern repeats 3+ times with the same composition.
13+
- **Use the `@theme` tokens** (`bg-surface`, `text-ink`, `border-line`, etc.) — never hardcode hex in JSX.
14+
- **No `style={{ ... }}`** unless the value is dynamic (e.g. `width: ${pct}%`). Static styling belongs in className.
15+
- **No `@apply`** in `globals.css` (Tailwind 4 best practice — put the utilities on the element instead, or extract a React component).
16+
17+
## Constants + utils
18+
19+
- Magic numbers → `lib/constants/` (e.g. `SCORE_THRESHOLD_GOOD`, `LEADERBOARD_PAGE_SIZE`).
20+
- Repeated formatting → `lib/utils/format.ts` (`compactStars`, `relativeTime`, `hostLabel`).
21+
- Repeated scoring logic → `lib/utils/score.ts` (`scoreTier`, `TIER_TEXT_CLASS`, `TIER_BG_CLASS`).
22+
- A helper that's used once doesn't belong in `utils/`; inline it.
23+
24+
## I/O boundary (architecturally important)
25+
26+
- **`lib/scoring/` stays pure** — no DB imports, no HTTP, no side effects.
27+
- **All SQL in `lib/db.ts`.** Don't call `db.prepare(...)` from pages or components.
28+
- **Clients in `lib/clients/`**`git.ts` for cloning, `github.ts` for host APIs.
29+
- Pages and scripts are thin wrappers.
30+
31+
## React Server Components
32+
33+
- **Default to server components.** Only add `"use client"` when you need state, effects, browser APIs, or event handlers.
34+
- **Fetch data in pages**, pass props down. Components don't fetch.
35+
- **Use `<Link>` from `next/link`** for internal navigation, not `<a href>`.
36+
- **Query params over client state** where possible (model filter, pagination, search).
37+
38+
## Components
39+
40+
- If you're writing the same markup 2–3 times, extract a component into `components/`.
41+
- Components are presentational — no data fetching, no side effects.
42+
- Props are typed explicitly; avoid `any`.
43+
44+
## Icons
45+
46+
Only `@phosphor-icons/react`. Block Lucide, Heroicons, React Icons, inline SVG, emoji-as-icon. Import server components from `@phosphor-icons/react/dist/ssr`; client components from the root entry.
47+
48+
## Accessibility (quick pass — delegate deeper checks to `quality-check`)
49+
50+
- Icon-only affordances need `aria-label`.
51+
- Interactive elements are keyboard-reachable.
52+
- No color-only signals (pair color with text or icon).
53+
54+
## Security
55+
56+
- Parameterised SQL only.
57+
- No `dangerouslySetInnerHTML`.
58+
- External links include `rel="noopener"`.
59+
- Never execute code from a cloned repo.
60+
61+
## Response format when reviewing
62+
63+
Group findings by severity:
64+
65+
- **Must fix**: breaks conventions, security issues, accessibility blockers.
66+
- **Should fix**: best-practice violations, missing extractions.
67+
- **Consider**: minor polish.
68+
69+
Each finding cites the file + line and suggests the exact fix.
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
---
2+
name: post-change-check
3+
description: Orchestrates the end-of-turn review after non-trivial edits to app/, components/, lib/, scripts/, or config files. Runs the docs-sync audit, invokes the code-review and quality-check skills on the diff, and flags anything still missing before the turn can close.
4+
---
5+
6+
# Post-change check — docs sync + code review + quality sweep
7+
8+
Run this when a turn has made code changes and you're about to report done. It's the one stop that verifies **the work is coherent** (docs and code aligned) **and correct** (conventions followed).
9+
10+
## When to run
11+
12+
- Auto-invoke yourself when a turn edits any of: `app/**`, `components/**`, `lib/**`, `scripts/**`, `.claude/**`, `lefthook.yml`, `biome.json`, `package.json`, `tsconfig.json`, `next.config.ts`.
13+
- User can also invoke manually: `/post-change-check`.
14+
- **Skip** for pure typo fixes, comment tweaks, or changes entirely inside `tasks/` and `data/`.
15+
16+
## How to run
17+
18+
Work the four phases **in order**. Don't skip ahead — the docs phase often surfaces issues code review won't.
19+
20+
### Phase 1 — Collect the diff
21+
22+
1. `git status --short` — see what moved.
23+
2. `git diff --stat HEAD` — scope.
24+
3. `git diff HEAD -- app/ components/ lib/ scripts/` — the actual changes.
25+
26+
Keep a mental list of: touched files, added/removed exports, new deps, new env vars, new routes.
27+
28+
### Phase 2 — Docs sync audit
29+
30+
For each category below, decide **yes / no / N/A**, then act on the yeses:
31+
32+
- **`lib/changelog.ts` under the current release bucket**
33+
- Yes **only** if the change ships a user-facing capability (something a dashboard visitor, API caller, or maintainer of a listed repo can observe) **and** that capability maps to a `lib/roadmap.ts` entry or a `tasks/` file whose `Status` just flipped to `done`.
34+
- No for codebase hygiene (CI, linter, pre-commit, tests), contributor docs (CONTRIBUTING, PR templates), internal refactors, dep bumps, directory moves, or UI polish without a semantic change.
35+
- Action: append a one-line capability bullet (newest-first) **and** delete the matching item from `lib/roadmap.ts` — moved, not duplicated.
36+
- **`lib/roadmap.ts` integrity**
37+
- Every item must point to a real `tasks/X.Y.Z/NN-*.md` file — no dangling `taskFile` references.
38+
- Adding a roadmap entry requires adding its task file in the same PR. Don't split.
39+
- Released versions don't live on the roadmap; they move to `lib/changelog.ts` with a release label.
40+
- **`tasks/X.Y.Z/` files**
41+
- Yes if scope shifted, a task was partly/fully completed, or a new sub-task emerged.
42+
- Action: update the file's `**Status**:` header (`planned` / `in_progress` / `done`) and its "What shipped" section.
43+
- **`AGENTS.md`**
44+
- Yes if stack, directory layout, conventions, or I/O boundary changed.
45+
- Specifics:
46+
- Stack changes (Next.js / React / Tailwind / Bun / DB driver) → update the **Stack** bullets + cross-check with `README.md`'s Stack & rationale table.
47+
- New top-level folder → update the **Layout** tree.
48+
- New page in `app/` → mention in the **Layout** tree and link from the nav in `app/layout.tsx`.
49+
- New signal / model / host → ensure the matching "Adding a …" section in `AGENTS.md` is still accurate.
50+
- New convention or constraint → add a bullet under **Conventions**.
51+
- **`README.md`**
52+
- Yes if a product-facing behaviour, command, or screenshot changed.
53+
- Specifics: stack changes must match the `AGENTS.md` stack list; quickstart commands must actually work (`bun install`, `bun run prepare-hooks`, `bun run dev`, `bun run score <url>`); prior-art table updated if we've encountered new competitors since last sync.
54+
- **`CONTRIBUTING.md`**
55+
- Yes if the branch/commit/PR flow or review bar changed, or if the security-contact email moved.
56+
- **`.env.example`**
57+
- Yes if a new env var was introduced.
58+
- Action: document with a one-line comment above the line.
59+
- **`.claude/skills/*`**
60+
- Yes if a new agent workflow was added or an existing one changed.
61+
- Action: update or create a skill file.
62+
63+
Report each as `✓ updated`, `✓ N/A — reason`, or `✗ missing — file:line where it should go`.
64+
65+
### Phase 3 — Code review
66+
67+
Invoke the `code-review` skill on the diff. Its checklist:
68+
69+
- Tailwind-first (utilities > custom classes, `@theme` tokens > hex, no `@apply` in `globals.css`).
70+
- Constants / utils extracted when the rule-of-three is hit.
71+
- I/O boundary held — `lib/scoring/` stays pure, all SQL in `lib/db.ts`.
72+
- Server components default, client only when interactivity demands it.
73+
- Components presentational — no data fetching, no effects.
74+
- Phosphor icons only.
75+
76+
Report findings in severity order: **Must fix** / **Should fix** / **Consider**.
77+
78+
### Phase 4 — Quality sweep
79+
80+
Invoke the `quality-check` skill on the diff. Four dimensions:
81+
82+
- **Accessibility**: landmarks, aria, contrast, reduced-motion, keyboard reachability.
83+
- **Responsiveness**: 320 → 1080+, mobile nav, table overflow, line-length caps.
84+
- **Performance**: RSC default, `next/script` for third-party, bundle weight, prepared statements.
85+
- **Security**: parameterised SQL, no `dangerouslySetInnerHTML`, `rel="noopener"` on external links, clone safety.
86+
87+
Report Pass / Fail / Needs manual check per dimension.
88+
89+
## Output format
90+
91+
Print a single consolidated report:
92+
93+
```
94+
Post-change check — <N> files touched
95+
96+
Docs sync
97+
✓ lib/changelog.ts — bullet added
98+
✓ N/A — README.md (no product-facing change)
99+
✗ tasks/0.2.0/01-tests.md — status still "planned" but harness already committed; flip to in_progress
100+
101+
Code review
102+
Must fix: <none> | <file:line — why>
103+
Should fix: …
104+
Consider: …
105+
106+
Quality
107+
a11y: pass
108+
responsive: pass
109+
performance: pass — flagged `new-dep@1.2.3` at 73 KB gz, justify or swap
110+
security: pass
111+
112+
Next steps: <bullet list or "clean, ready to commit">
113+
```
114+
115+
If every phase is clean, say so plainly in one line. Don't invent findings to look thorough.
116+
117+
## What this skill does NOT do
118+
119+
- It does not run tests, biome, or tsc — those are the pre-commit hook's job (`lefthook.yml`).
120+
- It does not push, commit, or open PRs.
121+
- It does not modify the codebase beyond the docs updates in Phase 2 (changelog / tasks / AGENTS / README).
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
---
2+
name: quality-check
3+
description: Reviews code changes against accessibility, responsiveness, performance, and security standards. Triggers on PR review or after non-trivial changes to app/, components/, or lib/.
4+
---
5+
6+
# Quality check — a11y, responsive, performant, secure
7+
8+
Run the four checks below on any diff affecting UI or I/O. Report findings grouped by dimension.
9+
10+
## Accessibility
11+
12+
- **Landmarks**: one `<main>`, one `<h1>` per page, `<nav aria-label>` on every nav.
13+
- **Semantic HTML**: `<button>` for buttons, `<a>` for links, `<form>` for forms. No `<div onClick>`.
14+
- **aria-label** on every icon-only interactive element (hamburger button, external-link icon, medal chips, score bars).
15+
- **Keyboard reachability**: every interactive element is focusable and visibly shows focus. The global `*:focus-visible` ring covers most cases; verify for custom controls.
16+
- **Color contrast AA**: `text-muted` on `bg-surface` and `bg-surface-2` must hit 4.5:1 for body text (3:1 for large). Run the numbers if you change color tokens.
17+
- **Reduced motion**: transitions/animations respect `@media (prefers-reduced-motion: reduce)`.
18+
- **Form labels**: every input has an associated `<label>` (visible or `sr-only`).
19+
20+
## Responsive
21+
22+
- Viewport sizes to verify: **320 px**, **640 px** (sm), **768 px** (md), **1080 px+** (container max).
23+
- **Nav**: collapses to `<MobileNav>` hamburger below md.
24+
- **Tables**: wrapped in `overflow-x-auto` — never force horizontal page scroll.
25+
- **Hero / headings**: scale with `sm:` or `md:` prefix.
26+
- **Grids**: fall back to `grid-cols-1` on narrow.
27+
- **Flex rows** that hold long content: `flex-col sm:flex-row` to stack on mobile.
28+
- **Hero paragraph and body text**: max ~70ch to keep line length readable.
29+
30+
## Performance
31+
32+
- **Server components by default.** Client components only when necessary.
33+
- **Third-party scripts** loaded via `next/script` with `strategy="afterInteractive"` — see `GoogleAnalytics.tsx`.
34+
- **No client-side data fetching** for rendering — use the DB in a Server Component or API route.
35+
- **Image optimisation** via `<Image>` when we add images; no `<img>` unless the src is dynamic and unknown at build.
36+
- **Bundle weight**: flag any new dep > 50 KB gzipped. Justify or use a smaller alternative.
37+
- **Database queries** are `prepare`d once and reused where hot.
38+
39+
## Security
40+
41+
- **SQL parameterisation**: every query uses `?` placeholders. No string concatenation.
42+
- **No `dangerouslySetInnerHTML`.**
43+
- **External URLs** in `<a target="_blank">` always include `rel="noopener"`.
44+
- **User input at every boundary** is validated: `parseRepoUrl` for repo URLs, `Number.isFinite` for numeric params, length caps on search strings.
45+
- **Clone safety**: `git clone --depth 1 --single-branch`; never execute code from a clone (no `bun install`, no `npm install`, no post-clone scripts).
46+
- **Secrets never in code**. `.env.example` documents required vars; `.env.local` is gitignored.
47+
- **Operational concerns** (flag for ops review, not code):
48+
- Disk cap on `tmp-clones/`.
49+
- Rate limit on the public API before launch.
50+
- Sandbox the cloner in a container for production.
51+
52+
## Response format
53+
54+
For each dimension, list:
55+
56+
- **Pass** items (short, one line each).
57+
- **Fail** items with file:line + the fix.
58+
- **Needs manual check** items (e.g. "contrast pass requires a visual check at these two color pairings").
59+
60+
If everything passes, say so plainly. Don't invent issues to look thorough.

.env.example

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
PORT=3000
2+
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX
3+
NEXT_PUBLIC_APP_URL=https://agent-friendly-code.vercel.app
4+
5+
# ---------- Host API tokens ----------
6+
# Raise rate limits when fetching repo metadata (stars, default branch) during
7+
# `bun run score` / `bun run seed`. Scoring works without these, just slower
8+
# against the unauthenticated ceiling.
9+
10+
# Classic or fine-grained PAT with public_repo read access is enough.
11+
GITHUB_TOKEN=
12+
13+
# GitLab personal access token with read_api scope.
14+
GITLAB_TOKEN=
15+

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* @hsnice16

.github/FUNDING.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
github: hsnice16

.github/workflows/ci.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
branches: [main]
6+
push:
7+
branches: [main]
8+
9+
concurrency:
10+
group: ci-${{ github.ref }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
check:
15+
name: Lint · Format · Typecheck
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Setup Bun
21+
uses: oven-sh/setup-bun@v2
22+
with:
23+
bun-version: 1.2.16
24+
25+
- name: Install
26+
run: bun install --frozen-lockfile
27+
28+
- name: Biome check (lint + format)
29+
run: bun x @biomejs/biome ci .
30+
31+
- name: Typecheck
32+
run: bun x tsc --noEmit

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
node_modules/
2+
.next/
3+
data/*.db
4+
data/*.db-journal
5+
tmp-clones/
6+
.env
7+
.env.local
8+
.DS_Store
9+
next-env.d.ts

0 commit comments

Comments
 (0)