chore: update skills

This commit is contained in:
Thomas Ricouard 2026-01-28 07:10:57 +01:00
parent 9826f404c3
commit 527d60383c
3 changed files with 179 additions and 0 deletions

47
github/SKILL.md Normal file
View file

@ -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 <run-id> --repo owner/repo
```
View logs for failed steps only:
```bash
gh run view <run-id> --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)"'
```

View file

@ -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.

View file

@ -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 (
<div>
{items.map((item) => (
<MessageRow key={item.id} item={item} />
))}
<div>{formatDurationMs(elapsedMs)}</div>
</div>
);
}
```
**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 <div>{formatDurationMs(elapsedMs)}</div>;
});
function Messages({ items, isThinking, processingStartedAt }) {
return (
<div>
{items.map((item) => (
<MessageRow key={item.id} item={item} />
))}
<WorkingIndicator
isThinking={isThinking}
processingStartedAt={processingStartedAt}
/>
</div>
);
}
```
**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.