Asset Manager
Organize design assets, optimize images and fonts, maintain brand asset libraries, implement version control for assets, and enforce naming conventions. Keep design assets organized and production-ready.
Best use case
Asset Manager is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Organize design assets, optimize images and fonts, maintain brand asset libraries, implement version control for assets, and enforce naming conventions. Keep design assets organized and production-ready.
Teams using Asset Manager should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/asset-manager/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How Asset Manager Compares
| Feature / Agent | Asset Manager | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Organize design assets, optimize images and fonts, maintain brand asset libraries, implement version control for assets, and enforce naming conventions. Keep design assets organized and production-ready.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Asset Manager
Keep design assets organized, optimized, and accessible.
## Core Principle
**Organized assets = faster development.**
Assets should be:
- Easy to find
- Properly named
- Optimized for production
- Version controlled
- Consistently formatted
---
## Phase 1: Asset Organization
### Directory Structure
```
assets/
├── images/
│ ├── products/
│ ├── team/
│ ├── marketing/
│ └── ui/
├── icons/
│ ├── svg/
│ └── png/
├── fonts/
│ ├── primary/
│ └── secondary/
├── videos/
├── logos/
│ ├── svg/
│ ├── png/
│ └── variants/
└── brand/
├── colors.json
├── typography.json
└── guidelines.pdf
```
### Naming Conventions
**Images:**
```
{category}-{description}-{size}.{format}
Examples:
product-hero-1920x1080.jpg
team-sarah-400x400.jpg
ui-background-pattern.png
```
**Icons:**
```
{icon-name}-{variant}.svg
Examples:
home-outline.svg
home-filled.svg
user-circle.svg
arrow-right.svg
```
**Fonts:**
```
{font-family}-{weight}.{format}
Examples:
Inter-Regular.woff2
Inter-Bold.woff2
Poppins-SemiBold.woff2
```
### Automated Organization Script
```typescript
// scripts/organize-assets.ts
import fs from 'fs/promises'
import path from 'path'
interface AssetRule {
pattern: RegExp
destination: string
}
const rules: AssetRule[] = [
{ pattern: /product-/i, destination: 'images/products' },
{ pattern: /team-/i, destination: 'images/team' },
{ pattern: /icon-/i, destination: 'icons/svg' },
{ pattern: /logo-/i, destination: 'logos' }
]
async function organizeAssets(sourceDir: string) {
const files = await fs.readdir(sourceDir)
for (const file of files) {
const sourcePath = path.join(sourceDir, file)
const stat = await fs.stat(sourcePath)
if (stat.isDirectory()) continue
// Find matching rule
const rule = rules.find(r => r.pattern.test(file))
if (rule) {
const destDir = path.join('assets', rule.destination)
await fs.mkdir(destDir, { recursive: true })
const destPath = path.join(destDir, file)
await fs.rename(sourcePath, destPath)
console.log(`Moved: ${file} → ${rule.destination}`)
}
}
console.log('Assets organized!')
}
organizeAssets('./unsorted-assets')
```
---
## Phase 2: Image Optimization
### Install Optimization Tools
```bash
npm install sharp imagemin imagemin-mozjpeg imagemin-pngquant imagemin-svgo
```
### Optimize Images Script
```typescript
// scripts/optimize-images.ts
import sharp from 'sharp'
import imagemin from 'imagemin'
import imageminMozjpeg from 'imagemin-mozjpeg'
import imageminPngquant from 'imagemin-pngquant'
import imageminSvgo from 'imagemin-svgo'
import fs from 'fs/promises'
import path from 'path'
interface OptimizeOptions {
quality?: number
maxWidth?: number
formats?: ('jpg' | 'png' | 'webp' | 'avif')[]
}
async function optimizeImages(inputDir: string, outputDir: string, options: OptimizeOptions = {}) {
const { quality = 80, maxWidth = 2000, formats = ['jpg', 'png', 'webp'] } = options
const files = await fs.readdir(inputDir)
for (const file of files) {
const inputPath = path.join(inputDir, file)
const stat = await fs.stat(inputPath)
if (stat.isDirectory()) continue
const ext = path.extname(file).toLowerCase()
const name = path.basename(file, ext)
console.log(`Processing: ${file}`)
// Skip SVGs (handle separately)
if (ext === '.svg') {
await optimizeSVG(inputPath, outputDir)
continue
}
// Skip non-images
if (!['.jpg', '.jpeg', '.png'].includes(ext)) continue
// Read image
const image = sharp(inputPath)
const metadata = await image.metadata()
// Resize if too large
if (metadata.width && metadata.width > maxWidth) {
image.resize(maxWidth, null, {
withoutEnlargement: true,
fit: 'inside'
})
}
// Generate formats
for (const format of formats) {
const outputPath = path.join(outputDir, `${name}.${format}`)
if (format === 'jpg') {
await image.jpeg({ quality, mozjpeg: true }).toFile(outputPath)
} else if (format === 'png') {
await image.png({ quality, compressionLevel: 9 }).toFile(outputPath)
} else if (format === 'webp') {
await image.webp({ quality }).toFile(outputPath)
} else if (format === 'avif') {
await image.avif({ quality }).toFile(outputPath)
}
console.log(` ✓ Generated ${format}`)
}
}
console.log('Images optimized!')
}
async function optimizeSVG(inputPath: string, outputDir: string) {
const fileName = path.basename(inputPath)
const outputPath = path.join(outputDir, fileName)
await imagemin([inputPath], {
destination: outputDir,
plugins: [
imageminSvgo({
plugins: [
{ name: 'removeViewBox', active: false },
{ name: 'removeDimensions', active: true },
{ name: 'removeUselessStrokeAndFill', active: true }
]
})
]
})
console.log(` ✓ Optimized SVG`)
}
// Usage
optimizeImages('./assets/images/raw', './assets/images/optimized', {
quality: 85,
maxWidth: 1920,
formats: ['jpg', 'webp', 'avif']
})
```
### Responsive Images Generator
```typescript
// scripts/generate-responsive-images.ts
import sharp from 'sharp'
import fs from 'fs/promises'
import path from 'path'
const breakpoints = [
{ name: 'mobile', width: 640 },
{ name: 'tablet', width: 768 },
{ name: 'desktop', width: 1920 }
]
async function generateResponsiveImages(inputPath: string) {
const ext = path.extname(inputPath)
const name = path.basename(inputPath, ext)
const dir = path.dirname(inputPath)
for (const bp of breakpoints) {
const image = sharp(inputPath)
// Resize
image.resize(bp.width, null, {
withoutEnlargement: true,
fit: 'inside'
})
// Generate WebP
const webpPath = path.join(dir, `${name}-${bp.name}.webp`)
await image.webp({ quality: 80 }).toFile(webpPath)
console.log(` ✓ ${name}-${bp.name}.webp`)
// Generate AVIF
const avifPath = path.join(dir, `${name}-${bp.name}.avif`)
await image.avif({ quality: 80 }).toFile(avifPath)
console.log(` ✓ ${name}-${bp.name}.avif`)
}
console.log(`Generated responsive images for: ${name}`)
}
// Usage
generateResponsiveImages('./assets/images/hero.jpg')
```
---
## Phase 3: Font Management
### Font Optimization
```typescript
// scripts/optimize-fonts.ts
import { exec } from 'child_process'
import { promisify } from 'util'
import fs from 'fs/promises'
import path from 'path'
const execAsync = promisify(exec)
async function optimizeFonts(inputDir: string, outputDir: string) {
const files = await fs.readdir(inputDir)
for (const file of files) {
const inputPath = path.join(inputDir, file)
const ext = path.extname(file).toLowerCase()
if (ext !== '.ttf' && ext !== '.otf') continue
const name = path.basename(file, ext)
console.log(`Processing: ${file}`)
// Convert to WOFF2 (best compression)
const woff2Path = path.join(outputDir, `${name}.woff2`)
await convertToWOFF2(inputPath, woff2Path)
console.log(` ✓ ${name}.woff2`)
// Convert to WOFF (fallback)
const woffPath = path.join(outputDir, `${name}.woff`)
await convertToWOFF(inputPath, woffPath)
console.log(` ✓ ${name}.woff`)
}
console.log('Fonts optimized!')
}
async function convertToWOFF2(input: string, output: string) {
// Requires woff2_compress tool
// Install: brew install woff2
await execAsync(`woff2_compress ${input}`)
const woff2File = input.replace(/\.(ttf|otf)$/, '.woff2')
await fs.rename(woff2File, output)
}
async function convertToWOFF(input: string, output: string) {
// Requires sfnt2woff tool
await execAsync(`sfnt2woff ${input}`)
const woffFile = input.replace(/\.(ttf|otf)$/, '.woff')
await fs.rename(woffFile, output)
}
optimizeFonts('./assets/fonts/raw', './assets/fonts/optimized')
```
### Font Loading Strategy
```css
/* fonts.css */
/* Preload critical fonts */
@font-face {
font-family: 'Inter';
src:
url('/fonts/Inter-Regular.woff2') format('woff2'),
url('/fonts/Inter-Regular.woff') format('woff');
font-weight: 400;
font-style: normal;
font-display: swap; /* Show fallback first */
}
@font-face {
font-family: 'Inter';
src:
url('/fonts/Inter-Bold.woff2') format('woff2'),
url('/fonts/Inter-Bold.woff') format('woff');
font-weight: 700;
font-style: normal;
font-display: swap;
}
```
**Preload in HTML:**
```html
<head>
<!-- Preload critical fonts -->
<link rel="preload" href="/fonts/Inter-Regular.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/fonts/Inter-Bold.woff2" as="font" type="font/woff2" crossorigin />
</head>
```
---
## Phase 4: Asset Version Control
### Git LFS Setup
```bash
# Install Git LFS
brew install git-lfs
git lfs install
# Track large files
git lfs track "*.psd"
git lfs track "*.ai"
git lfs track "*.sketch"
git lfs track "*.fig"
git lfs track "*.mp4"
git lfs track "*.mov"
git lfs track "assets/images/**/*.jpg"
git lfs track "assets/images/**/*.png"
# Add .gitattributes
git add .gitattributes
git commit -m "Configure Git LFS"
```
### Asset Versioning System
```typescript
// scripts/version-assets.ts
import fs from 'fs/promises'
import path from 'path'
import crypto from 'crypto'
interface AssetVersion {
path: string
hash: string
size: number
modified: string
version: number
}
class AssetVersionManager {
private versionFile = 'asset-versions.json'
private versions: Map<string, AssetVersion[]> = new Map()
async load() {
try {
const data = await fs.readFile(this.versionFile, 'utf-8')
const parsed = JSON.parse(data)
for (const [path, versions] of Object.entries(parsed)) {
this.versions.set(path, versions as AssetVersion[])
}
} catch (error) {
// File doesn't exist yet
}
}
async save() {
const data = Object.fromEntries(this.versions)
await fs.writeFile(this.versionFile, JSON.stringify(data, null, 2))
}
async trackAsset(filePath: string) {
const buffer = await fs.readFile(filePath)
const hash = crypto.createHash('sha256').update(buffer).digest('hex')
const stat = await fs.stat(filePath)
const versions = this.versions.get(filePath) || []
const lastVersion = versions[versions.length - 1]
// Check if changed
if (lastVersion && lastVersion.hash === hash) {
console.log(`No changes: ${filePath}`)
return
}
// Add new version
versions.push({
path: filePath,
hash,
size: stat.size,
modified: stat.mtime.toISOString(),
version: versions.length + 1
})
this.versions.set(filePath, versions)
console.log(`Tracked: ${filePath} (v${versions.length})`)
}
async trackDirectory(dirPath: string) {
const files = await fs.readdir(dirPath, { recursive: true })
for (const file of files) {
const filePath = path.join(dirPath, file.toString())
const stat = await fs.stat(filePath)
if (stat.isDirectory()) continue
await this.trackAsset(filePath)
}
}
}
// Usage
const manager = new AssetVersionManager()
await manager.load()
await manager.trackDirectory('./assets')
await manager.save()
```
---
## Phase 5: Brand Asset Library
### Brand Kit Structure
```
brand/
├── logos/
│ ├── primary/
│ │ ├── logo-full.svg
│ │ ├── logo-icon.svg
│ │ └── logo-wordmark.svg
│ ├── variations/
│ │ ├── logo-white.svg
│ │ ├── logo-black.svg
│ │ └── logo-inverted.svg
│ └── exports/
│ ├── png/
│ ├── pdf/
│ └── eps/
├── colors/
│ ├── colors.json
│ ├── colors.css
│ └── colors.scss
├── typography/
│ ├── fonts/
│ └── typography.json
└── guidelines/
├── brand-guidelines.pdf
├── logo-usage.pdf
└── color-usage.pdf
```
### Brand Asset Manifest
```typescript
// brand/manifest.ts
export interface BrandAssets {
version: string
lastUpdated: string
logos: LogoAsset[]
colors: ColorAsset[]
typography: TypographyAsset[]
}
export interface LogoAsset {
name: string
variants: {
full: string
icon: string
wordmark: string
}
formats: {
svg: string
png: { [size: string]: string }
pdf: string
}
}
export interface ColorAsset {
name: string
hex: string
rgb: { r: number; g: number; b: number }
usage: string
}
export interface TypographyAsset {
name: string
family: string
weights: number[]
formats: string[]
}
export const brandAssets: BrandAssets = {
version: '2.0.0',
lastUpdated: '2024-01-15',
logos: [
{
name: 'Primary Logo',
variants: {
full: '/brand/logos/logo-full.svg',
icon: '/brand/logos/logo-icon.svg',
wordmark: '/brand/logos/logo-wordmark.svg'
},
formats: {
svg: '/brand/logos/logo-full.svg',
png: {
'1x': '/brand/logos/exports/png/logo-full@1x.png',
'2x': '/brand/logos/exports/png/logo-full@2x.png',
'3x': '/brand/logos/exports/png/logo-full@3x.png'
},
pdf: '/brand/logos/exports/pdf/logo-full.pdf'
}
}
],
colors: [
{
name: 'Primary',
hex: '#0066cc',
rgb: { r: 0, g: 102, b: 204 },
usage: 'Primary actions, links, brand elements'
}
],
typography: [
{
name: 'Inter',
family: 'Inter',
weights: [400, 600, 700],
formats: ['woff2', 'woff']
}
]
}
```
---
## Best Practices
### 1. Optimize for Web
- **Images:** Use WebP/AVIF with JPEG fallback
- **Icons:** Use SVG sprites for multiple icons
- **Fonts:** Use WOFF2 with WOFF fallback
- **Videos:** Compress and provide multiple formats
### 2. Lazy Loading
```tsx
// Lazy load images
;<img src="placeholder.jpg" data-src="hero.jpg" loading="lazy" alt="Hero" />
// Or use Next.js Image
import Image from 'next/image'
;<Image src="/hero.jpg" width={1920} height={1080} placeholder="blur" alt="Hero" />
```
### 3. Asset CDN
```typescript
// Use CDN for assets
const CDN_URL = process.env.CDN_URL || ''
export function getAssetUrl(path: string): string {
if (CDN_URL) {
return `${CDN_URL}${path}`
}
return path
}
// Usage
<img src={getAssetUrl('/images/hero.jpg')} alt="Hero" />
```
### 4. Automated Asset Pipeline
```typescript
// scripts/asset-pipeline.ts
async function runAssetPipeline() {
console.log('Starting asset pipeline...')
// 1. Organize assets
await organizeAssets('./unsorted')
// 2. Optimize images
await optimizeImages('./assets/images/raw', './assets/images/optimized')
// 3. Generate responsive images
await generateResponsiveImages('./assets/images/optimized')
// 4. Optimize fonts
await optimizeFonts('./assets/fonts/raw', './assets/fonts/optimized')
// 5. Version assets
const manager = new AssetVersionManager()
await manager.load()
await manager.trackDirectory('./assets')
await manager.save()
console.log('Asset pipeline complete!')
}
runAssetPipeline()
```
---
## Tools & Resources
**Optimization Tools:**
- [Sharp](https://sharp.pixelplumbing.com/) - Image processing
- [ImageOptim](https://imageoptim.com/) - Image compression
- [SVGO](https://github.com/svg/svgo) - SVG optimization
- [Squoosh](https://squoosh.app/) - Online image compression
**Font Tools:**
- [Glyphhanger](https://github.com/zachleat/glyphhanger) - Font subsetting
- [Transfonter](https://transfonter.org/) - Font conversion
- [FontForge](https://fontforge.org/) - Font editor
**Related Skills:**
- `visual-designer` - Design principles
- `figma-developer` - Export from Figma
- `brand-designer` - Brand asset creation
---
**Organized assets, optimized performance.** 📦Related Skills
asset-placement-strategy
Specialized logic for strategic placement of Northcote Curio design artifacts (Seeds, Pebbles, Lenses, Stones) based on biological asymmetry and curiosity specimen aesthetics. Ensures organic balance and avoids "slop" by enforcing strict archetype constraints and structural integrity.
subagent_manager
Manager skill that delegates all tasks to sub-agents for parallel execution and timely progress updates
skill-manager
Manage, sync, and publish Agent Skills across multiple AI platforms (Claude, Codex, Gemini, Copilot) and marketplace repositories. Use when users want to list skills, sync between platforms, publish to marketplace keys, mirror canonical skills, audit drift, or set up their environment. Triggers on phrases like "list skills", "sync skills", "publish skill", "skill marketplace", "deploy skill", "audit skills", or "skill inventory".
risk-manager
Monitor portfolio risk, R-multiples, and position limits. Creates hedging strategies, calculates expectancy, and implements stop-losses. Use PROACTIVELY for risk assessment, trade tracking, or portfolio protection.
naiba-openai-managers
ChatGPT use cases and prompts for managers | Part of naiba-openai-work-assistant
llm-manager
Claude acts as manager/architect while delegating all coding to external LLM CLIs (Gemini, Codex, Qwen). Claude never writes code - only plans, delegates, and verifies. Use when user says "manage", "architect mode", "delegate to", or wants Claude to drive another LLM.
data-pipeline-manager
Design and troubleshoot robust data pipelines with comprehensive quality validation, error handling, and monitoring capabilities for bioinformatics and data processing workflows
asset-tracking
Use when managing asset metadata, dependencies, and delivery workflows across teams.
asset-forge
Creates new skills, rules, and MCPs for ai-driven-dev-system or project-specific use. Use when user requests a new reusable component, wants to add coding standards, needs to document a workflow, or asks to create a skill or rule.
asset-bundles
Create and configure Databricks Asset Bundles (DABs) with best practices for multi-environment deployments. Use when working with: (1) Creating new DAB projects, (2) Adding resources (dashboards, pipelines, jobs, alerts), (3) Configuring multi-environment deployments, (4) Setting up permissions, (5) Deploying or running bundle resources
agent-risk-manager
Expert risk manager specializing in comprehensive risk assessment, mitigation strategies, and compliance frameworks. Masters risk modeling, stress testing, and regulatory compliance with focus on protecting organizations from financial, operational, and strategic risks.
agent-project-manager
Expert project manager specializing in project planning, execution, and delivery. Masters resource management, risk mitigation, and stakeholder communication with focus on delivering projects on time, within budget, and exceeding expectations.