Creating Packs
Complete guide to creating community packs: pack.yml manifest, validators, agents, knowledge, and publishing.
What is a Pack?
A pack is a modular extension that adds language-specific or framework-specific capabilities to AI Craftsman Superpowers. Packs can include:
- Rules: Validation patterns for your language/framework
- Agents: Specialized agents with domain expertise
- Commands: Custom workflow commands
- Canonical examples: Iron Law templates for code generation
- Knowledge: Guides and architectural patterns
Community packs are available for Go, Rust, Python, and more.
Pack Structure
packs/my-pack/
├── pack.yml # Pack manifest (required)
├── agents/ # Pack-specific agents
│ └── my-agent.md
├── commands/ # Pack-specific commands
│ └── my-command.md
├── knowledge/
│ ├── canonical/ # Iron Law template examples
│ │ └── my-example.ext
│ └── guides/ # Knowledge documents
│ └── patterns.md
├── hooks/ # Validation scripts
│ └── validators/
│ └── validate-my-rules.sh
├── templates/ # Code scaffolding templates
│ └── entity.tpl
├── static-analysis/ # Analysis rule configs
│ └── ruleset.xml
└── tests/ # Pack validation tests
└── validate-pack.test.sh
pack.yml Manifest
The pack manifest is the entry point. All fields are required unless marked optional.
name: "go-craftsman" # Unique pack identifier (kebab-case)
version: "1.0.0" # Semantic version
description: "Go language pack with idiomatic patterns and Clean Architecture"
author: "your-github-handle"
license: "Apache-2.0"
# Minimum plugin version required
requires_plugin: "2.4.0"
# Language/framework support
languages:
- go
# Agents this pack provides
agents:
- name: "go-craftsman"
file: "agents/go-craftsman.md"
description: "Senior Go expert: idiomatic patterns, Clean Architecture"
# Commands this pack provides
commands:
- name: "scaffold-handler"
file: "commands/scaffold-handler.md"
description: "Scaffold HTTP handler with validation and error handling"
# Rules this pack adds (loaded into rules engine)
rules:
go:
error_wrapping: true # Always wrap errors with context
no_panic: "warn" # Avoid panic in non-main packages
interface_segregation: true # Small, focused interfaces
# Canonical examples for Iron Law pattern
canonical_examples:
- name: "go-entity"
file: "knowledge/canonical/go-entity.go"
description: "Domain entity with value objects"
- name: "go-repository"
file: "knowledge/canonical/go-repository.go"
description: "Repository interface + in-memory implementation"
# Knowledge documents loaded into agent context
knowledge:
- file: "knowledge/guides/go-patterns.md"
tags: ["patterns", "idioms", "clean-architecture"]
# Hooks to register
hooks:
pre_write:
- file: "hooks/validators/validate-go.sh"
languages: ["go"]
post_write:
- file: "hooks/validators/static-analysis.sh"
languages: ["go"]
Writing Validators
Pack validators are shell scripts that receive the file path and content via stdin:
#!/usr/bin/env bash
# hooks/validators/validate-go.sh
# Called by hooks with: validate-go.sh <file_path>
set -euo pipefail
FILE="$1"
# Load rules engine
source "$(dirname "$0")/../../lib/rules-engine.sh"
rules_init
# Check error wrapping rule
SEVERITY=$(rules_severity_for_file "go.error_wrapping" "$FILE")
if [[ "$SEVERITY" == "block" || "$SEVERITY" == "warn" ]]; then
# Check for unhandled errors (simplified)
if grep -qP 'err\s*:?=.*\n\s*if err' "$FILE" 2>/dev/null; then
if [[ "$SEVERITY" == "block" ]]; then
echo "✗ GO001: Always wrap errors with context (fmt.Errorf(\"...: %w\", err))"
exit 2 # Block
else
echo "⚠ GO001: Consider wrapping error with context"
exit 0 # Warn but allow
fi
fi
fi
echo "✓ Go validation passed"
exit 0
Exit codes
Use exit 2 to block the write (Level 1 violation). Use exit 0 to pass (or warn). The rules engine interprets exit codes: 0 = pass, 1 = warn, 2 = block.
Writing Agents
Pack agents follow the same structure as core agents:
---
name: go-craftsman
description: Senior Go expert: idiomatic patterns, Clean Architecture, error handling
model: claude-sonnet-4-6
effort: medium
memory: project
maxTurns: 20
allowedTools:
- Read
- Glob
- Grep
- Bash
- Write
- Edit
---
# Go Craftsman Agent
You are a Senior Go Software Craftsman with 10+ years of experience in idiomatic Go, Clean Architecture, and production systems.
## Your Standards
- **Error handling**: Always wrap errors with context (`fmt.Errorf("operation: %w", err)`)
- **Interfaces**: Define interfaces at the point of use (consumer-side)
- **Packages**: One responsibility per package, avoid circular dependencies
- **Testing**: Table-driven tests, testify for assertions, httptest for HTTP handlers
## When Reviewing Code
Check for:
1. Error handling completeness (no ignored errors)
2. Interface segregation (small, focused interfaces)
3. Context propagation (ctx as first param)
4. Goroutine lifecycle management
5. Proper use of defer for cleanup
Writing Canonical Examples
Canonical examples are the templates Claude loads before generating code (Iron Law pattern). They must represent production-quality, idiomatic code:
// knowledge/canonical/go-entity.go
// CANONICAL: Go Domain Entity with Value Objects
// This is the reference implementation. Load this before generating any Go entity.
package domain
import (
"errors"
"time"
)
// UserID is a branded type for user identifiers
type UserID string
func NewUserID(id string) (UserID, error) {
if id == "" {
return "", errors.New("user ID cannot be empty")
}
return UserID(id), nil
}
// Email is a value object with validation
type Email struct {
value string
}
func NewEmail(email string) (Email, error) {
// validation logic
return Email{value: email}, nil
}
func (e Email) String() string { return e.value }
// User is a domain entity: immutable after creation
type User struct {
id UserID
email Email
createdAt time.Time
}
// Create is the factory function (no public constructor)
func Create(id UserID, email Email) (*User, error) {
return &User{
id: id,
email: email,
createdAt: time.Now(),
}, nil
}
// Behavioral methods: no setters
func (u *User) ChangeEmail(email Email) {
u.email = email
}
Testing Your Pack
Create a test script to validate your pack:
#!/usr/bin/env bash
# tests/validate-pack.test.sh
set -euo pipefail
PACK_DIR="$(dirname "$0")/.."
PASS=0
FAIL=0
run_test() {
local name="$1"
local expected="$2"
local actual="$3"
if [[ "$actual" == "$expected" ]]; then
echo " ✓ $name"
((PASS++))
else
echo " ✗ $name (expected: $expected, got: $actual)"
((FAIL++))
fi
}
echo "Testing go-craftsman pack..."
# Test pack.yml is valid YAML
python3 -c "import yaml; yaml.safe_load(open('$PACK_DIR/pack.yml'))" 2>/dev/null
run_test "pack.yml is valid YAML" "0" "$?"
# Test validator script exists and is executable
test -x "$PACK_DIR/hooks/validators/validate-go.sh"
run_test "validator is executable" "0" "$?"
# Test canonical example exists
test -f "$PACK_DIR/knowledge/canonical/go-entity.go"
run_test "canonical example exists" "0" "$?"
echo ""
echo "Results: $PASS passed, $FAIL failed"
[[ $FAIL -eq 0 ]] && exit 0 || exit 1
Run validation with:
claude plugin validate ./packs/my-pack
Publishing Your Pack
- Validate: Run
claude plugin validate ./packs/my-packand fix all issues - Test: Run the pack test suite:
bash tests/validate-pack.test.sh - Fork: Fork the ai-craftsman-superpowers repository
- Add: Place your pack in
packs/my-pack/ - PR: Open a pull request with a description of what your pack adds
Pack skeletons available
Run /craftsman:scaffold pack to generate a pack skeleton with all required files pre-populated. The scaffold uses the Go pack skeleton as a reference template.
Using External Packs
Point to a local pack path in your .craft-config.yml:
packs:
core: true
external:
- path: "~/my-custom-pack" # Local path
- path: "/usr/local/share/go-pack" # System path
External packs are loaded by the pack-loader.sh script at session start.