Last active
December 15, 2025 11:50
-
-
Save busla/06228d9d6dd8e17a2f7600a1f0d167c9 to your computer and use it in GitHub Desktop.
SDAD - Spec-Driven Agentic Development Setup
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # SDAD Install - Minimal hooks infrastructure setup | |
| # Usage: curl -sL https://gist.github.com/busla/06228d9d6dd8e17a2f7600a1f0d167c9/raw | bash | |
| set -e | |
| TARGET_DIR="${1:-.}" | |
| TARGET_DIR=$(cd "$TARGET_DIR" 2>/dev/null && pwd || echo "$TARGET_DIR") | |
| mkdir -p "$TARGET_DIR" | |
| TARGET_DIR=$(cd "$TARGET_DIR" && pwd) | |
| echo "π SDAD: Installing hooks infrastructure..." | |
| echo "" | |
| echo "π Target: $TARGET_DIR" | |
| # Create directories | |
| mkdir -p "$TARGET_DIR/.claude" | |
| mkdir -p "$TARGET_DIR/.specify/scripts/bash" | |
| # Backup existing settings if present | |
| if [ -f "$TARGET_DIR/.claude/settings.json" ]; then | |
| cp "$TARGET_DIR/.claude/settings.json" "$TARGET_DIR/.claude/settings.json.backup" | |
| echo " Backed up existing settings.json" | |
| fi | |
| # Write settings.json with hooks | |
| cat > "$TARGET_DIR/.claude/settings.json" << 'SETTINGS_EOF' | |
| { | |
| "hooks": { | |
| "PostToolUse": [ | |
| { | |
| "matcher": "Write|Edit|MultiEdit", | |
| "hooks": [ | |
| { | |
| "type": "command", | |
| "command": "bash .specify/scripts/bash/post-tool-use.sh" | |
| } | |
| ] | |
| } | |
| ], | |
| "PreToolUse": [ | |
| { | |
| "matcher": "Write|Edit|MultiEdit", | |
| "hooks": [ | |
| { | |
| "type": "command", | |
| "command": "bash .specify/scripts/bash/pre-tool-use.sh" | |
| } | |
| ] | |
| } | |
| ], | |
| "Stop": [ | |
| { | |
| "hooks": [ | |
| { | |
| "type": "command", | |
| "command": "bash .specify/scripts/bash/on-stop.sh" | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| } | |
| SETTINGS_EOF | |
| echo " β .claude/settings.json" | |
| # Write post-tool-use.sh | |
| cat > "$TARGET_DIR/.specify/scripts/bash/post-tool-use.sh" << 'POSTTOOL_EOF' | |
| #!/bin/bash | |
| # SDAD Post-Tool-Use Hook | |
| set -e | |
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| # Read tool input from stdin | |
| INPUT=$(cat) | |
| FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // empty') | |
| # Only process .specify/* files | |
| if [[ "$FILE" != *.specify/* ]]; then | |
| exit 0 | |
| fi | |
| # Sync to memory if script exists | |
| if [ -f "$SCRIPT_DIR/sync-to-memory.sh" ]; then | |
| "$SCRIPT_DIR/sync-to-memory.sh" 2>/dev/null || true | |
| fi | |
| # Get filename and provide agent spawning guidance | |
| BASENAME=$(basename "$FILE") | |
| case "$BASENAME" in | |
| spec.md) | |
| echo "" | |
| echo "π SDAD: Specification written!" | |
| echo "π€ Spawn these agents NOW in a SINGLE message:" | |
| echo " Task(\"Requirements Researcher\", \"Analyze spec for completeness.\", \"researcher\")" | |
| echo " Task(\"Domain Analyst\", \"Validate requirements against domain patterns.\", \"analyst\")" | |
| echo "" | |
| ;; | |
| plan.md) | |
| echo "" | |
| echo "ποΈ SDAD: Plan written!" | |
| echo "π€ Spawn these agents NOW in a SINGLE message:" | |
| echo " Task(\"System Architect\", \"Design architecture per constitution.\", \"system-architect\")" | |
| echo " Task(\"API Designer\", \"Define OpenAPI contracts.\", \"api-docs\")" | |
| echo " Task(\"Data Modeler\", \"Design entity schemas.\", \"code-analyzer\")" | |
| echo "" | |
| ;; | |
| tasks.md) | |
| echo "" | |
| echo "β SDAD: Tasks written!" | |
| echo "π€ Spawn these agents NOW in a SINGLE message:" | |
| echo " Task(\"Implementation Coordinator\", \"Orchestrate tasks from tasks.md\", \"coordinator\")" | |
| echo " Task(\"Backend Developer\", \"Implement backend tasks\", \"backend-dev\")" | |
| echo " Task(\"Test Engineer\", \"Write tests BEFORE implementation (TDD)\", \"tester\")" | |
| echo " Task(\"Code Reviewer\", \"Validate against constitution\", \"reviewer\")" | |
| echo "" | |
| ;; | |
| constitution.md) | |
| echo "π SDAD: Constitution updated" | |
| ;; | |
| esac | |
| POSTTOOL_EOF | |
| # Write pre-tool-use.sh | |
| cat > "$TARGET_DIR/.specify/scripts/bash/pre-tool-use.sh" << 'PRETOOL_EOF' | |
| #!/bin/bash | |
| # SDAD Pre-Tool-Use Hook | |
| set -e | |
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" | |
| # Read tool input from stdin | |
| INPUT=$(cat) | |
| FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // empty') | |
| # Only validate src/* files | |
| if [[ "$FILE" != */src/* ]]; then | |
| exit 0 | |
| fi | |
| # Run constitution validation if script exists | |
| if [ -f "$SCRIPT_DIR/validate-constitution.sh" ]; then | |
| "$SCRIPT_DIR/validate-constitution.sh" 2>/dev/null || true | |
| fi | |
| PRETOOL_EOF | |
| # Write on-stop.sh | |
| cat > "$TARGET_DIR/.specify/scripts/bash/on-stop.sh" << 'ONSTOP_EOF' | |
| #!/bin/bash | |
| # SDAD Stop Hook | |
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" | |
| if [ ! -d "$PROJECT_ROOT/.specify/specs" ]; then | |
| exit 0 | |
| fi | |
| echo "π SDAD: Session ending - syncing state..." | |
| if [ -f "$SCRIPT_DIR/sync-to-memory.sh" ]; then | |
| "$SCRIPT_DIR/sync-to-memory.sh" 2>/dev/null || true | |
| fi | |
| ONSTOP_EOF | |
| # Write sync-to-memory.sh | |
| cat > "$TARGET_DIR/.specify/scripts/bash/sync-to-memory.sh" << 'SYNC_EOF' | |
| #!/bin/bash | |
| # SDAD Memory Sync | |
| set -e | |
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" | |
| # Sync constitution | |
| if [ -f "$PROJECT_ROOT/.specify/memory/constitution.md" ]; then | |
| npx claude-flow@alpha memory store \ | |
| --key "project/constitution" \ | |
| --namespace "foundation" \ | |
| --file "$PROJECT_ROOT/.specify/memory/constitution.md" 2>/dev/null || true | |
| fi | |
| # Sync feature specs | |
| if [ -d "$PROJECT_ROOT/.specify/specs" ]; then | |
| for feature_dir in "$PROJECT_ROOT/.specify/specs"/*/; do | |
| if [ -d "$feature_dir" ]; then | |
| feature_name=$(basename "$feature_dir") | |
| for doc in spec.md plan.md tasks.md; do | |
| if [ -f "$feature_dir/$doc" ]; then | |
| ns="specifications" | |
| [[ "$doc" == "plan.md" ]] && ns="architecture" | |
| [[ "$doc" == "tasks.md" ]] && ns="tasks" | |
| npx claude-flow@alpha memory store \ | |
| --key "features/$feature_name/${doc%.md}" \ | |
| --namespace "$ns" \ | |
| --file "$feature_dir/$doc" 2>/dev/null || true | |
| fi | |
| done | |
| fi | |
| done | |
| fi | |
| SYNC_EOF | |
| # Write validate-constitution.sh | |
| cat > "$TARGET_DIR/.specify/scripts/bash/validate-constitution.sh" << 'VALIDATE_EOF' | |
| #!/bin/bash | |
| # SDAD Constitution Validator | |
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" | |
| CONSTITUTION="$PROJECT_ROOT/.specify/memory/constitution.md" | |
| if [ ! -f "$CONSTITUTION" ]; then | |
| exit 0 | |
| fi | |
| if grep -q "Test-First" "$CONSTITUTION"; then | |
| if [ -d "$PROJECT_ROOT/src" ] && [ ! -d "$PROJECT_ROOT/tests" ] && [ ! -d "$PROJECT_ROOT/__tests__" ]; then | |
| echo "β οΈ SDAD: Constitution requires Test-First Development" | |
| echo " No tests directory found. Create tests before implementation!" | |
| fi | |
| fi | |
| if grep -q "Type Safety" "$CONSTITUTION"; then | |
| if [ -f "$PROJECT_ROOT/tsconfig.json" ]; then | |
| if ! grep -q '"strict": true' "$PROJECT_ROOT/tsconfig.json"; then | |
| echo "β οΈ SDAD: Constitution requires strict typing" | |
| fi | |
| fi | |
| fi | |
| exit 0 | |
| VALIDATE_EOF | |
| # Make scripts executable | |
| chmod +x "$TARGET_DIR/.specify/scripts/bash/"*.sh | |
| echo " β .specify/scripts/bash/*.sh" | |
| echo "" | |
| echo "β SDAD hooks installed!" | |
| echo "" | |
| echo "π Hooks will fire when spec-kit writes to .specify/* files" | |
| echo " Use /speckit.specify, /speckit.plan, /speckit.tasks commands to start" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment