Sanitize SVGs, auto-generate AI metadata, and export ready-to-upload packages for microstock marketplaces — from your terminal.
VectorStock CLI is a Node.js/TypeScript command-line tool for designers and developers who sell vector assets on Adobe Stock, Freepik, and Shutterstock. It sanitizes your SVGs to a marketplace-ready standard (flattens nested transforms, strips editor cruft, guarantees a valid viewBox), uses AI — local via Ollama, or OpenAI/Anthropic — to generate commercial titles and 30-50 SEO tags per asset, and exports the ZIP + CSV files each marketplace expects for bulk upload.
$ vector-stock run ./my-svgs --zip
→ Sanitizing SVGs from "./my-svgs" -> "./vectorstock-output/sanitized"
done coffee-cup.svg (44% smaller, viewBox added)
done mountain-icon.svg (61% smaller, viewBox added)
info Manifest written (2 sanitized asset(s), 0 failed).
→ Generating AI metadata via "ollama" for 2 asset(s)
done coffee-cup.svg: "Hand-drawn coffee cup with steam, flat vector icon" (32 tags)
done mountain-icon.svg: "Minimalist mountain range line icon" (35 tags)
→ Exporting CSV metadata for: adobe-stock, shutterstock, freepik
done Wrote ./vectorstock-output/adobe-stock.csv
done Wrote ./vectorstock-output/shutterstock.csv
done Wrote ./vectorstock-output/freepik.csv
→ Packaging 2 asset(s) into "./vectorstock-output/vectorstock-package.zip"
done Wrote ./vectorstock-output/vectorstock-package.zip
done Pipeline complete.
- From 2 hours to 10 seconds. Tagging 100 SVGs by hand for three different marketplaces is the single most tedious part of selling vector assets. One command replaces it.
- Real sanitization, not a linter. Built on SVGO with a preset tuned for stock submissions: nested
<g transform>chains collapse to a single, minimal transform (or disappear entirely when possible), editor metadata/comments are stripped, and a missingviewBoxis safely derived fromwidth/heightrather than silently dropped. - AI tagging with a local-first option. Point it at a local Ollama model and generate metadata with zero API cost and no data leaving your machine — or use OpenAI/Anthropic when you want a stronger model.
- Never blocks on AI. If Ollama isn't running or an API key is missing, tagging degrades to deterministic, structure-derived metadata (filename, detected shapes, color palette, aspect ratio) instead of failing your whole batch — you still get a usable result, clearly flagged as a fallback.
- The exact CSV shape each marketplace expects. Adobe Stock, Shutterstock, and Freepik each require different column layouts — VectorStock CLI ships one exporter per marketplace instead of a generic "keywords" dump.
- Solid error handling. Malformed SVGs, unreachable AI providers, missing config keys, and empty batches all fail with a specific, actionable message — never a silent no-op or a stack trace.
No install needed — run it directly with npx:
npx vector-stock-cli init # writes .stockrc.json in the current directory
npx vector-stock-cli sanitize ./my-svgs # clean + validate SVGs
npx vector-stock-cli tag # AI-generate titles & tags
npx vector-stock-cli export # write CSVs for your configured marketplaces
npx vector-stock-cli package # bundle everything into a ZIPOr run the whole pipeline in one command:
npx vector-stock-cli run ./my-svgs --zip --marketplaces adobe-stock,shutterstockPrefer a local install? npm install --save-dev vector-stock-cli and use the vector-stock bin the same way.
| Command | Description |
|---|---|
vector-stock init [--force] |
Writes a .stockrc.json config file in the current directory. |
vector-stock sanitize <input> |
Cleans and validates SVGs, writing sanitized copies + a manifest. |
vector-stock tag [--force] |
Generates AI titles/tags for the manifest's assets (--force re-tags assets that already have metadata). |
vector-stock export [-m <list>] |
Writes marketplace CSV(s) from the manifest. |
vector-stock package |
Bundles sanitized assets (+ any matching .eps/.png sidecar files) into a ZIP. |
vector-stock run <input> [--zip] [-m <list>] |
Runs sanitize → tag → export → (optionally) package in one command. |
Every command accepts -c, --config <path> to use a config file other than ./.stockrc.json.
{
"aiProvider": "ollama",
"ollama": { "host": "http://localhost:11434", "model": "llama3.1", "timeoutMs": 30000 },
"openai": { "model": "gpt-4o-mini", "apiKeyEnvVar": "OPENAI_API_KEY", "timeoutMs": 30000 },
"anthropic": { "model": "claude-3-5-haiku-latest", "apiKeyEnvVar": "ANTHROPIC_API_KEY", "timeoutMs": 30000 },
"tags": { "min": 30, "max": 50 },
"title": { "maxLength": 200 },
"marketplaces": ["adobe-stock", "shutterstock", "freepik"],
"output": {
"sanitizedDir": "./vectorstock-output/sanitized",
"manifestFile": "./vectorstock-output/manifest.json",
"zipFile": "./vectorstock-output/vectorstock-package.zip",
"csvDir": "./vectorstock-output"
},
"sanitizer": { "precision": 3, "removeTextElements": false }
}Switch aiProvider to "openai" or "anthropic" to use a hosted model instead of Ollama — set the corresponding API key in the environment variable named by apiKeyEnvVar (OPENAI_API_KEY / ANTHROPIC_API_KEY by default).
| Marketplace | CSV columns |
|---|---|
| Adobe Stock | Filename, Title, Keywords, Category, Releases |
| Shutterstock | Filename, Description, Keywords, Categories, Editorial, Mature content, Illustration |
| Freepik | File name, Title, Keywords |
Column layouts follow each marketplace's publicly documented contributor bulk-metadata CSV template at the time of writing. Marketplaces occasionally revise their required columns — always verify against the current template in your contributor dashboard before a large bulk upload.
- Optimize — runs SVGO with
convertTransform+collapseGroupsto collapse nested transform chains into a single, minimal transform (or eliminate it entirely for single-child groups), strips comments/metadata/editor namespaces, and rounds path precision without visibly degrading curves. - Validate the viewBox — a missing
viewBoxis derived fromwidth/heightwhen both are present; when neither is usable, sanitization fails loudly for that file rather than guessing. - Flag what it can't safely auto-fix — embedded raster
<image>data and live<text>elements are flagged as warnings (most marketplaces require text outlined to paths, which needs a font-aware vector editor, not a CLI heuristic). Setsanitizer.removeTextElements: trueto strip<text>automatically instead of just flagging it.
VectorStock CLI does not perform computer vision on your artwork. Instead, it extracts a structural fingerprint directly from the sanitized SVG's markup — shape composition (circle×3, path×12, ...), the dominant colors actually used in fill/stroke, viewBox aspect ratio, gradient presence, and overall complexity — combines it with a filename-derived hint, and sends that structured summary to your configured LLM with a prompt asking for a commercial title and a 30-50 keyword list. This is honest about what it is: a strong, real signal for inferring subject matter without requiring a vision model or any GPU, while staying fast and free to run locally via Ollama.
Issues and pull requests are welcome. Before opening a PR:
npm install
npm run typecheck # tsc --noEmit
npm test # vitest run — unit tests for the sanitizer, structural analyzer, tagger, and CSV exporters
npm run build # compiles src/ to dist/Please add or update a test for any behavior change — the sanitizer, structural analyzer, and CSV exporters are all covered by real fixtures (actual SVG markup, actual parsed output), not mocks, and new marketplace exporters or sanitizer rules should follow that pattern.
vector-stock-cli/
├── bin/vector-stock.js # Stable CLI entry point (requires dist/cli.js)
├── src/
│ ├── cli.ts # Commander command definitions
│ ├── types.ts # Shared types
│ ├── config/ # .stockrc.json loading + validation
│ ├── sanitizer/svg.ts # SVGO-based cleanup, viewBox repair, warnings
│ ├── analyzer/structural.ts # Structural fingerprint extraction (shapes, colors, complexity)
│ ├── ai/tagger.ts # Ollama / OpenAI / Anthropic metadata generation + fallback
│ ├── exporters/csv.ts # Per-marketplace CSV templates
│ ├── packager/zip.ts # ZIP packaging (+ .eps/.png sidecar bundling)
│ └── utils/ # Logging, manifest read/write
├── tests/ # vitest unit tests
├── .stockrc.json.example
└── .github/workflows/test.yml # Typecheck, test, build, and an end-to-end CLI smoke test