diff --git a/scripts/templates/transfer/MANIFEST.md b/scripts/templates/transfer/MANIFEST.md new file mode 100644 index 0000000..9751b71 --- /dev/null +++ b/scripts/templates/transfer/MANIFEST.md @@ -0,0 +1,29 @@ +# Agent System Manifest +Export name: {{EXPORT_NAME}} +Export date: {{EXPORT_DATE}} +Generated by: Agent Factory export-system.sh + +## Components + +| Type | File | Checksum (SHA256, first 16) | +|------|------|---------------------------| +| agent | .claude/agents/pipeline-runner.md | a1b2c3d4e5f6a7b8 | +| skill | .claude/skills/my-pipeline/SKILL.md | b2c3d4e5f6a7b8c9 | +| hook | .claude/hooks/pre-tool-use.sh | c3d4e5f6a7b8c9d0 | +| settings | .claude/settings.json | d4e5f6a7b8c9d0e1 | +| automation | automation/run-pipeline.sh | e5f6a7b8c9d0e1f2 | +| context | CLAUDE.md | f6a7b8c9d0e1f2a3 | + +## Requirements + +| Requirement | Value | +|-------------|-------| +| Claude Code version | 1.x or later | +| MCP servers | (list any required MCP servers, e.g. GitHub, Slack) | +| Tools | Bash, Read, Write, Glob, Grep | +| Environment | ANTHROPIC_API_KEY | + +## Notes + +Replace all `{{PLACEHOLDER}}` variables after import. +See transfer/README.md for import instructions. diff --git a/scripts/templates/transfer/README.md b/scripts/templates/transfer/README.md new file mode 100644 index 0000000..05bf4c6 --- /dev/null +++ b/scripts/templates/transfer/README.md @@ -0,0 +1,64 @@ +# Import / Export System + +Portable packaging for agent systems. Export a working agent system from one +project and import it into another, with automatic placeholder substitution. + +## Export + +Pack your current agent system into a tarball: + +```bash +bash scripts/templates/transfer/export-system.sh my-project-name +# Creates: agent-system-my-project-name-2026-04-11.tar.gz +``` + +### What is included + +| Included | Excluded | +|---------|---------| +| `.claude/agents/` | `.env` (secrets) | +| `.claude/skills/` | `*.local.*` files | +| `.claude/hooks/` | `audit.log` | +| `.claude/settings.json` | `cost-events.jsonl` | +| `hooks/` | `memory/` (machine-specific state) | +| `automation/` | `.git/` | +| `scripts/` | | +| `CLAUDE.md` | | + +## Import + +Extract a tarball into a new project directory: + +```bash +# Dry run first: inspect without extracting +tar -tzf agent-system-my-project-name-2026-04-11.tar.gz + +# Import (will stop if files conflict) +bash scripts/templates/transfer/import-system.sh agent-system-my-project-name-2026-04-11.tar.gz + +# Import and overwrite existing files +bash scripts/templates/transfer/import-system.sh agent-system-my-project-name-2026-04-11.tar.gz --force +``` + +The import script will: +1. Read `MANIFEST.md` and verify the archive +2. Check for conflicts with existing files (stops unless `--force`) +3. Extract all files +4. Replace `{{PROJECT_DIR}}` and `{{PROJECT_NAME}}` placeholders +5. Make `.sh` files executable +6. Validate agent frontmatter and hook syntax + +## Customization after import + +1. **Update `CLAUDE.md`** — replace project-specific context, goals, and constraints +2. **Add secrets** — create `.env` with `ANTHROPIC_API_KEY=sk-ant-...` +3. **Review hooks** — check that paths in hook scripts match your environment +4. **Run `/agent-factory:status`** — verify all components load correctly +5. **Run `/agent-factory:evaluate`** — check capability coverage + +## Manifest + +Each export includes `MANIFEST.md` listing every file with a SHA256 checksum +(first 16 hex characters). Use this to verify file integrity after transfer. + +Template manifest: `scripts/templates/transfer/MANIFEST.md` diff --git a/scripts/templates/transfer/export-system.sh b/scripts/templates/transfer/export-system.sh new file mode 100644 index 0000000..1e53f02 --- /dev/null +++ b/scripts/templates/transfer/export-system.sh @@ -0,0 +1,132 @@ +#!/bin/bash +# Agent Factory — export-system.sh +# Packages an agent system into a portable tarball with checksums. +# Usage: bash export-system.sh +# Bash 3.2 compatible. + +set -e + +EXPORT_NAME="${1:-agent-system}" +DATE=$(python3 -c "import datetime; print(datetime.date.today().strftime('%Y-%m-%d'))") +OUTPUT_FILE="agent-system-${EXPORT_NAME}-${DATE}.tar.gz" +STAGING_DIR="/tmp/agent-export-$$" +MANIFEST_FILE="${STAGING_DIR}/MANIFEST.md" + +# Determine project root (directory containing .claude/) +PROJECT_ROOT="$(pwd)" +if [ ! -d "${PROJECT_ROOT}/.claude" ]; then + echo "ERROR: No .claude/ directory found in $(pwd)" >&2 + echo "Run this script from your project root." >&2 + exit 1 +fi + +echo "[export] Starting export: ${EXPORT_NAME}" +echo "[export] Date: ${DATE}" +echo "[export] Output: ${OUTPUT_FILE}" + +mkdir -p "${STAGING_DIR}" + +# Collect files — explicit inclusions only +collect_files() { + local dir="$1" + local dest="$2" + if [ -d "${PROJECT_ROOT}/${dir}" ]; then + mkdir -p "${STAGING_DIR}/${dest}" + cp -r "${PROJECT_ROOT}/${dir}/." "${STAGING_DIR}/${dest}/" 2>/dev/null || true + echo "[export] Collected: ${dir}" + fi +} + +collect_files ".claude/agents" ".claude/agents" +collect_files ".claude/skills" ".claude/skills" +collect_files ".claude/hooks" ".claude/hooks" +collect_files "hooks" "hooks" +collect_files "automation" "automation" +collect_files "scripts" "scripts" + +# Copy settings.json if it exists +if [ -f "${PROJECT_ROOT}/.claude/settings.json" ]; then + mkdir -p "${STAGING_DIR}/.claude" + cp "${PROJECT_ROOT}/.claude/settings.json" "${STAGING_DIR}/.claude/settings.json" + echo "[export] Collected: .claude/settings.json" +fi + +# Copy CLAUDE.md if it exists +if [ -f "${PROJECT_ROOT}/CLAUDE.md" ]; then + cp "${PROJECT_ROOT}/CLAUDE.md" "${STAGING_DIR}/CLAUDE.md" + echo "[export] Collected: CLAUDE.md" +fi + +# Remove excluded files from staging +echo "[export] Removing excluded files" +rm -f "${STAGING_DIR}/.env" 2>/dev/null || true +find "${STAGING_DIR}" -name "*.local.*" -delete 2>/dev/null || true +find "${STAGING_DIR}" -name "audit.log" -delete 2>/dev/null || true +find "${STAGING_DIR}" -name "cost-events.jsonl" -delete 2>/dev/null || true +find "${STAGING_DIR}" -name ".git" -prune -o -print | grep "\.git$" | xargs rm -rf 2>/dev/null || true +rm -rf "${STAGING_DIR}/memory" 2>/dev/null || true + +# Generate MANIFEST.md with checksums +echo "[export] Generating manifest" + +COMPONENT_ROWS="" +find "${STAGING_DIR}" -type f | sort | while read -r fpath; do + rel="${fpath#${STAGING_DIR}/}" + checksum=$(python3 -c "import hashlib; print(hashlib.sha256(open('${fpath}','rb').read()).hexdigest()[:16])") + filetype="other" + case "$rel" in + .claude/agents/*) filetype="agent" ;; + .claude/skills/*) filetype="skill" ;; + .claude/hooks/*|hooks/*) filetype="hook" ;; + .claude/settings.json) filetype="settings" ;; + automation/*) filetype="automation" ;; + CLAUDE.md) filetype="context" ;; + esac + echo "| ${filetype} | ${rel} | ${checksum} |" >> "${MANIFEST_FILE}.rows" +done + +cat > "${MANIFEST_FILE}" << MANIFEST +# Agent System Manifest +Export name: ${EXPORT_NAME} +Export date: ${DATE} +Generated by: Agent Factory export-system.sh + +## Components + +| Type | File | Checksum (SHA256, first 16) | +|------|------|---------------------------| +MANIFEST + +if [ -f "${MANIFEST_FILE}.rows" ]; then + cat "${MANIFEST_FILE}.rows" >> "${MANIFEST_FILE}" + rm "${MANIFEST_FILE}.rows" +fi + +cat >> "${MANIFEST_FILE}" << MANIFEST + +## Requirements + +| Requirement | Value | +|-------------|-------| +| Claude Code version | 1.x or later | +| MCP servers | List any required MCP servers here | +| Tools | List any required tools (Bash, WebSearch, etc.) | +| Environment | List required env vars (ANTHROPIC_API_KEY, etc.) | + +## Notes + +Add any project-specific setup notes here. +MANIFEST + +echo "[export] Manifest written" + +# Create tarball +cd "${STAGING_DIR}" +tar -czf "${PROJECT_ROOT}/${OUTPUT_FILE}" . +cd "${PROJECT_ROOT}" + +# Cleanup +rm -rf "${STAGING_DIR}" + +echo "[export] Done: ${OUTPUT_FILE}" +echo "[export] Size: $(du -sh "${OUTPUT_FILE}" | cut -f1)" diff --git a/scripts/templates/transfer/import-system.sh b/scripts/templates/transfer/import-system.sh new file mode 100644 index 0000000..5949df1 --- /dev/null +++ b/scripts/templates/transfer/import-system.sh @@ -0,0 +1,147 @@ +#!/bin/bash +# Agent Factory — import-system.sh +# Imports an exported agent system tarball into the current project. +# Usage: bash import-system.sh [--force] +# Bash 3.2 compatible. + +set -e + +TARBALL="$1" +FORCE="${2:-}" +STAGING_DIR="/tmp/agent-import-$$" + +if [ -z "$TARBALL" ]; then + echo "Usage: bash import-system.sh [--force]" >&2 + exit 1 +fi + +if [ ! -f "$TARBALL" ]; then + echo "ERROR: File not found: $TARBALL" >&2 + exit 1 +fi + +echo "[import] Importing: $TARBALL" + +mkdir -p "$STAGING_DIR" +tar -xzf "$TARBALL" -C "$STAGING_DIR" + +# Read MANIFEST.md +MANIFEST="${STAGING_DIR}/MANIFEST.md" +if [ ! -f "$MANIFEST" ]; then + echo "ERROR: No MANIFEST.md found in tarball — may not be a valid Agent Factory export" >&2 + rm -rf "$STAGING_DIR" + exit 1 +fi + +echo "[import] Manifest found:" +head -5 "$MANIFEST" +echo "" + +# Check for conflicts (existing files in destination) +CONFLICTS="" +find "$STAGING_DIR" -type f | sort | while read -r fpath; do + rel="${fpath#${STAGING_DIR}/}" + dest="${PWD}/${rel}" + if [ -f "$dest" ] && [ "$FORCE" != "--force" ]; then + echo "[import] CONFLICT: $rel already exists" + CONFLICTS="yes" + fi +done + +if [ -n "$CONFLICTS" ] && [ "$FORCE" != "--force" ]; then + echo "" + echo "Conflicts detected. Re-run with --force to overwrite, or remove conflicting files first." >&2 + rm -rf "$STAGING_DIR" + exit 1 +fi + +# Extract files to project +echo "[import] Extracting files" +find "$STAGING_DIR" -type f | sort | while read -r fpath; do + rel="${fpath#${STAGING_DIR}/}" + dest="${PWD}/${rel}" + destdir="$(dirname "$dest")" + mkdir -p "$destdir" + cp "$fpath" "$dest" + echo "[import] Wrote: $rel" +done + +# Replace {{PLACEHOLDER}} variables +echo "[import] Replacing placeholders" +PROJECT_DIR="$(pwd)" +PROJECT_NAME="$(basename "$PROJECT_DIR")" + +find "$PROJECT_DIR/.claude" "$PROJECT_DIR/automation" "$PROJECT_DIR/hooks" \ + "$PROJECT_DIR/CLAUDE.md" -type f 2>/dev/null | while read -r fpath; do + case "$fpath" in + *.sh|*.md|*.json|*.yml|*.yaml) + python3 - "$fpath" "$PROJECT_DIR" "$PROJECT_NAME" << 'PYEOF' +import sys, re +path, project_dir, project_name = sys.argv[1], sys.argv[2], sys.argv[3] +try: + with open(path, 'r', encoding='utf-8') as f: + content = f.read() + content = content.replace('{{PROJECT_DIR}}', project_dir) + content = content.replace('{{PROJECT_NAME}}', project_name) + with open(path, 'w', encoding='utf-8') as f: + f.write(content) +except Exception: + pass +PYEOF + ;; + esac +done + +# Make .sh files executable +echo "[import] Setting executable permissions" +find "$PROJECT_DIR/.claude/hooks" "$PROJECT_DIR/hooks" "$PROJECT_DIR/automation" \ + -name "*.sh" -type f 2>/dev/null | while read -r fpath; do + chmod +x "$fpath" + echo "[import] chmod +x: $fpath" +done + +# Validate frontmatter in agent files +echo "[import] Validating agent frontmatter" +INVALID_AGENTS="" +if [ -d "$PROJECT_DIR/.claude/agents" ]; then + for agent in "$PROJECT_DIR/.claude/agents"/*.md; do + [ -f "$agent" ] || continue + python3 - "$agent" << 'PYEOF' +import sys, re +path = sys.argv[1] +try: + with open(path, 'r') as f: + content = f.read() + if not content.startswith('---'): + print(f"WARN: {path} missing YAML frontmatter") + sys.exit(0) + parts = content.split('---', 2) + if len(parts) < 3: + print(f"WARN: {path} malformed frontmatter") +except Exception as e: + print(f"WARN: {path} could not be validated: {e}") +PYEOF + done +fi + +# Validate bash syntax on hook scripts +echo "[import] Validating hook syntax" +if [ -d "$PROJECT_DIR/.claude/hooks" ]; then + for script in "$PROJECT_DIR/.claude/hooks"/*.sh; do + [ -f "$script" ] || continue + if bash -n "$script" 2>/dev/null; then + echo "[import] OK: $script" + else + echo "[import] WARN: $script failed bash -n check" + fi + done +fi + +rm -rf "$STAGING_DIR" + +echo "" +echo "[import] Import complete." +echo "[import] Next steps:" +echo " 1. Review CLAUDE.md and update project-specific sections" +echo " 2. Add your ANTHROPIC_API_KEY to .env" +echo " 3. Run /agent-factory:status to verify the system"