From 527d60383c7e03968b91326557d7232596c77d34 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Wed, 28 Jan 2026 07:10:57 +0100 Subject: [PATCH] chore: update skills --- github/SKILL.md | 47 ++++++++++ react-component-performance/SKILL.md | 44 ++++++++++ .../references/examples.md | 88 +++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 github/SKILL.md create mode 100644 react-component-performance/SKILL.md create mode 100644 react-component-performance/references/examples.md diff --git a/github/SKILL.md b/github/SKILL.md new file mode 100644 index 0000000..03b2a00 --- /dev/null +++ b/github/SKILL.md @@ -0,0 +1,47 @@ +--- +name: github +description: "Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries." +--- + +# GitHub Skill + +Use the `gh` CLI to interact with GitHub. Always specify `--repo owner/repo` when not in a git directory, or use URLs directly. + +## Pull Requests + +Check CI status on a PR: +```bash +gh pr checks 55 --repo owner/repo +``` + +List recent workflow runs: +```bash +gh run list --repo owner/repo --limit 10 +``` + +View a run and see which steps failed: +```bash +gh run view --repo owner/repo +``` + +View logs for failed steps only: +```bash +gh run view --repo owner/repo --log-failed +``` + +## API for Advanced Queries + +The `gh api` command is useful for accessing data not available through other subcommands. + +Get PR with specific fields: +```bash +gh api repos/owner/repo/pulls/55 --jq '.title, .state, .user.login' +``` + +## JSON Output + +Most commands support `--json` for structured output. You can use `--jq` to filter: + +```bash +gh issue list --repo owner/repo --json number,title --jq '.[] | "\(.number): \(.title)"' +``` diff --git a/react-component-performance/SKILL.md b/react-component-performance/SKILL.md new file mode 100644 index 0000000..dfea788 --- /dev/null +++ b/react-component-performance/SKILL.md @@ -0,0 +1,44 @@ +--- +name: react-component-performance +description: Analyze and optimize React component performance issues (slow renders, re-render thrash, laggy lists, expensive computations). Use when asked to profile or improve a React component, reduce re-renders, or speed up UI updates in React apps. +--- + +# React Component Performance + +## Overview + +Identify render hotspots, isolate expensive updates, and apply targeted optimizations without changing UI behavior. + +## Workflow + +1. Reproduce or describe the slowdown. +2. Identify what triggers re-renders (state updates, props churn, effects). +3. Isolate fast-changing state from heavy subtrees. +4. Stabilize props and handlers; memoize where it pays off. +5. Reduce expensive work (computation, DOM size, list length). +6. Validate with profiling; avoid speculative changes. + +## Checklist + +- Measure: use React DevTools Profiler or log renders; capture baseline. +- Find churn: identify state updated on a timer, scroll, input, or animation. +- Split: move ticking state into a child; keep heavy lists static. +- Memoize: wrap leaf rows with `memo` only when props are stable. +- Stabilize props: use `useCallback`/`useMemo` for handlers and derived values. +- Avoid derived work in render: precompute, or compute inside memoized helpers. +- Control list size: window/virtualize long lists; avoid rendering hidden items. +- Keys: ensure stable keys; avoid index when order can change. +- Effects: verify dependency arrays; avoid effects that re-run on every render. +- Style/layout: watch for expensive layout thrash or large Markdown/diff renders. + +## Optimization Patterns + +- **Isolate ticking state**: move a timer/animation into a child component so the parent list does not re-render every tick. +- **Stabilize callbacks**: prefer `useCallback` for handlers passed to memoized rows. +- **Split rows**: extract list rows into memoized components with narrow props. +- **Defer heavy rendering**: lazy-render or collapse expensive content until expanded. +- **Prefer derived data outside render**: compute summaries with `useMemo` or helper functions when inputs are stable. + +## Example Reference + +Load `references/examples.md` when the user wants a concrete refactor example. diff --git a/react-component-performance/references/examples.md b/react-component-performance/references/examples.md new file mode 100644 index 0000000..d26cf65 --- /dev/null +++ b/react-component-performance/references/examples.md @@ -0,0 +1,88 @@ +# Examples + +## Isolate a ticking timer from a long list + +**Scenario:** A message list re-renders every second because a timer (`elapsedMs`) lives in the parent component. This causes visible jank on large lists. + +**Goal:** Keep UI identical but limit re-renders to the timer area. + +**Before (problematic pattern):** + +```tsx +function Messages({ items, isThinking, processingStartedAt }) { + const [elapsedMs, setElapsedMs] = useState(0); + + useEffect(() => { + if (!isThinking || !processingStartedAt) { + setElapsedMs(0); + return; + } + setElapsedMs(Date.now() - processingStartedAt); + const interval = window.setInterval(() => { + setElapsedMs(Date.now() - processingStartedAt); + }, 1000); + return () => window.clearInterval(interval); + }, [isThinking, processingStartedAt]); + + return ( +
+ {items.map((item) => ( + + ))} +
{formatDurationMs(elapsedMs)}
+
+ ); +} +``` + +**After (isolated ticking state):** + +```tsx +type WorkingIndicatorProps = { + isThinking: boolean; + processingStartedAt?: number | null; +}; + +const WorkingIndicator = memo(function WorkingIndicator({ + isThinking, + processingStartedAt = null, +}: WorkingIndicatorProps) { + const [elapsedMs, setElapsedMs] = useState(0); + + useEffect(() => { + if (!isThinking || !processingStartedAt) { + setElapsedMs(0); + return; + } + setElapsedMs(Date.now() - processingStartedAt); + const interval = window.setInterval(() => { + setElapsedMs(Date.now() - processingStartedAt); + }, 1000); + return () => window.clearInterval(interval); + }, [isThinking, processingStartedAt]); + + return
{formatDurationMs(elapsedMs)}
; +}); + +function Messages({ items, isThinking, processingStartedAt }) { + return ( +
+ {items.map((item) => ( + + ))} + +
+ ); +} +``` + +**Why it helps:** Only the `WorkingIndicator` subtree re-renders every second. The list remains stable unless its props change. + +**Optional follow-ups:** + +- Wrap `MessageRow` in `memo` if props are stable. +- Use `useCallback` for handlers passed to rows to avoid re-render churn. +- Consider list virtualization if the list is very large.