adapter-factory
Guide for creating new CLI or HTTP adapters to integrate AI models into the AI Counsel deliberation system
Best use case
adapter-factory is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Guide for creating new CLI or HTTP adapters to integrate AI models into the AI Counsel deliberation system
Teams using adapter-factory 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/adapter-factory/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How adapter-factory Compares
| Feature / Agent | adapter-factory | 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?
Guide for creating new CLI or HTTP adapters to integrate AI models into the AI Counsel deliberation system
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
# Adapter Factory Skill
This skill teaches how to integrate new AI models into the AI Counsel MCP server by creating adapters. There are two types of adapters:
1. **CLI Adapters** - For command-line AI tools (e.g., `claude`, `droid`, `codex`)
2. **HTTP Adapters** - For HTTP API integrations (e.g., Ollama, LM Studio, OpenRouter)
## When to Use This Skill
Use this skill when you need to:
- Add support for a new AI model or service to participate in deliberations
- Integrate a command-line AI tool (CLI adapter)
- Integrate an HTTP-based AI API (HTTP adapter)
- Understand the adapter pattern used in AI Counsel
- Debug or modify existing adapters
## Architecture Overview
### Base Classes
**BaseCLIAdapter** (`adapters/base.py`)
- Handles subprocess execution, timeout management, and error handling
- Provides `invoke()` method that manages the full CLI lifecycle
- Subclasses only implement `parse_output()` for tool-specific parsing
- Optional: Override `_adjust_args_for_context()` for deliberation vs. non-deliberation behavior
- Optional: Implement `validate_prompt_length()` for length limits
**BaseHTTPAdapter** (`adapters/base_http.py`)
- Handles HTTP requests, retry logic with exponential backoff, and timeout management
- Uses `httpx` for async HTTP and `tenacity` for retry logic
- Retries on 5xx/429/network errors, fails fast on 4xx client errors
- Subclasses implement `build_request()` and `parse_response()`
- Supports environment variable substitution for API keys
### Factory Pattern
The `create_adapter()` function in `adapters/__init__.py`:
- Maintains registries for CLI and HTTP adapters
- Creates appropriate adapter instances from config
- Handles backward compatibility with legacy config formats
---
## Creating a CLI Adapter
Follow these 6 steps to add a new command-line AI tool:
### Step 1: Create Adapter File
Create `adapters/your_cli.py`:
```python
"""Your CLI tool adapter."""
from adapters.base import BaseCLIAdapter
class YourCLIAdapter(BaseCLIAdapter):
"""Adapter for your-cli tool."""
def parse_output(self, raw_output: str) -> str:
"""
Parse your-cli output to extract model response.
Args:
raw_output: Raw stdout from CLI tool
Returns:
Parsed model response text
"""
# Example: Strip headers and extract main response
lines = raw_output.strip().split("\n")
# Skip header lines (tool-specific logic)
start_idx = 0
for i, line in enumerate(lines):
if line.strip() and not line.startswith("Loading"):
start_idx = i
break
return "\n".join(lines[start_idx:]).strip()
```
**Optional:** Override `_adjust_args_for_context()` if your CLI needs different args for deliberation vs. regular use:
```python
def _adjust_args_for_context(self, is_deliberation: bool) -> list[str]:
"""
Adjust CLI arguments based on context.
Args:
is_deliberation: True if part of multi-model deliberation
Returns:
Adjusted argument list
"""
args = self.args.copy()
if is_deliberation:
# Remove flags that interfere with deliberation
if "--interactive" in args:
args.remove("--interactive")
else:
# Add flags for regular Claude Code work
if "--context" not in args:
args.append("--context")
return args
```
**Optional:** Implement `validate_prompt_length()` if your API has length limits:
```python
MAX_PROMPT_CHARS = 100000 # Example: 100k char limit
def validate_prompt_length(self, prompt: str) -> bool:
"""
Validate prompt length against API limits.
Args:
prompt: The full prompt to validate
Returns:
True if valid, False if too long
"""
return len(prompt) <= self.MAX_PROMPT_CHARS
```
### Step 2: Update Config
Add your CLI to `config.yaml`:
```yaml
adapters:
your_cli:
type: cli
command: "your-cli"
args: ["--model", "{model}", "{prompt}"]
timeout: 60 # Adjust based on your model's speed
```
**Placeholder Variables:**
- `{model}` - Replaced with model identifier
- `{prompt}` - Replaced with full prompt text (including context)
**Timeout Guidelines:**
- Fast models (GPT-3.5): 30-60s
- Reasoning models (Claude Sonnet 4.5, GPT-5): 180-300s
- Local models: Varies, test and adjust
### Step 3: Register Adapter
Update `adapters/__init__.py`:
```python
# Add import at top
from adapters.your_cli import YourCLIAdapter
# Add to cli_adapters dict in create_adapter()
cli_adapters: dict[str, Type[BaseCLIAdapter]] = {
"claude": ClaudeAdapter,
"codex": CodexAdapter,
"droid": DroidAdapter,
"gemini": GeminiAdapter,
"llamacpp": LlamaCppAdapter,
"your_cli": YourCLIAdapter, # Add this line
}
# Add to __all__ export list
__all__ = [
"BaseCLIAdapter",
"BaseHTTPAdapter",
"ClaudeAdapter",
"CodexAdapter",
"DroidAdapter",
"GeminiAdapter",
"LlamaCppAdapter",
"YourCLIAdapter", # Add this line
# ... rest of exports
]
```
### Step 4: Update Schema
Update `models/schema.py`:
```python
# Find the Participant class and add your CLI to the Literal type
class Participant(BaseModel):
cli: Literal[
"claude",
"codex",
"droid",
"gemini",
"llamacpp",
"your_cli" # Add this
]
model: str
```
Update the MCP tool description in `server.py`:
```python
# Find RECOMMENDED_MODELS and add your models
RECOMMENDED_MODELS = {
"claude": ["sonnet-4.5", "opus-4"],
"codex": ["gpt-5-codex", "gpt-4o"],
# ... other models
"your_cli": ["your-model-1", "your-model-2"], # Add this
}
```
### Step 5: Add Recommended Models
Update `server.py::RECOMMENDED_MODELS` with suggested models for your CLI:
```python
RECOMMENDED_MODELS = {
# ... existing models
"your_cli": [
"your-fast-model", # For quick responses
"your-reasoning-model", # For complex analysis
],
}
```
### Step 6: Write Tests
Create unit tests in `tests/unit/test_adapters.py`:
```python
import pytest
from adapters.your_cli import YourCLIAdapter
class TestYourCLIAdapter:
def test_parse_output_basic(self):
"""Test basic output parsing."""
adapter = YourCLIAdapter(
command="your-cli",
args=["--model", "{model}", "{prompt}"],
timeout=60
)
raw_output = "Loading...\n\nActual response text here"
result = adapter.parse_output(raw_output)
assert result == "Actual response text here"
assert "Loading" not in result
def test_parse_output_multiline(self):
"""Test multiline response parsing."""
adapter = YourCLIAdapter(
command="your-cli",
args=["--model", "{model}", "{prompt}"],
timeout=60
)
raw_output = "Header\n\nLine 1\nLine 2\nLine 3"
result = adapter.parse_output(raw_output)
assert "Line 1" in result
assert "Line 2" in result
assert "Line 3" in result
```
Add integration tests in `tests/integration/`:
```python
import pytest
from adapters.your_cli import YourCLIAdapter
@pytest.mark.integration
@pytest.mark.asyncio
async def test_your_cli_integration():
"""Test actual CLI invocation (requires your-cli installed)."""
adapter = YourCLIAdapter(
command="your-cli",
args=["--model", "{model}", "{prompt}"],
timeout=60
)
result = await adapter.invoke(
prompt="What is 2+2?",
model="your-default-model"
)
assert result
assert len(result) > 0
# Add assertions specific to your model's response format
```
---
## Creating an HTTP Adapter
Follow these 6 steps to add a new HTTP API integration:
### Step 1: Create Adapter File
Create `adapters/your_adapter.py`:
```python
"""Your API adapter."""
from typing import Tuple
from adapters.base_http import BaseHTTPAdapter
class YourAdapter(BaseHTTPAdapter):
"""
Adapter for Your AI API.
API reference: https://docs.yourapi.com
Default endpoint: https://api.yourservice.com
Example:
adapter = YourAdapter(
base_url="https://api.yourservice.com",
api_key="your-key",
timeout=120
)
result = await adapter.invoke(prompt="Hello", model="your-model")
"""
def build_request(
self, model: str, prompt: str
) -> Tuple[str, dict[str, str], dict]:
"""
Build API request components.
Args:
model: Model identifier (e.g., "your-model-v1")
prompt: The prompt to send
Returns:
Tuple of (endpoint, headers, body):
- endpoint: URL path (e.g., "/v1/chat/completions")
- headers: Request headers dict
- body: Request body dict (will be JSON-encoded)
"""
endpoint = "/v1/chat/completions"
headers = {
"Content-Type": "application/json",
}
# Add authentication if API key provided
if self.api_key:
headers["Authorization"] = f"Bearer {self.api_key}"
# Build request body (adapt to your API format)
body = {
"model": model,
"messages": [
{"role": "user", "content": prompt}
],
"temperature": 0.7,
"stream": False,
}
return (endpoint, headers, body)
def parse_response(self, response_json: dict) -> str:
"""
Parse API response to extract model output.
Your API response format:
{
"id": "resp-123",
"model": "your-model",
"choices": [
{
"message": {
"role": "assistant",
"content": "The model's response text"
}
}
]
}
Args:
response_json: Parsed JSON response from API
Returns:
Extracted model response text
Raises:
KeyError: If response format is unexpected
"""
try:
return response_json["choices"][0]["message"]["content"]
except (KeyError, IndexError) as e:
raise KeyError(
f"Unexpected API response format. "
f"Expected 'choices[0].message.content', "
f"got keys: {list(response_json.keys())}"
) from e
```
### Step 2: Update Config
Add your HTTP adapter to `config.yaml`:
```yaml
adapters:
your_adapter:
type: http
base_url: "https://api.yourservice.com"
api_key: "${YOUR_API_KEY}" # Environment variable substitution
timeout: 120
max_retries: 3
```
**Configuration Options:**
- `base_url`: Base URL for API (no trailing slash)
- `api_key`: API key (use `${ENV_VAR}` for environment variables)
- `timeout`: Request timeout in seconds
- `max_retries`: Max retry attempts for 5xx/429/network errors (default: 3)
- `headers`: Optional default headers dict
**Environment Variable Substitution:**
- Pattern: `${VAR_NAME}` in config
- Substituted at runtime from environment
- Secure: Keeps secrets out of config files
### Step 3: Register Adapter
Update `adapters/__init__.py`:
```python
# Add import at top
from adapters.your_adapter import YourAdapter
# Add to http_adapters dict in create_adapter()
http_adapters: dict[str, Type[BaseHTTPAdapter]] = {
"ollama": OllamaAdapter,
"lmstudio": LMStudioAdapter,
"openrouter": OpenRouterAdapter,
"your_adapter": YourAdapter, # Add this line
}
# Add to __all__ export list
__all__ = [
"BaseCLIAdapter",
"BaseHTTPAdapter",
# ... CLI adapters
"OllamaAdapter",
"LMStudioAdapter",
"OpenRouterAdapter",
"YourAdapter", # Add this line
"create_adapter",
]
```
### Step 4: Set Environment Variables
If your adapter uses API keys:
```bash
# Add to your shell profile (~/.bashrc, ~/.zshrc, etc.)
export YOUR_API_KEY="your-actual-api-key-here"
# Or set temporarily for testing
export YOUR_API_KEY="test-key" && python server.py
```
### Step 5: Write Tests
Create unit tests with VCR for HTTP response recording in `tests/unit/test_your_adapter.py`:
```python
import pytest
import vcr
from adapters.your_adapter import YourAdapter
# Configure VCR for recording HTTP interactions
vcr_instance = vcr.VCR(
cassette_library_dir="tests/fixtures/vcr_cassettes/your_adapter/",
record_mode="once", # Record once, then replay
match_on=["method", "scheme", "host", "port", "path", "query"],
filter_headers=["authorization"], # Hide API keys in recordings
)
class TestYourAdapter:
def test_build_request_basic(self):
"""Test request building without API key."""
adapter = YourAdapter(
base_url="https://api.example.com",
timeout=60
)
endpoint, headers, body = adapter.build_request(
model="test-model",
prompt="Hello"
)
assert endpoint == "/v1/chat/completions"
assert headers["Content-Type"] == "application/json"
assert body["model"] == "test-model"
assert body["messages"][0]["content"] == "Hello"
def test_build_request_with_auth(self):
"""Test request building with API key."""
adapter = YourAdapter(
base_url="https://api.example.com",
api_key="test-key",
timeout=60
)
endpoint, headers, body = adapter.build_request(
model="test-model",
prompt="Hello"
)
assert "Authorization" in headers
assert headers["Authorization"] == "Bearer test-key"
def test_parse_response_success(self):
"""Test parsing successful response."""
adapter = YourAdapter(
base_url="https://api.example.com",
timeout=60
)
response = {
"id": "resp-123",
"choices": [
{
"message": {
"role": "assistant",
"content": "Hello! How can I help?"
}
}
]
}
result = adapter.parse_response(response)
assert result == "Hello! How can I help?"
def test_parse_response_missing_field(self):
"""Test error handling for malformed response."""
adapter = YourAdapter(
base_url="https://api.example.com",
timeout=60
)
response = {"id": "resp-123"} # Missing choices
with pytest.raises(KeyError) as exc_info:
adapter.parse_response(response)
assert "Unexpected API response format" in str(exc_info.value)
@pytest.mark.asyncio
@vcr_instance.use_cassette("invoke_basic.yaml")
async def test_invoke_with_vcr(self):
"""Test full invoke() with VCR recording."""
adapter = YourAdapter(
base_url="https://api.example.com",
api_key="test-key",
timeout=60
)
result = await adapter.invoke(
prompt="What is 2+2?",
model="test-model"
)
assert result
assert len(result) > 0
```
**Optional:** Add integration tests (requires running service):
```python
@pytest.mark.integration
@pytest.mark.asyncio
async def test_your_adapter_live():
"""Test with live API (requires service running and API key)."""
import os
api_key = os.getenv("YOUR_API_KEY")
if not api_key:
pytest.skip("YOUR_API_KEY not set")
adapter = YourAdapter(
base_url="https://api.yourservice.com",
api_key=api_key,
timeout=120
)
result = await adapter.invoke(
prompt="What is the capital of France?",
model="your-model"
)
assert "Paris" in result or "paris" in result
```
### Step 6: Test with Deliberation
Create a simple test script to verify integration:
```python
"""Test your adapter in a deliberation context."""
import asyncio
from adapters.your_adapter import YourAdapter
async def test():
adapter = YourAdapter(
base_url="https://api.yourservice.com",
api_key="your-key",
timeout=120
)
# Test basic invocation
result = await adapter.invoke(
prompt="What is 2+2?",
model="your-model"
)
print(f"Response: {result}")
# Test with context (simulating Round 2+ in deliberation)
result_with_context = await adapter.invoke(
prompt="Do you agree with this answer?",
model="your-model",
context="Previous participant said: 2+2 equals 4"
)
print(f"Response with context: {result_with_context}")
if __name__ == "__main__":
asyncio.run(test())
```
---
## Key Design Principles
### DRY (Don't Repeat Yourself)
- Common logic in base classes (`BaseCLIAdapter`, `BaseHTTPAdapter`)
- Tool-specific logic in concrete adapters
- Only implement what's unique to your adapter
### YAGNI (You Aren't Gonna Need It)
- Build only what's needed for basic integration
- Don't add features until they're required
- Start simple, extend as needed
### TDD (Test-Driven Development)
- Write tests first (red)
- Implement adapter (green)
- Refactor for clarity (refactor)
### Type Safety
- Use type hints throughout
- Pydantic validation where applicable
- Let mypy catch errors early
### Error Isolation
- Adapter failures don't halt deliberations
- Other participants continue if one fails
- Graceful degradation with informative errors
---
## Common Patterns
### CLI Adapter with Custom Parsing
```python
def parse_output(self, raw_output: str) -> str:
"""Parse CLI output with multiple header formats."""
lines = raw_output.strip().split("\n")
# Skip all header lines until we find content
content_started = False
result_lines = []
for line in lines:
# Detect header patterns
if any(marker in line.lower() for marker in ["loading", "initializing", "version"]):
continue
# Detect content start
if line.strip():
content_started = True
if content_started:
result_lines.append(line)
return "\n".join(result_lines).strip()
```
### HTTP Adapter with Streaming Support
```python
def build_request(self, model: str, prompt: str) -> Tuple[str, dict, dict]:
"""Build request with optional streaming."""
# Note: BaseHTTPAdapter doesn't support streaming yet
# This is for future extension
body = {
"model": model,
"prompt": prompt,
"stream": False, # Keep False for now
}
return ("/api/generate", {"Content-Type": "application/json"}, body)
```
### Environment Variable Validation
```python
def __init__(self, base_url: str, api_key: str = None, **kwargs):
"""Initialize with API key validation."""
if not api_key:
raise ValueError(
"API key required. Set YOUR_API_KEY environment variable or "
"provide api_key parameter."
)
super().__init__(base_url=base_url, api_key=api_key, **kwargs)
```
---
## Testing Guidelines
### Unit Tests (Required)
- Test `parse_output()` with various input formats
- Test `build_request()` with different parameters
- Test `parse_response()` with success and error cases
- Mock external dependencies (no actual API calls)
- Fast execution (< 1s total)
### Integration Tests (Optional)
- Test actual CLI/API invocation
- Requires tool installed or service running
- Mark with `@pytest.mark.integration`
- May be slow, use sparingly
### VCR for HTTP Tests
- Record real HTTP interactions once
- Replay from cassettes in CI/CD
- Filter sensitive data (API keys, auth tokens)
- Store in `tests/fixtures/vcr_cassettes/your_adapter/`
### E2E Tests (Optional)
- Full deliberation with your adapter
- Mark with `@pytest.mark.e2e`
- Very slow, expensive (real API calls)
- Use for final validation only
---
## Troubleshooting
### CLI Adapter Issues
**Problem:** Timeout errors
- **Solution:** Increase timeout in config.yaml
- **Reasoning models need 180-300s, not 60s**
**Problem:** Output parsing fails
- **Solution:** Print `raw_output` and examine format
- **Each CLI has unique output format, adjust parsing logic**
**Problem:** Hook interference (Claude CLI)
- **Solution:** Add `--settings '{"disableAllHooks": true}'` to args
- **User hooks can interfere with deliberation invocations**
### HTTP Adapter Issues
**Problem:** Connection refused
- **Solution:** Verify base_url and service is running
- **Check with `curl $BASE_URL/health` or similar**
**Problem:** 401 Authentication errors
- **Solution:** Verify API key is set: `echo $YOUR_API_KEY`
- **Check environment variable substitution in config**
**Problem:** 400 Bad Request errors
- **Solution:** Log request body, check API documentation
- **Common issue: Wrong field names or missing required fields**
**Problem:** Retries exhausted
- **Solution:** Check if service is healthy
- **5xx errors trigger retries, 4xx do not (by design)**
### General Issues
**Problem:** Adapter not found
- **Solution:** Verify registration in `adapters/__init__.py`
- **Check both import and dict addition**
**Problem:** Schema validation errors
- **Solution:** Add CLI name to `Participant.cli` Literal in `models/schema.py`
- **MCP won't accept unlisted CLI names**
---
## Reference Files
### Essential Files
- `adapters/base.py` - CLI adapter base class
- `adapters/base_http.py` - HTTP adapter base class
- `adapters/__init__.py` - Adapter factory and registry
- `models/config.py` - Configuration schema
- `models/schema.py` - Data models and validation
### Example Adapters
- `adapters/claude.py` - CLI adapter with context-aware args
- `adapters/gemini.py` - CLI adapter with length validation
- `adapters/ollama.py` - HTTP adapter for local API
- `adapters/lmstudio.py` - HTTP adapter with OpenAI-compatible format
### Test Examples
- `tests/unit/test_adapters.py` - CLI adapter unit tests
- `tests/unit/test_ollama.py` - HTTP adapter unit tests with VCR
- `tests/integration/test_cli_adapters.py` - CLI integration tests
---
## Next Steps After Creating Adapter
1. **Update CLAUDE.md** if you added new patterns or gotchas
2. **Add to RECOMMENDED_MODELS** in `server.py` with usage guidance
3. **Document API quirks** in adapter docstrings for future maintainers
4. **Test in real deliberation** with 2-3 participants
5. **Monitor transcript** for response quality and voting behavior
6. **Share findings** if you discover best practices for your model
---
## Quick Reference
### CLI Adapter Checklist
- [ ] Create `adapters/your_cli.py` with `parse_output()`
- [ ] Add to `config.yaml` with command, args, timeout
- [ ] Register in `adapters/__init__.py` (import + dict + export)
- [ ] Add to `Participant.cli` Literal in `models/schema.py`
- [ ] Add to `RECOMMENDED_MODELS` in `server.py`
- [ ] Write unit tests for `parse_output()`
- [ ] Optional: Write integration test with real CLI
### HTTP Adapter Checklist
- [ ] Create `adapters/your_adapter.py` with `build_request()` and `parse_response()`
- [ ] Add to `config.yaml` with base_url, api_key, timeout
- [ ] Register in `adapters/__init__.py` (import + dict + export)
- [ ] Set environment variables for API keys
- [ ] Write unit tests with VCR cassettes
- [ ] Test with simple script before full deliberation
### Common Commands
```bash
# Run unit tests for new adapter
pytest tests/unit/test_your_adapter.py -v
# Run with coverage
pytest tests/unit/test_your_adapter.py --cov=adapters.your_adapter
# Format and lint
black adapters/your_adapter.py && ruff check adapters/your_adapter.py
# Test integration (requires tool/service)
pytest tests/integration/ -v -m integration
# Record VCR cassette (first run, then commit)
pytest tests/unit/test_your_adapter.py -v
```
---
## Additional Resources
- **CLAUDE.md** - Full project documentation with architecture details
- **MCP Protocol** - https://modelcontextprotocol.io/introduction
- **Pydantic Docs** - https://docs.pydantic.dev/latest/
- **httpx Docs** - https://www.python-httpx.org/
- **VCR.py Docs** - https://vcrpy.readthedocs.io/
---
This skill encodes the institutional knowledge for extending AI Counsel with new model integrations. Follow the patterns, write tests, and maintain backward compatibility.Related Skills
patterns/adapter
Adapter (Wrapper) Pattern pattern for C development
bio-read-qc-adapter-trimming
Remove sequencing adapters from FASTQ files using Cutadapt and Trimmomatic. Supports single-end and paired-end reads, Illumina TruSeq, Nextera, and custom adapter sequences. Use when FastQC shows adapter contamination or before alignment of short reads.
adapter-expert
Adapter Layer 전문가. CommandAdapter(persist만, JpaRepository 1:1), QueryAdapter(4개 메서드, QueryDslRepository 1:1), AdminQueryAdapter(Join 허용, DTO Projection), LockQueryAdapter(6개 Lock 메서드). 필드 2개만(Repository + Mapper). @Component 필수. @Transactional 절대 금지.
adapter-development
Comprehensive guide for AIDB debug adapter development. Covers component-based architecture, language-specific patterns (Python/debugpy, JavaScript/vscode-js-debug, Java/java-debug), lifecycle hooks, process management, port management, launch orchestration, resource cleanup, child sessions, and common pitfalls. Essential for developing or maintaining AIDB debug adapters.
adapter-configurator
Activate when users need help setting up, configuring, or troubleshooting LimaCharlie adapters to ingest telemetry from cloud services, identity providers, log sources, or other data sources.
acc-create-psr17-http-factory
Generates PSR-17 HTTP Factories implementation for PHP 8.5. Creates RequestFactoryInterface, ResponseFactoryInterface, StreamFactoryInterface, UriFactoryInterface, ServerRequestFactoryInterface, UploadedFileFactoryInterface. Includes unit tests.
acc-create-factory
Generates DDD Factory for PHP 8.5. Creates factories for complex domain object instantiation with validation and encapsulated creation logic. Includes unit tests.
theme-factory
Toolkit for styling artifacts with a theme. These artifacts can be slides, docs, reportings, HTML landing pages, etc. There are 10 pre-set themes with colors/fonts that you can apply to any artifact that has been creating, or can generate a new theme on-the-fly.
grail-miner
This skill assists in setting up, managing, and optimizing Grail miners on Bittensor Subnet 81, handling tasks like environment configuration, R2 storage, model checkpoint management, and performance tuning.
modal-deployment
Run Python code in the cloud with serverless containers, GPUs, and autoscaling using Modal. This skill enables agents to generate code for deploying ML models, running batch jobs, serving APIs, and scaling compute-intensive workloads.
ontopo
An AI agent skill to search for Israeli restaurants, check table availability, view menus, and retrieve booking links via the Ontopo platform, acting as an unofficial interface to its data.
astro
This skill provides essential Astro framework patterns, focusing on server-side rendering (SSR), static site generation (SSG), middleware, and TypeScript best practices. It helps AI agents implement secure authentication, manage API routes, and debug rendering behaviors within Astro projects.