Initial commit
This commit is contained in:
201
hooks/README.md
Normal file
201
hooks/README.md
Normal file
@@ -0,0 +1,201 @@
|
||||
# SpecWeave Azure DevOps Plugin Hooks
|
||||
|
||||
**Plugin**: `specweave-ado`
|
||||
**Location**: `plugins/specweave-ado/hooks/`
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
This hook automatically syncs SpecWeave increment progress to Azure DevOps Work Items after each task completion.
|
||||
|
||||
**Key Features**:
|
||||
- ✅ Updates Azure DevOps work item status based on increment progress
|
||||
- ✅ Syncs task completion state to ADO
|
||||
- ✅ Bidirectional sync (local → Azure DevOps)
|
||||
- ✅ Non-blocking (failures don't stop core workflow)
|
||||
- ✅ Self-contained (no dependencies on core plugin)
|
||||
|
||||
---
|
||||
|
||||
## Available Hooks
|
||||
|
||||
### 1. `post-task-completion.sh`
|
||||
|
||||
**Triggers**: After ANY task is marked complete (via TodoWrite tool)
|
||||
|
||||
**Preconditions**:
|
||||
- ✅ Active increment exists (`.specweave/increments/####/`)
|
||||
- ✅ `metadata.json` has `.ado.item` field
|
||||
- ✅ Node.js installed
|
||||
- ✅ ADO sync script exists (`dist/commands/ado-sync.js`)
|
||||
- ✅ Azure DevOps PAT in `.env`
|
||||
|
||||
**Actions**:
|
||||
1. Reads increment metadata
|
||||
2. Calls ADO sync script (Node.js)
|
||||
3. Updates Azure DevOps work item status
|
||||
4. Logs all actions to `.specweave/logs/hooks-debug.log`
|
||||
|
||||
**Example Flow**:
|
||||
```
|
||||
Task T-003 completed in tasks.md
|
||||
↓
|
||||
Hook fires (PostToolUse + TodoWrite matcher)
|
||||
↓
|
||||
ADO sync script updates work item #4567
|
||||
↓
|
||||
Log: "[ADO] ✅ Azure DevOps sync complete"
|
||||
```
|
||||
|
||||
**Dependencies**:
|
||||
- Node.js 18+
|
||||
- Azure DevOps PAT (`.env`)
|
||||
- `jq` for JSON parsing
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### Hook Registration (`hooks.json`)
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "TodoWrite",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/post-task-completion.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Metadata Format
|
||||
|
||||
Increments must have `metadata.json` with Azure DevOps work item link:
|
||||
|
||||
```json
|
||||
{
|
||||
"ado": {
|
||||
"item": 4567,
|
||||
"url": "https://dev.azure.com/org/project/_workitems/edit/4567",
|
||||
"project": "MyProject"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Required in `.env`:
|
||||
|
||||
```bash
|
||||
AZURE_DEVOPS_ORG=your-org
|
||||
AZURE_DEVOPS_PROJECT=your-project
|
||||
AZURE_DEVOPS_PAT=your_personal_access_token_here
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Automatic (Recommended)
|
||||
|
||||
```bash
|
||||
npx specweave init my-project
|
||||
# Installs all plugins including specweave-ado
|
||||
# Hooks auto-register via hooks.json
|
||||
```
|
||||
|
||||
### Manual (Development)
|
||||
|
||||
```bash
|
||||
# Install plugin
|
||||
/plugin install specweave-ado
|
||||
|
||||
# Verify hook registration
|
||||
cat ~/.claude/settings.json | grep specweave-ado
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
### Test Hook Independently
|
||||
|
||||
```bash
|
||||
# Test syntax
|
||||
bash -n plugins/specweave-ado/hooks/post-task-completion.sh
|
||||
|
||||
# Test execution (requires active increment + ADO work item)
|
||||
./plugins/specweave-ado/hooks/post-task-completion.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Logging
|
||||
|
||||
All Azure DevOps sync actions are logged to:
|
||||
|
||||
```
|
||||
.specweave/logs/hooks-debug.log
|
||||
```
|
||||
|
||||
**Example Log Output**:
|
||||
```
|
||||
[2025-11-10] [ADO] 🔗 Azure DevOps sync hook fired
|
||||
[2025-11-10] [ADO] 🔄 Syncing to Azure DevOps work item 4567
|
||||
[2025-11-10] [ADO] ✅ Azure DevOps sync complete
|
||||
```
|
||||
|
||||
**Log Prefixes**:
|
||||
- `[ADO]` - All messages from this hook
|
||||
- `🔗` - Hook fired
|
||||
- `🔄` - Syncing started
|
||||
- `✅` - Success
|
||||
- `⚠️` - Warning (non-blocking failure)
|
||||
- `ℹ️` - Info (skipped due to precondition)
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
### Separation from Core Plugin
|
||||
|
||||
**Before (v0.12.x)**:
|
||||
```
|
||||
Core hook (500+ lines)
|
||||
├── Azure DevOps sync ← Embedded in core!
|
||||
```
|
||||
|
||||
**After (v0.13.0+)**:
|
||||
```
|
||||
Core hook (330 lines) ADO plugin hook (150 lines)
|
||||
├── Core concerns ├── Check for ADO work item
|
||||
├── Call Node.js sync script
|
||||
└── Log actions
|
||||
```
|
||||
|
||||
**Key Benefits**:
|
||||
- ✅ **Optional plugin**: ADO sync only runs if `specweave-ado` installed
|
||||
- ✅ **Independent testing**: Test ADO sync in isolation
|
||||
- ✅ **No core dependencies**: Core plugin doesn't depend on Azure DevOps API
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- **Core Plugin Hooks**: `plugins/specweave/hooks/README.md`
|
||||
- **Architecture Analysis**: `.specweave/increments/0018-strict-increment-discipline-enforcement/reports/HOOKS-ARCHITECTURE-ANALYSIS.md`
|
||||
- **ADO Sync Command**: `plugins/specweave-ado/commands/specweave-ado-sync.md`
|
||||
|
||||
---
|
||||
|
||||
**Version**: v0.13.0+
|
||||
**Last Updated**: 2025-11-10
|
||||
360
hooks/post-living-docs-update.sh
Normal file
360
hooks/post-living-docs-update.sh
Normal file
@@ -0,0 +1,360 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================================================
|
||||
# Post Living Docs Update Hook - Azure DevOps Sync
|
||||
# ============================================================================
|
||||
#
|
||||
# Triggered after living docs are updated to sync with Azure DevOps.
|
||||
# CRITICAL: External tool status ALWAYS wins in conflicts!
|
||||
#
|
||||
# Triggers:
|
||||
# 1. After /specweave:done (increment completion)
|
||||
# 2. After /specweave:sync-docs update
|
||||
# 3. After manual spec edits
|
||||
# 4. After webhook from ADO
|
||||
#
|
||||
# ============================================================================
|
||||
|
||||
set +e # EMERGENCY FIX: Prevents Claude Code crashes
|
||||
|
||||
# EMERGENCY KILL SWITCH
|
||||
if [[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Configuration
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
||||
LIVING_DOCS_DIR="$PROJECT_ROOT/.specweave/docs/internal/specs"
|
||||
LOG_FILE="$PROJECT_ROOT/.specweave/logs/ado-sync.log"
|
||||
DEBUG=${DEBUG:-0}
|
||||
|
||||
# Ensure log directory exists
|
||||
mkdir -p "$(dirname "$LOG_FILE")"
|
||||
|
||||
# ============================================================================
|
||||
# Logging
|
||||
# ============================================================================
|
||||
|
||||
log() {
|
||||
local level=$1
|
||||
shift
|
||||
local message="$@"
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
|
||||
[ "$DEBUG" -eq 1 ] && echo "[$level] $message" >&2
|
||||
}
|
||||
|
||||
log_info() {
|
||||
log "INFO" "$@"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
log "ERROR" "$@"
|
||||
}
|
||||
|
||||
log_debug() {
|
||||
[ "$DEBUG" -eq 1 ] && log "DEBUG" "$@"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# External Tool Detection
|
||||
# ============================================================================
|
||||
|
||||
detect_external_tool() {
|
||||
local spec_path=$1
|
||||
|
||||
# Check for external links in spec metadata
|
||||
if grep -q "externalLinks:" "$spec_path"; then
|
||||
if grep -q "ado:" "$spec_path"; then
|
||||
echo "ado"
|
||||
elif grep -q "jira:" "$spec_path"; then
|
||||
echo "jira"
|
||||
elif grep -q "github:" "$spec_path"; then
|
||||
echo "github"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Status Mapping
|
||||
# ============================================================================
|
||||
|
||||
map_ado_status_to_local() {
|
||||
local ado_status=$1
|
||||
|
||||
case "$ado_status" in
|
||||
"New")
|
||||
echo "draft"
|
||||
;;
|
||||
"Active")
|
||||
echo "in-progress"
|
||||
;;
|
||||
"Resolved")
|
||||
echo "implemented"
|
||||
;;
|
||||
"Closed")
|
||||
echo "complete"
|
||||
;;
|
||||
"In Review"|"In QA")
|
||||
echo "in-qa"
|
||||
;;
|
||||
*)
|
||||
echo "unknown"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
map_local_status_to_ado() {
|
||||
local local_status=$1
|
||||
|
||||
case "$local_status" in
|
||||
"draft")
|
||||
echo "New"
|
||||
;;
|
||||
"in-progress")
|
||||
echo "Active"
|
||||
;;
|
||||
"implemented")
|
||||
echo "Resolved"
|
||||
;;
|
||||
"complete")
|
||||
echo "Closed"
|
||||
;;
|
||||
"in-qa")
|
||||
echo "In Review"
|
||||
;;
|
||||
*)
|
||||
echo "Active"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# ADO API Functions
|
||||
# ============================================================================
|
||||
|
||||
get_ado_work_item_status() {
|
||||
local work_item_id=$1
|
||||
local org="${AZURE_DEVOPS_ORG}"
|
||||
local project="${AZURE_DEVOPS_PROJECT}"
|
||||
local pat="${AZURE_DEVOPS_PAT}"
|
||||
|
||||
if [ -z "$org" ] || [ -z "$pat" ]; then
|
||||
log_error "ADO credentials not configured"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local api_url="https://dev.azure.com/${org}/${project}/_apis/wit/workitems/${work_item_id}?api-version=7.0"
|
||||
|
||||
log_debug "Fetching ADO work item $work_item_id status"
|
||||
|
||||
local response=$(curl -s -u ":${pat}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$api_url")
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
log_error "Failed to fetch ADO work item status"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Extract status from response
|
||||
local status=$(echo "$response" | jq -r '.fields["System.State"]')
|
||||
|
||||
if [ "$status" = "null" ] || [ -z "$status" ]; then
|
||||
log_error "Could not extract status from ADO response"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$status"
|
||||
}
|
||||
|
||||
update_ado_work_item() {
|
||||
local work_item_id=$1
|
||||
local spec_content=$2
|
||||
local org="${AZURE_DEVOPS_ORG}"
|
||||
local project="${AZURE_DEVOPS_PROJECT}"
|
||||
local pat="${AZURE_DEVOPS_PAT}"
|
||||
|
||||
if [ -z "$org" ] || [ -z "$pat" ]; then
|
||||
log_error "ADO credentials not configured"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Extract current status from spec
|
||||
local local_status=$(echo "$spec_content" | grep "^status:" | cut -d: -f2 | tr -d ' ')
|
||||
local ado_status=$(map_local_status_to_ado "$local_status")
|
||||
|
||||
local api_url="https://dev.azure.com/${org}/${project}/_apis/wit/workitems/${work_item_id}?api-version=7.0"
|
||||
|
||||
# Create update payload
|
||||
local payload=$(cat <<EOF
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/fields/System.State",
|
||||
"value": "$ado_status"
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/fields/System.History",
|
||||
"value": "Updated from SpecWeave living docs"
|
||||
}
|
||||
]
|
||||
EOF
|
||||
)
|
||||
|
||||
log_debug "Updating ADO work item $work_item_id with status: $ado_status"
|
||||
|
||||
curl -s -X PATCH \
|
||||
-u ":${pat}" \
|
||||
-H "Content-Type: application/json-patch+json" \
|
||||
-d "$payload" \
|
||||
"$api_url" > /dev/null
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
log_error "Failed to update ADO work item"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Updated ADO work item $work_item_id"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Conflict Resolution - CRITICAL: External Wins!
|
||||
# ============================================================================
|
||||
|
||||
resolve_status_conflict() {
|
||||
local spec_path=$1
|
||||
local local_status=$2
|
||||
local external_status=$3
|
||||
|
||||
local mapped_external=$(map_ado_status_to_local "$external_status")
|
||||
|
||||
if [ "$local_status" != "$mapped_external" ]; then
|
||||
log_info "Status conflict detected:"
|
||||
log_info " Local: $local_status"
|
||||
log_info " External: $external_status (mapped: $mapped_external)"
|
||||
log_info " Resolution: EXTERNAL WINS - applying $mapped_external"
|
||||
|
||||
# Update local spec with external status
|
||||
sed -i.bak "s/^status: .*/status: $mapped_external/" "$spec_path"
|
||||
|
||||
# Add sync metadata
|
||||
local timestamp=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
|
||||
|
||||
# Check if syncedAt exists, update or add
|
||||
if grep -q "syncedAt:" "$spec_path"; then
|
||||
sed -i.bak "s/syncedAt: .*/syncedAt: \"$timestamp\"/" "$spec_path"
|
||||
else
|
||||
# Add after externalLinks section
|
||||
sed -i.bak "/externalLinks:/a\\
|
||||
syncedAt: \"$timestamp\"" "$spec_path"
|
||||
fi
|
||||
|
||||
# Clean up backup files
|
||||
rm -f "${spec_path}.bak"
|
||||
|
||||
log_info "Local spec updated with external status: $mapped_external"
|
||||
return 0
|
||||
else
|
||||
log_debug "No status conflict - local and external match: $local_status"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Main Sync Function
|
||||
# ============================================================================
|
||||
|
||||
sync_spec_with_ado() {
|
||||
local spec_path=$1
|
||||
|
||||
if [ ! -f "$spec_path" ]; then
|
||||
log_error "Spec file not found: $spec_path"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local spec_name=$(basename "$spec_path")
|
||||
log_info "Syncing spec: $spec_name"
|
||||
|
||||
# Read spec content
|
||||
local spec_content=$(cat "$spec_path")
|
||||
|
||||
# Extract ADO work item ID from metadata
|
||||
local work_item_id=$(echo "$spec_content" | grep -A5 "externalLinks:" | grep -A3 "ado:" | grep "featureId:" | cut -d: -f2 | tr -d ' ')
|
||||
|
||||
if [ -z "$work_item_id" ]; then
|
||||
log_debug "No ADO work item linked to spec, skipping sync"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Found ADO work item ID: $work_item_id"
|
||||
|
||||
# Step 1: Push updates to ADO (content changes)
|
||||
update_ado_work_item "$work_item_id" "$spec_content"
|
||||
|
||||
# Step 2: CRITICAL - Pull status from ADO (external wins!)
|
||||
local external_status=$(get_ado_work_item_status "$work_item_id")
|
||||
|
||||
if [ -z "$external_status" ]; then
|
||||
log_error "Could not fetch ADO status"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "ADO status: $external_status"
|
||||
|
||||
# Step 3: Extract local status
|
||||
local local_status=$(echo "$spec_content" | grep "^status:" | cut -d: -f2 | tr -d ' ')
|
||||
|
||||
log_info "Local status: $local_status"
|
||||
|
||||
# Step 4: Resolve conflicts - EXTERNAL WINS
|
||||
resolve_status_conflict "$spec_path" "$local_status" "$external_status"
|
||||
|
||||
log_info "Sync completed for $spec_name"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Entry Point
|
||||
# ============================================================================
|
||||
|
||||
main() {
|
||||
log_info "=== Post Living Docs Update Hook Started ==="
|
||||
|
||||
# Get the spec path from arguments or environment
|
||||
local spec_path="${1:-$SPECWEAVE_UPDATED_SPEC}"
|
||||
|
||||
if [ -z "$spec_path" ]; then
|
||||
log_error "No spec path provided"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Detect external tool
|
||||
local tool=$(detect_external_tool "$spec_path")
|
||||
|
||||
if [ "$tool" != "ado" ]; then
|
||||
log_debug "Not an ADO-linked spec, skipping"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log_info "Detected ADO integration for spec"
|
||||
|
||||
# Perform sync
|
||||
sync_spec_with_ado "$spec_path"
|
||||
|
||||
local exit_code=$?
|
||||
|
||||
if [ $exit_code -eq 0 ]; then
|
||||
log_info "=== Sync completed successfully ==="
|
||||
else
|
||||
log_error "=== Sync failed with exit code: $exit_code ==="
|
||||
fi
|
||||
|
||||
exit $exit_code
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
# ALWAYS exit 0 - NEVER let hook errors crash Claude Code
|
||||
exit 0
|
||||
180
hooks/post-task-completion.sh
Executable file
180
hooks/post-task-completion.sh
Executable file
@@ -0,0 +1,180 @@
|
||||
#!/bin/bash
|
||||
|
||||
# SpecWeave Azure DevOps Sync Hook
|
||||
# Runs after task completion to sync progress to Azure DevOps Work Items
|
||||
#
|
||||
# This hook is part of the specweave-ado plugin and handles:
|
||||
# - Syncing task completion state to Azure DevOps work items
|
||||
# - Updating ADO work item status based on increment progress
|
||||
#
|
||||
# Dependencies:
|
||||
# - Node.js for running sync scripts
|
||||
# - jq for JSON parsing
|
||||
# - metadata.json must have .ado.item field
|
||||
# - Azure DevOps PAT in .env
|
||||
|
||||
set +e # EMERGENCY FIX: Prevents Claude Code crashes
|
||||
|
||||
# EMERGENCY KILL SWITCH
|
||||
if [[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# PROJECT ROOT DETECTION
|
||||
# ============================================================================
|
||||
|
||||
# Find project root by searching upward for .specweave/ directory
|
||||
find_project_root() {
|
||||
local dir="$1"
|
||||
while [ "$dir" != "/" ]; do
|
||||
if [ -d "$dir/.specweave" ]; then
|
||||
echo "$dir"
|
||||
return 0
|
||||
fi
|
||||
dir="$(dirname "$dir")"
|
||||
done
|
||||
# Fallback: try current directory
|
||||
if [ -d "$(pwd)/.specweave" ]; then
|
||||
pwd
|
||||
else
|
||||
echo "$(pwd)"
|
||||
fi
|
||||
}
|
||||
|
||||
PROJECT_ROOT="$(find_project_root "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")"
|
||||
cd "$PROJECT_ROOT" 2>/dev/null || true
|
||||
|
||||
# ============================================================================
|
||||
# CONFIGURATION
|
||||
# ============================================================================
|
||||
|
||||
LOGS_DIR=".specweave/logs"
|
||||
DEBUG_LOG="$LOGS_DIR/hooks-debug.log"
|
||||
|
||||
mkdir -p "$LOGS_DIR" 2>/dev/null || true
|
||||
|
||||
# ============================================================================
|
||||
# PRECONDITIONS CHECK
|
||||
# ============================================================================
|
||||
|
||||
echo "[$(date)] [ADO] 🔗 Azure DevOps sync hook fired" >> "$DEBUG_LOG" 2>/dev/null || true
|
||||
|
||||
# Detect current increment
|
||||
CURRENT_INCREMENT=$(ls -td .specweave/increments/*/ 2>/dev/null | xargs -n1 basename | grep -v "_backlog" | grep -v "_archive" | grep -v "_working" | head -1)
|
||||
|
||||
if [ -z "$CURRENT_INCREMENT" ]; then
|
||||
echo "[$(date)] [ADO] ℹ️ No active increment, skipping ADO sync" >> "$DEBUG_LOG" 2>/dev/null || true
|
||||
cat <<EOF
|
||||
{
|
||||
"continue": true
|
||||
}
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check for metadata.json
|
||||
METADATA_FILE=".specweave/increments/$CURRENT_INCREMENT/metadata.json"
|
||||
|
||||
if [ ! -f "$METADATA_FILE" ]; then
|
||||
echo "[$(date)] [ADO] ℹ️ No metadata.json for $CURRENT_INCREMENT, skipping ADO sync" >> "$DEBUG_LOG" 2>/dev/null || true
|
||||
cat <<EOF
|
||||
{
|
||||
"continue": true
|
||||
}
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check for ADO work item link
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "[$(date)] [ADO] ⚠️ jq not found, skipping ADO sync" >> "$DEBUG_LOG" 2>/dev/null || true
|
||||
cat <<EOF
|
||||
{
|
||||
"continue": true
|
||||
}
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ADO_ITEM=$(jq -r '.ado.item // empty' "$METADATA_FILE" 2>/dev/null)
|
||||
|
||||
if [ -z "$ADO_ITEM" ]; then
|
||||
echo "[$(date)] [ADO] ℹ️ No Azure DevOps work item linked to $CURRENT_INCREMENT, skipping sync" >> "$DEBUG_LOG" 2>/dev/null || true
|
||||
cat <<EOF
|
||||
{
|
||||
"continue": true
|
||||
}
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check for Node.js
|
||||
if ! command -v node &> /dev/null; then
|
||||
echo "[$(date)] [ADO] ⚠️ Node.js not found, skipping ADO sync" >> "$DEBUG_LOG" 2>/dev/null || true
|
||||
cat <<EOF
|
||||
{
|
||||
"continue": true
|
||||
}
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check for ADO sync script
|
||||
if [ ! -f "dist/commands/ado-sync.js" ]; then
|
||||
echo "[$(date)] [ADO] ⚠️ ado-sync.js not found, skipping ADO sync" >> "$DEBUG_LOG" 2>/dev/null || true
|
||||
cat <<EOF
|
||||
{
|
||||
"continue": true
|
||||
}
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# AZURE DEVOPS SYNC LOGIC
|
||||
# ============================================================================
|
||||
|
||||
echo "[$(date)] [ADO] 🔄 Syncing to Azure DevOps work item $ADO_ITEM" >> "$DEBUG_LOG" 2>/dev/null || true
|
||||
|
||||
# Run ADO sync command (non-blocking)
|
||||
node dist/commands/ado-sync.js "$CURRENT_INCREMENT" 2>&1 | tee -a "$DEBUG_LOG" >/dev/null || {
|
||||
echo "[$(date)] [ADO] ⚠️ Failed to sync to Azure DevOps (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
|
||||
}
|
||||
|
||||
echo "[$(date)] [ADO] ✅ Azure DevOps sync complete" >> "$DEBUG_LOG" 2>/dev/null || true
|
||||
|
||||
# ============================================================================
|
||||
# SPEC COMMIT SYNC (NEW!)
|
||||
# ============================================================================
|
||||
|
||||
echo "[$(date)] [ADO] 🔗 Checking for spec commit sync..." >> "$DEBUG_LOG" 2>/dev/null || true
|
||||
|
||||
# Call TypeScript CLI to sync commits
|
||||
if command -v node &> /dev/null && [ -f "$PROJECT_ROOT/dist/cli/commands/sync-spec-commits.js" ]; then
|
||||
echo "[$(date)] [ADO] 🚀 Running spec commit sync..." >> "$DEBUG_LOG" 2>/dev/null || true
|
||||
|
||||
node "$PROJECT_ROOT/dist/cli/commands/sync-spec-commits.js" \
|
||||
--increment "$PROJECT_ROOT/.specweave/increments/$CURRENT_INCREMENT" \
|
||||
--provider ado \
|
||||
2>&1 | tee -a "$DEBUG_LOG" >/dev/null || {
|
||||
echo "[$(date)] [ADO] ⚠️ Spec commit sync failed (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
|
||||
}
|
||||
|
||||
echo "[$(date)] [ADO] ✅ Spec commit sync complete" >> "$DEBUG_LOG" 2>/dev/null || true
|
||||
else
|
||||
echo "[$(date)] [ADO] ℹ️ Spec commit sync not available (node or script not found)" >> "$DEBUG_LOG" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# OUTPUT TO CLAUDE
|
||||
# ============================================================================
|
||||
|
||||
cat <<EOF
|
||||
{
|
||||
"continue": true
|
||||
}
|
||||
EOF
|
||||
|
||||
# ALWAYS exit 0 - NEVER let hook errors crash Claude Code
|
||||
exit 0
|
||||
Reference in New Issue
Block a user