feat: hand-drawn GitHub badges (library + MCP)#116
Merged
Conversation
New design doc for hand-drawn Badge component, plus three MCP tools (render-badge, render-github-badge, render-github-badge-row) backed by an injectable GitHub client with TTL caching and optional GITHUB_TOKEN auth. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Pin server-render path to goldenchart/server (not goldenchart). - Drop useBrand() branch; always resolveBrand(props.brand) in body. - Define RepoSummary/ReleaseSummary/WorkflowStatus shapes. - Pin width constants, height (26), icon authoring contract (16x16, stroke-only). - Clarify tone source: success/warn/danger fixed; neutral/info brand-derived. - Add in-flight promise map for request dedup; document single-token-per-instance. - Pin TTL precedence (option > env > default). - Pin workflow label fallback order. - Add mcp force-recopy step + LF-snapshot note to verification gates. - Document bundle fallback if check:bundle fails. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
11 tasks: icons + tone table, Badge component (TDD), library exports, GitHub client (cache + in-flight dedup + typed errors), MCP refresh, three new MCP tools (badge / github-badge / github-badge-row), registry wiring, compare carry-forward, full verification + PR. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add badgeIcons.test.ts to Task 1 Files header. - Drop the .replace gimmick on tag glyph; define once cleanly. - Fix (seed ?? 0) + i precedence in renderIcon. - Math.ceil width in Badge so row regex never sees floats. - Replace __client arg seam with module-level __setGithubClientForTests (SDK input validator might strip unknown args). - Drop width: 0 lie; parse intrinsic width via shared parseSvgWidth helper. - Add explicit 12.3k value assertion for stars test. - Show full updated import block for Task 7 tests. - Inline the <style>-dedup logic in row tool (was advisory). - Pin Task 9 to mcp/src/tools.ts (spec wording was loose). - PowerShell-correct chaining (semicolons) in Tasks 5 and 11. - Replace bash heredoc with PS here-string + --body-file in Task 11. - gh pr checks --watch for green verification. - Task 5 prelude: verify src/core/text.ts does not import assets/fonts. - Document workflow_id name-vs-id GitHub API caveat. - Task 10: instructions for finding artifact path from prior carry-forward commit. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
tsup's sideEffects:false stripped re-exported tagged-string arrays because Badge.tsx only consumed BADGE_ICON_PATHS / BADGE_TONE_COLORS. Add isBadgeTone / isBadgeIcon type predicates that reference both arrays at runtime so they survive into dist/. MCP tools (Task 6+) build Zod enums from these arrays. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… workflow runs Two findings from the whole-branch review: - render-badge handler now wraps renderToSVGString in try/catch and returns isError:true with kind:'unexpected', matching the structured-error contract used by render-github-badge and render-github-badge-row. - getWorkflowStatus's 'no runs' path was pairing kind:'not-found' with HTTP 200 (the request succeeded, the list was empty). Use 404 as the sentinel so the structured error doesn't read 'not-found' with a 2xx status. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
| let xOff = 0; | ||
| const inners = parts.map((svg, i) => { | ||
| let inner = svg.replace(/^<svg[^>]*>/, '').replace(/<\/svg>$/, ''); | ||
| if (i > 0) inner = inner.replace(/<style\b[^>]*>[\s\S]*?<\/style>/g, ''); |
| const widths = parts.map(parseSvgWidth); | ||
| const inners = parts.map((svg, i) => { | ||
| let inner = svg.replace(/^<svg[^>]*>/, '').replace(/<\/svg>$/, ''); | ||
| if (i > 0) inner = inner.replace(/<style\b[^>]*>[\s\S]*?<\/style>/g, ''); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a hand-drawn GitHub badge capability — both as a new library
component and as three MCP tools.
What's new
Library:
Badgecomponent (src/components/Badge.tsx) — intrinsic-SVG, brand-awarevia
resolveBrand(props.brand)in the body (per the documentedbrand-without-context pattern for chart bodies). Optional hand-drawn icon
glyph, five tone variants, sketchy Rough.js pill outline.
isBadgeTone/isBadgeIconpredicatesin
src/core/badgeIcons.ts.goldenchartentries.MCP (
goldenchart-mcp):mcp/src/githubClient.ts— injectablefetch, 5-min TTL cache, in-flightpromise dedup, optional
GITHUB_TOKENauth, typedGithubFetchError.render-badge— literal label/value, no network.render-github-badge— single GitHub metric (stars/forks/open-issues/release/license/last-commit/contributors/language/workflow), fetched with
cache + dedup.
render-github-badge-row— N metrics composed into one SVG row, singleHTTP roundtrip per unique endpoint, dedupes
@font-faceblocks.Carry-forward:
mcp/scripts/compare-agent-surface.mjsproducebefore/after renders under
comparisons/(no live network).Verification
npm run typecheck✅ /npm run build✅ /npm run check:bundle✅ (47 KB gzipped, no font leak).
npx tsc --noEmit✅ / filtered vitest 12/12 ✅.CLAUDE.md).Out of scope
variant: 'shields'mode on Badge.<BadgeRow>library component.Spec:
docs/superpowers/specs/2026-05-28-github-badge-rendering-design.mdPlan:
docs/superpowers/plans/2026-05-28-github-badge-rendering.md