skills/prompt-template-manager/SKILL.md
Version-control, parameterize, and A/B test LLM prompt templates with Git-native workflows. Activate on: prompt versioning, prompt templates, A/B test prompts, manage prompts, prompt registry. NOT for: writing prompts from scratch (prompt-engineer), fine-tuning data (fine-tuning-dataset-curator).
npx skillsauth add curiositech/windags-skills prompt-template-managerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Version-control, parameterize, and A/B test LLM prompt templates using Git-native workflows and structured registries.
Activate on: "prompt versioning", "prompt template", "A/B test prompts", "prompt registry", "manage prompt variants", "prompt as code", "parameterized prompts", "prompt lifecycle"
NOT for: Writing or optimizing individual prompts (prompt-engineer), fine-tuning dataset preparation (fine-tuning-dataset-curator), or LLM application architecture (ai-engineer)
prompts/{domain}/{name}.yaml with metadata, variables, and versioned content.{{variable}} placeholders for dynamic content; separate static instruction from dynamic context.| Domain | Technologies | Notes |
|--------|-------------|-------|
| Template Format | YAML + Jinja2, Handlebars, Mustache | YAML frontmatter for metadata, body for template |
| Version Control | Git tags, branches, semantic versioning | v1.2.0 tags for production, branches for experiments |
| A/B Testing | Feature flags (LaunchDarkly, Unleash, custom) | Percentage-based routing with metrics collection |
| Registry | File-based, PostgreSQL, Redis | Central lookup for template resolution at runtime |
| Evaluation | LLM-as-judge, human eval, RAGAS | Automated comparison of variant outputs |
| Rendering | Jinja2 (Python), Handlebars (JS), custom | Variable interpolation with type validation |
prompts/
├── customer-support/
│ ├── ticket-classifier.yaml # Active production template
│ ├── ticket-classifier.v2.yaml # Experiment variant
│ └── response-generator.yaml
├── content/
│ ├── blog-outline.yaml
│ └── social-post.yaml
└── _shared/
├── system-safety.yaml # Reusable system prompt fragments
└── output-format-json.yaml
# prompts/customer-support/ticket-classifier.yaml
name: ticket-classifier
version: "1.3.0"
model: claude-sonnet-4-20250514
temperature: 0
description: Classify support tickets into categories
variables:
- name: ticket_text
type: string
required: true
- name: categories
type: list
default: [billing, technical, account, other]
includes:
- _shared/output-format-json.yaml
template: |
You are a support ticket classifier.
Classify the following ticket into exactly one category.
Categories: {{categories | join(", ")}}
Ticket:
{{ticket_text}}
{{> output-format-json}}
tests:
- input: { ticket_text: "I can't log in to my account" }
expected_category: "account"
- input: { ticket_text: "You charged me twice" }
expected_category: "billing"
Request ──→ [Router] ──→ Variant A (control, 80%) ──→ [LLM] ──→ Response + Log
│ │
└──→ Variant B (experiment, 20%) ──→ [LLM] ──→ Response + Log
│
▼
[Metrics Store]
- latency
- token count
- quality score
- user feedback
│
▼
[Evaluation]
Winner → promote to 100%
# A/B routing with metrics
import random, time
class PromptRouter:
def __init__(self, registry, metrics):
self.registry = registry
self.metrics = metrics
def resolve(self, template_name: str, user_id: str) -> dict:
variants = self.registry.get_variants(template_name)
# Deterministic assignment by user_id for consistency
bucket = hash(f"{user_id}:{template_name}") % 100
for variant in variants:
if bucket < variant["traffic_pct"]:
self.metrics.log("variant_assigned", {
"template": template_name,
"variant": variant["version"],
"user_id": user_id
})
return variant
bucket -= variant["traffic_pct"]
return variants[0] # Default to control
System Prompt = [safety-preamble] + [role-definition] + [output-format]
User Prompt = [context-injection] + [user-query] + [constraints]
Compose from reusable fragments:
_shared/safety-preamble.yaml ──→ "You must not generate harmful content..."
_shared/json-output.yaml ──→ "Respond with valid JSON matching this schema..."
domain/role.yaml ──→ "You are an expert in {{domain}}..."
Final prompt = render(compose([safety, role, output]), variables)
_shared/ for composition{{placeholders}} in outputdata-ai
license: Apache-2.0 NOT for unrelated tasks outside this domain.
development
Use when designing caching strategies (cache-aside, write-through, write-behind), implementing distributed locks, building rate limiters, leaderboards, real-time streams (XADD/consumer groups), pub/sub, or tuning eviction policies. Triggers: thundering-herd on cache miss, dogpile on key expiry, Redlock vs SET-NX-PX choice, sliding-window rate limiter, hot-key on a single cluster slot, big-key blowup, MULTI/EXEC across slots, KEYS in production. NOT for Redis Cluster operations/admin (different domain), embedded KV (SQLite, leveldb), in-process LRU caches, or Memcached.
tools
Drawing the `'use client'` boundary correctly in React Server Components apps (Next.js App Router, RSC frameworks) — leaf-pushing, slot composition, serialization rules, and environment poisoning prevention. Grounded in react.dev and Next.js 16 docs.
development
Use when designing rate limiting for an API, choosing between token bucket / sliding window / leaky bucket / fixed window, implementing it in Redis, deciding edge (Cloudflare/Upstash) vs origin enforcement, sizing per-user vs per-IP vs per-endpoint quotas, returning the right 429 response with Retry-After, or fixing the boundary-burst bug in fixed-window limiters. Triggers: 429 too many requests, INCR + EXPIRE, ZADD + ZREMRANGEBYSCORE + ZCARD, X-RateLimit-Remaining header, Cloudflare WAF rate limiting rules, Upstash @upstash/ratelimit, leaky bucket shaping vs policing, distributed rate limiter consistency. NOT for DDoS mitigation specifically (different scale), CAPTCHA / bot management, full WAF design, or per-user quota billing.