mirror of
https://github.com/CodeWithShreyans/skills.git
synced 2026-09-19 06:04:47 +08:00
@@ -0,0 +1,102 @@
|
||||
# AGENTS.md
|
||||
|
||||
This file provides guidance to AI coding agents (Claude Code, Cursor, Copilot, etc.) when working with code in this repository.
|
||||
|
||||
## Repository Overview
|
||||
|
||||
A collection of skills for coding agents. Skills are packaged instructions and scripts that extend agent's capabilities.
|
||||
|
||||
## Creating a New Skill
|
||||
|
||||
### Directory Structure
|
||||
|
||||
```
|
||||
skills/
|
||||
{skill-name}/ # kebab-case directory name
|
||||
SKILL.md # Required: skill definition
|
||||
scripts/ # Required: executable scripts
|
||||
{script-name}.sh # Bash scripts (preferred)
|
||||
{skill-name}.zip # Required: packaged for distribution
|
||||
```
|
||||
|
||||
### Naming Conventions
|
||||
|
||||
- **Skill directory**: `kebab-case` (e.g., `vercel-deploy`, `log-monitor`)
|
||||
- **SKILL.md**: Always uppercase, always this exact filename
|
||||
- **Scripts**: `kebab-case.sh` (e.g., `deploy.sh`, `fetch-logs.sh`)
|
||||
- **Zip file**: Must match directory name exactly: `{skill-name}.zip`
|
||||
|
||||
### SKILL.md Format
|
||||
|
||||
````markdown
|
||||
---
|
||||
name: { skill-name }
|
||||
description:
|
||||
{
|
||||
One sentence describing when to use this skill. Include trigger phrases like "Deploy my app",
|
||||
"Check logs",
|
||||
etc.,
|
||||
}
|
||||
---
|
||||
|
||||
# {Skill Title}
|
||||
|
||||
{Brief description of what the skill does.}
|
||||
|
||||
## How It Works
|
||||
|
||||
{Numbered list explaining the skill's workflow}
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
bash /mnt/skills/user/{skill-name}/scripts/{script}.sh [args]
|
||||
```
|
||||
````
|
||||
|
||||
**Arguments:**
|
||||
|
||||
- `arg1` - Description (defaults to X)
|
||||
|
||||
**Examples:**
|
||||
{Show 2-3 common usage patterns}
|
||||
|
||||
## Output
|
||||
|
||||
{Show example output users will see}
|
||||
|
||||
## Present Results to User
|
||||
|
||||
{Template for how agent should format results when presenting to users}
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
{Common issues and solutions, especially network/permissions errors}
|
||||
|
||||
````
|
||||
|
||||
### Best Practices for Context Efficiency
|
||||
|
||||
Skills are loaded on-demand — only the skill name and description are loaded at startup. The full `SKILL.md` loads into context only when the agent decides the skill is relevant. To minimize context usage:
|
||||
|
||||
- **Keep SKILL.md under 500 lines** — put detailed reference material in separate files
|
||||
- **Write specific descriptions** — helps the agent know exactly when to activate the skill
|
||||
- **Use progressive disclosure** — reference supporting files that get read only when needed
|
||||
- **Prefer scripts over inline code** — script execution doesn't consume context (only output does)
|
||||
- **File references work one level deep** — link directly from SKILL.md to supporting files
|
||||
|
||||
### Script Requirements
|
||||
|
||||
- Use `#!/bin/bash` shebang
|
||||
- Use `set -e` for fail-fast behavior
|
||||
- Write status messages to stderr: `echo "Message" >&2`
|
||||
- Write machine-readable output (JSON) to stdout
|
||||
- Include a cleanup trap for temp files
|
||||
- Reference the script path as `/mnt/skills/user/{skill-name}/scripts/{script}.sh`
|
||||
|
||||
### End-User Installation
|
||||
|
||||
```bash
|
||||
npx skills add CodeWithShreyans/skills --skill <skill-name>
|
||||
```
|
||||
````
|
||||
@@ -0,0 +1,59 @@
|
||||
# Agent Skills
|
||||
|
||||
A collection of skills for AI coding agents. Skills are packaged instructions and references that extend agent capabilities.
|
||||
|
||||
Skills follow the [Agent Skills](https://agentskills.io/) format.
|
||||
|
||||
## Available Skills
|
||||
|
||||
### bulletproof-react-components
|
||||
|
||||
Nine patterns for building React components that survive real-world conditions — SSR, hydration, concurrent rendering, portals, transitions, and future React changes. Based on [Shu Ding's guide](https://shud.in/thoughts/build-bulletproof-react-components).
|
||||
|
||||
**Use when:**
|
||||
- Writing reusable React components
|
||||
- Fixing hydration mismatches
|
||||
- Handling SSR edge cases
|
||||
- Building component libraries
|
||||
|
||||
**Patterns covered:**
|
||||
- Server-Proof (Critical) - no browser APIs during render
|
||||
- Hydration-Proof (Critical) - inline scripts before hydration
|
||||
- Instance-Proof (High) - `useId()` over hardcoded IDs
|
||||
- Concurrent-Proof (High) - `React.cache()` deduplication
|
||||
- Composition-Proof (High) - Context over `cloneElement`
|
||||
- Portal-Proof (Medium) - `ownerDocument.defaultView` for listeners
|
||||
- Transition-Proof (Medium) - `startTransition()` for View Transitions
|
||||
- Activity-Proof (Medium) - `useLayoutEffect` for `<Activity>` visibility
|
||||
- Future-Proof (Medium) - `useState` initializer for stable values
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npx skills add shreyans/skills
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Skills are automatically available once installed. The agent will use them when relevant tasks are detected.
|
||||
|
||||
**Examples:**
|
||||
```
|
||||
Review this component for bulletproof patterns
|
||||
```
|
||||
```
|
||||
Make this component SSR-safe
|
||||
```
|
||||
```
|
||||
Check this component for hydration issues
|
||||
```
|
||||
|
||||
## Skill Structure
|
||||
|
||||
Each skill contains:
|
||||
- `SKILL.md` - Instructions for the agent
|
||||
- `references/` - Supporting documentation loaded on demand
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
name: bulletproof-react-components
|
||||
description: Build bulletproof React components that survive SSR, hydration, concurrent rendering, portals, transitions, and future React changes. Nine essential patterns from Shu Ding's guide. Use when writing reusable React components, fixing hydration mismatches, handling SSR edge cases, or building component libraries.
|
||||
---
|
||||
|
||||
# Bulletproof React Components
|
||||
|
||||
Nine patterns that ensure React components survive real-world conditions beyond the happy path — SSR, hydration, concurrent rendering, portals, and more.
|
||||
|
||||
Source: [shud.in/thoughts/build-bulletproof-react-components](https://shud.in/thoughts/build-bulletproof-react-components)
|
||||
|
||||
## How It Works
|
||||
|
||||
1. When writing or reviewing a reusable React component, consult the **Quick Rules** below
|
||||
2. For code examples and deeper explanation, read `./references/patterns.md`
|
||||
3. Run through the **Checklist** before shipping
|
||||
|
||||
## Quick Rules
|
||||
|
||||
| # | Pattern | Rule |
|
||||
|---|---------|------|
|
||||
| 1 | **Server-Proof** | Never call browser APIs (`localStorage`, `window`, `document`) during render. Use `useEffect`. |
|
||||
| 2 | **Hydration-Proof** | Inject a synchronous inline `<script>` to set client-dependent values before React hydration. |
|
||||
| 3 | **Instance-Proof** | Use `useId()` for all generated IDs. Never hardcode IDs in reusable components. |
|
||||
| 4 | **Concurrent-Proof** | Wrap server data-fetching in `React.cache()` to deduplicate calls per request. |
|
||||
| 5 | **Composition-Proof** | Use Context instead of `React.cloneElement()` — cloneElement breaks with Server Components, lazy, and memo. |
|
||||
| 6 | **Portal-Proof** | Use `ref.current?.ownerDocument.defaultView \|\| window` for event listeners, not the global `window`. |
|
||||
| 7 | **Transition-Proof** | Wrap state updates in `startTransition()` to enable View Transition API animations. |
|
||||
| 8 | **Activity-Proof** | Use `useLayoutEffect` to disable DOM side effects (e.g., `<style>` tags) when hidden by `<Activity>`. |
|
||||
| 9 | **Future-Proof** | Use `useState(() => value)` for stable identity. `useMemo` is only a performance hint — React may discard it. |
|
||||
|
||||
## Checklist
|
||||
|
||||
When building a reusable React component, verify:
|
||||
|
||||
- [ ] No browser APIs called during render (server-proof)
|
||||
- [ ] No hydration flash for client-storage values (hydration-proof)
|
||||
- [ ] No hardcoded IDs; uses `useId()` (instance-proof)
|
||||
- [ ] Server data fetches wrapped in `cache()` (concurrent-proof)
|
||||
- [ ] Uses Context instead of `cloneElement` (composition-proof)
|
||||
- [ ] Event listeners use `ownerDocument.defaultView` (portal-proof)
|
||||
- [ ] State updates wrapped in `startTransition()` where needed (transition-proof)
|
||||
- [ ] DOM side effects respect `<Activity>` visibility (activity-proof)
|
||||
- [ ] Stable values use `useState` initializer, not `useMemo` (future-proof)
|
||||
|
||||
## Present Results to User
|
||||
|
||||
When reviewing a component against these patterns, format as:
|
||||
|
||||
```
|
||||
**Bulletproof Check: `<ComponentName>`**
|
||||
|
||||
| Pattern | Status | Notes |
|
||||
|---------|--------|-------|
|
||||
| Server-Proof | PASS/FAIL | ... |
|
||||
| ... | ... | ... |
|
||||
|
||||
**Suggested fixes:**
|
||||
1. ...
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- `./references/patterns.md` — Detailed code examples (bad/good) for all nine patterns
|
||||
@@ -0,0 +1,195 @@
|
||||
# Bulletproof React Patterns — Code Examples
|
||||
|
||||
Detailed bad/good code examples for each of the nine patterns.
|
||||
|
||||
---
|
||||
|
||||
## 1. Server-Proof
|
||||
|
||||
Browser APIs crash during SSR. Move them into `useEffect`.
|
||||
|
||||
```tsx
|
||||
// BAD - crashes on server
|
||||
const theme = localStorage.getItem('theme')
|
||||
|
||||
// GOOD - safe for SSR
|
||||
const [theme, setTheme] = useState('light')
|
||||
useEffect(() => {
|
||||
setTheme(localStorage.getItem('theme') || 'light')
|
||||
}, [])
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Hydration-Proof
|
||||
|
||||
Server renders initial state, client hydrates with different values, causing visual flashes. Inject a synchronous inline script that sets the correct value before React hydration.
|
||||
|
||||
```tsx
|
||||
// Inline script runs before React hydration — no flash
|
||||
<script dangerouslySetInnerHTML={{
|
||||
__html: `document.documentElement.dataset.theme = localStorage.getItem('theme') || 'light'`
|
||||
}} />
|
||||
```
|
||||
|
||||
This avoids the flash-of-wrong-content that happens when `useEffect` corrects the value after paint.
|
||||
|
||||
---
|
||||
|
||||
## 3. Instance-Proof
|
||||
|
||||
Multiple instances with hardcoded IDs conflict. Use `useId()`.
|
||||
|
||||
```tsx
|
||||
// BAD - breaks with multiple instances
|
||||
const id = 'my-tooltip'
|
||||
|
||||
// GOOD - unique per instance, stable across server/client
|
||||
const id = useId()
|
||||
|
||||
return (
|
||||
<>
|
||||
<button aria-describedby={id}>Hover me</button>
|
||||
<div id={id} role="tooltip">Tooltip content</div>
|
||||
</>
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Concurrent-Proof
|
||||
|
||||
Server Components fetched multiple times cause duplicate queries. Wrap in `React.cache()`.
|
||||
|
||||
```tsx
|
||||
import { cache } from 'react'
|
||||
|
||||
// Deduplicated across all Server Components in the same request
|
||||
const getUser = cache(async (id: string) => {
|
||||
return await db.user.findUnique({ where: { id } })
|
||||
})
|
||||
|
||||
// Both components call getUser('123') but only one DB query executes
|
||||
async function UserProfile({ id }: { id: string }) {
|
||||
const user = await getUser(id)
|
||||
return <h1>{user.name}</h1>
|
||||
}
|
||||
|
||||
async function UserAvatar({ id }: { id: string }) {
|
||||
const user = await getUser(id)
|
||||
return <img src={user.avatar} />
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Composition-Proof
|
||||
|
||||
`React.cloneElement()` fails with Server Components, lazy-loaded components, or memo. Use Context.
|
||||
|
||||
```tsx
|
||||
// BAD - breaks with Server Components, React.lazy, React.memo
|
||||
React.Children.map(children, child =>
|
||||
React.cloneElement(child, { active: true })
|
||||
)
|
||||
|
||||
// GOOD - works with any child type
|
||||
const TabContext = createContext({ activeIndex: 0 })
|
||||
|
||||
function Tabs({ children, activeIndex }: Props) {
|
||||
return (
|
||||
<TabContext.Provider value={{ activeIndex }}>
|
||||
{children}
|
||||
</TabContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
function Tab({ index, children }: TabProps) {
|
||||
const { activeIndex } = useContext(TabContext)
|
||||
return <div data-active={index === activeIndex}>{children}</div>
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Portal-Proof
|
||||
|
||||
Event listeners on `window` fail in portals, iframes, pop-out windows. Use the component's actual window via its DOM ref.
|
||||
|
||||
```tsx
|
||||
// BAD - only works in the main window
|
||||
useEffect(() => {
|
||||
window.addEventListener('keydown', handler)
|
||||
return () => window.removeEventListener('keydown', handler)
|
||||
}, [])
|
||||
|
||||
// GOOD - works in portals, iframes, pop-out windows
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const win = ref.current?.ownerDocument.defaultView || window
|
||||
win.addEventListener('keydown', handler)
|
||||
return () => win.removeEventListener('keydown', handler)
|
||||
}, [])
|
||||
|
||||
return <div ref={ref}>...</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Transition-Proof
|
||||
|
||||
View Transitions don't animate without `startTransition()`.
|
||||
|
||||
```tsx
|
||||
import { startTransition } from 'react'
|
||||
|
||||
function handleNavigate() {
|
||||
// Enables View Transition API animation
|
||||
startTransition(() => {
|
||||
setCurrentPage(nextPage)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Activity-Proof
|
||||
|
||||
DOM-level side effects (like `<style>` tags) persist globally even when hidden by `<Activity>`. Use `useLayoutEffect` to disable them.
|
||||
|
||||
```tsx
|
||||
const ref = useRef<HTMLStyleElement>(null)
|
||||
|
||||
// Cleanup runs when Activity hides; re-runs when visible
|
||||
useLayoutEffect(() => {
|
||||
if (ref.current) {
|
||||
ref.current.media = 'all' // Enable styles when visible
|
||||
}
|
||||
return () => {
|
||||
if (ref.current) {
|
||||
ref.current.media = 'not all' // Disable styles when hidden
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
return <style ref={ref}>{css}</style>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Future-Proof
|
||||
|
||||
`useMemo` is only a performance hint — React may discard cached values. Use `useState` with an initializer for values that must persist.
|
||||
|
||||
```tsx
|
||||
// BAD - React may discard this at any time
|
||||
const stableId = useMemo(() => crypto.randomUUID(), [])
|
||||
|
||||
// GOOD - useState guarantees persistence for the component's lifetime
|
||||
const [stableId] = useState(() => crypto.randomUUID())
|
||||
```
|
||||
|
||||
**When to use which:**
|
||||
- `useMemo`: Recomputable values where recalculation is just expensive (derived data, filtered lists)
|
||||
- `useState` initializer: Values where identity/stability matters (IDs, subscriptions, one-time setup)
|
||||
Reference in New Issue
Block a user