specweave 1.0.30 → 1.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +140 -1235
- package/bin/specweave.js +23 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +3 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.js +39 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
- package/dist/src/cli/commands/set-sync-target.d.ts +41 -0
- package/dist/src/cli/commands/set-sync-target.d.ts.map +1 -0
- package/dist/src/cli/commands/set-sync-target.js +126 -0
- package/dist/src/cli/commands/set-sync-target.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/github-multi-repo.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js +5 -8
- package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js.map +1 -1
- package/dist/src/core/hooks/HookScanner.d.ts +32 -0
- package/dist/src/core/hooks/HookScanner.d.ts.map +1 -1
- package/dist/src/core/hooks/HookScanner.js +125 -1
- package/dist/src/core/hooks/HookScanner.js.map +1 -1
- package/dist/src/core/hooks/types.d.ts +10 -1
- package/dist/src/core/hooks/types.d.ts.map +1 -1
- package/dist/src/core/increment/metadata-manager.d.ts +67 -1
- package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
- package/dist/src/core/increment/metadata-manager.js +93 -0
- package/dist/src/core/increment/metadata-manager.js.map +1 -1
- package/dist/src/core/project/index.d.ts +21 -0
- package/dist/src/core/project/index.d.ts.map +1 -0
- package/dist/src/core/project/index.js +22 -0
- package/dist/src/core/project/index.js.map +1 -0
- package/dist/src/core/project/project-service.d.ts +122 -0
- package/dist/src/core/project/project-service.d.ts.map +1 -0
- package/dist/src/core/project/project-service.js +334 -0
- package/dist/src/core/project/project-service.js.map +1 -0
- package/dist/src/core/sync/external-tool-resolver.d.ts +171 -0
- package/dist/src/core/sync/external-tool-resolver.d.ts.map +1 -0
- package/dist/src/core/sync/external-tool-resolver.js +569 -0
- package/dist/src/core/sync/external-tool-resolver.js.map +1 -0
- package/dist/src/core/types/increment-metadata.d.ts +92 -0
- package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
- package/dist/src/hooks/processor.d.ts +7 -3
- package/dist/src/hooks/processor.d.ts.map +1 -1
- package/dist/src/hooks/processor.js +11 -5
- package/dist/src/hooks/processor.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/hooks/hooks.json +0 -69
- package/plugins/specweave/hooks/v2/handlers/project-bridge-handler.sh +96 -0
- package/plugins/specweave/hooks/v2/queue/processor.sh +13 -5
- package/plugins/specweave/lib/hooks/project-bridge.js +76 -0
- package/plugins/specweave/lib/hooks/update-tasks-md.js +0 -0
- package/plugins/specweave/lib/hooks/us-completion-orchestrator.js +0 -0
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.d.ts +67 -1
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +93 -0
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -1
- package/plugins/specweave/lib/vendor/core/types/increment-metadata.d.ts +92 -0
- package/plugins/specweave-github/lib/github-client-v2.js +39 -0
- package/plugins/specweave-github/lib/github-client-v2.ts +44 -0
- package/plugins/specweave/hooks/docs-changed.sh +0 -87
- package/plugins/specweave/hooks/human-input-required.sh +0 -83
- package/plugins/specweave/hooks/post-edit-write-consolidated.sh +0 -428
- package/plugins/specweave/hooks/post-first-increment.sh +0 -61
- package/plugins/specweave/hooks/post-increment-change.sh +0 -103
- package/plugins/specweave/hooks/post-increment-completion.sh +0 -513
- package/plugins/specweave/hooks/post-increment-planning.sh +0 -1204
- package/plugins/specweave/hooks/post-increment-status-change.sh +0 -243
- package/plugins/specweave/hooks/post-metadata-change.sh +0 -246
- package/plugins/specweave/hooks/post-spec-update.sh +0 -158
- package/plugins/specweave/hooks/post-task-completion.sh +0 -557
- package/plugins/specweave/hooks/post-task-edit.sh +0 -47
- package/plugins/specweave/hooks/post-user-story-complete.sh +0 -230
- package/plugins/specweave/hooks/pre-command-deduplication.sh +0 -68
- package/plugins/specweave/hooks/pre-edit-write-consolidated.sh +0 -225
- package/plugins/specweave/hooks/pre-implementation.sh +0 -75
- package/plugins/specweave/hooks/pre-increment-start.sh +0 -173
- package/plugins/specweave/hooks/pre-task-completion-edit.sh +0 -355
- package/plugins/specweave/hooks/pre-task-completion.sh +0 -269
- package/plugins/specweave/hooks/pre-tool-use.sh +0 -137
- package/plugins/specweave/hooks/session-start-reconcile.sh +0 -139
- package/plugins/specweave/hooks/shared/bulk-operation-detector.sh +0 -167
- package/plugins/specweave/hooks/test-pretooluse-env.sh +0 -72
- package/plugins/specweave/hooks/validate-increment-completion.sh +0 -113
- package/plugins/specweave/lib/hooks/consolidated-sync.js +0 -288
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# SpecWeave Post-Increment-Status-Change Hook (v0.28.33)
|
|
4
|
-
# Runs automatically after increment status changes (pause/resume/abandon)
|
|
5
|
-
#
|
|
6
|
-
# Trigger: /sw:pause, /sw:resume, /sw:abandon commands
|
|
7
|
-
# Purpose: Sync GitHub issue state with increment status
|
|
8
|
-
#
|
|
9
|
-
# What it does:
|
|
10
|
-
# 1. Detects status change (paused, resumed, abandoned)
|
|
11
|
-
# 2. Posts comment to GitHub issue
|
|
12
|
-
# 3. NEW (v0.28.33): Reopens issues when resumed, closes when abandoned
|
|
13
|
-
#
|
|
14
|
-
# Usage:
|
|
15
|
-
# ./post-increment-status-change.sh <incrementId> <newStatus> <reason>
|
|
16
|
-
#
|
|
17
|
-
# Example:
|
|
18
|
-
# ./post-increment-status-change.sh 0015-hierarchical-sync paused "Waiting for API keys"
|
|
19
|
-
|
|
20
|
-
set +e # EMERGENCY FIX: Prevents Claude Code crashes
|
|
21
|
-
|
|
22
|
-
# EMERGENCY KILL SWITCH
|
|
23
|
-
if [[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]]; then
|
|
24
|
-
exit 0
|
|
25
|
-
fi
|
|
26
|
-
|
|
27
|
-
# Find project root
|
|
28
|
-
find_project_root() {
|
|
29
|
-
local dir="$1"
|
|
30
|
-
while [ "$dir" != "/" ]; do
|
|
31
|
-
if [ -d "$dir/.specweave" ]; then
|
|
32
|
-
echo "$dir"
|
|
33
|
-
return 0
|
|
34
|
-
fi
|
|
35
|
-
dir="$(dirname "$dir")"
|
|
36
|
-
done
|
|
37
|
-
pwd
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
PROJECT_ROOT="$(find_project_root "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")"
|
|
41
|
-
cd "$PROJECT_ROOT" 2>/dev/null || true
|
|
42
|
-
|
|
43
|
-
# Configuration
|
|
44
|
-
LOGS_DIR=".specweave/logs"
|
|
45
|
-
DEBUG_LOG="$LOGS_DIR/hooks-debug.log"
|
|
46
|
-
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
47
|
-
|
|
48
|
-
mkdir -p "$LOGS_DIR" 2>/dev/null || true
|
|
49
|
-
|
|
50
|
-
# Arguments
|
|
51
|
-
INCREMENT_ID="$1"
|
|
52
|
-
NEW_STATUS="$2"
|
|
53
|
-
REASON="$3"
|
|
54
|
-
|
|
55
|
-
if [ -z "$INCREMENT_ID" ] || [ -z "$NEW_STATUS" ]; then
|
|
56
|
-
echo "Usage: $0 <incrementId> <newStatus> [reason]" >&2
|
|
57
|
-
echo "Example: $0 0015-hierarchical-sync paused 'Waiting for API'" >&2
|
|
58
|
-
exit 1
|
|
59
|
-
fi
|
|
60
|
-
|
|
61
|
-
echo "[$(date)] 📊 Status changed: $NEW_STATUS" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
62
|
-
|
|
63
|
-
# Validate status
|
|
64
|
-
case "$NEW_STATUS" in
|
|
65
|
-
paused|abandoned|active|planning|backlog|ready_for_review|completed)
|
|
66
|
-
;;
|
|
67
|
-
*)
|
|
68
|
-
echo "[$(date)] ⚠️ Unknown status: $NEW_STATUS (skipping sync)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
69
|
-
exit 0
|
|
70
|
-
;;
|
|
71
|
-
esac
|
|
72
|
-
|
|
73
|
-
# Check if GitHub CLI available
|
|
74
|
-
if ! command -v gh &> /dev/null; then
|
|
75
|
-
echo "[$(date)] ℹ️ GitHub CLI not found, skipping sync" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
76
|
-
exit 0
|
|
77
|
-
fi
|
|
78
|
-
|
|
79
|
-
# Check if authenticated
|
|
80
|
-
if ! gh auth status &> /dev/null; then
|
|
81
|
-
echo "[$(date)] ℹ️ GitHub CLI not authenticated, skipping sync" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
82
|
-
exit 0
|
|
83
|
-
fi
|
|
84
|
-
|
|
85
|
-
# Load metadata
|
|
86
|
-
METADATA_FILE=".specweave/increments/$INCREMENT_ID/metadata.json"
|
|
87
|
-
|
|
88
|
-
if [ ! -f "$METADATA_FILE" ]; then
|
|
89
|
-
echo "[$(date)] ℹ️ No metadata.json found, skipping sync" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
90
|
-
exit 0
|
|
91
|
-
fi
|
|
92
|
-
|
|
93
|
-
# Extract GitHub issue number (main increment issue)
|
|
94
|
-
GITHUB_ISSUE=$(jq -r '.github.issue // empty' "$METADATA_FILE" 2>/dev/null)
|
|
95
|
-
|
|
96
|
-
# Detect repository
|
|
97
|
-
GITHUB_REMOTE=$(git remote get-url origin 2>/dev/null || echo "")
|
|
98
|
-
REPO_MATCH=$(echo "$GITHUB_REMOTE" | grep -o 'github\.com[:/][^/]*/[^/]*' | sed 's/github\.com[:/]//')
|
|
99
|
-
|
|
100
|
-
if [ -z "$REPO_MATCH" ]; then
|
|
101
|
-
echo "[$(date)] ⚠️ Could not detect GitHub repository" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
102
|
-
exit 0
|
|
103
|
-
fi
|
|
104
|
-
|
|
105
|
-
# ============================================================================
|
|
106
|
-
# STATUS-BASED ACTIONS (NEW in v0.28.33)
|
|
107
|
-
# ============================================================================
|
|
108
|
-
|
|
109
|
-
case "$NEW_STATUS" in
|
|
110
|
-
active|planning|backlog|ready_for_review)
|
|
111
|
-
# ========================================================================
|
|
112
|
-
# REOPEN GitHub Issues (NEW)
|
|
113
|
-
# ========================================================================
|
|
114
|
-
# When increment becomes active (including resumed from pause), reopen all closed GitHub issues
|
|
115
|
-
echo "[$(date)] ▶️ Status is $NEW_STATUS - checking if issues need reopening" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
116
|
-
|
|
117
|
-
if command -v node &> /dev/null; then
|
|
118
|
-
# Find reopen script
|
|
119
|
-
REOPEN_SCRIPT=""
|
|
120
|
-
if [ -f "$PROJECT_ROOT/plugins/specweave/lib/hooks/reopen-github-issues.js" ]; then
|
|
121
|
-
REOPEN_SCRIPT="$PROJECT_ROOT/plugins/specweave/lib/hooks/reopen-github-issues.js"
|
|
122
|
-
elif [ -f "$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/reopen-github-issues.js" ]; then
|
|
123
|
-
REOPEN_SCRIPT="$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/reopen-github-issues.js"
|
|
124
|
-
elif [ -f "$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/reopen-github-issues.js" ]; then
|
|
125
|
-
REOPEN_SCRIPT="$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/reopen-github-issues.js"
|
|
126
|
-
elif [ -n "${CLAUDE_PLUGIN_ROOT}" ] && [ -f "${CLAUDE_PLUGIN_ROOT}/lib/hooks/reopen-github-issues.js" ]; then
|
|
127
|
-
REOPEN_SCRIPT="${CLAUDE_PLUGIN_ROOT}/lib/hooks/reopen-github-issues.js"
|
|
128
|
-
fi
|
|
129
|
-
|
|
130
|
-
if [ -n "$REOPEN_SCRIPT" ]; then
|
|
131
|
-
# Load GITHUB_TOKEN from .env
|
|
132
|
-
if [ -f "$PROJECT_ROOT/.env" ]; then
|
|
133
|
-
GITHUB_TOKEN_FROM_ENV=$(grep -E '^GITHUB_TOKEN=' "$PROJECT_ROOT/.env" 2>/dev/null | head -1 | cut -d'=' -f2- | sed 's/^["'\'']//' | sed 's/["'\'']$//')
|
|
134
|
-
if [ -n "$GITHUB_TOKEN_FROM_ENV" ]; then
|
|
135
|
-
export GITHUB_TOKEN="$GITHUB_TOKEN_FROM_ENV"
|
|
136
|
-
fi
|
|
137
|
-
fi
|
|
138
|
-
|
|
139
|
-
echo "▶️ Reopening GitHub issues for resumed increment..."
|
|
140
|
-
(cd "$PROJECT_ROOT" && node "$REOPEN_SCRIPT" "$INCREMENT_ID" "${REASON:-Increment resumed}") 2>&1 | tee -a "$DEBUG_LOG" || {
|
|
141
|
-
echo "[$(date)] ⚠️ Failed to reopen issues (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
142
|
-
}
|
|
143
|
-
else
|
|
144
|
-
echo "[$(date)] ⚠️ reopen-github-issues.js not found" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
145
|
-
fi
|
|
146
|
-
fi
|
|
147
|
-
;;
|
|
148
|
-
|
|
149
|
-
abandoned)
|
|
150
|
-
# ========================================================================
|
|
151
|
-
# CLOSE GitHub Issues on Abandon (NEW)
|
|
152
|
-
# ========================================================================
|
|
153
|
-
echo "[$(date)] 🗑️ Status is abandoned - closing GitHub issues" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
154
|
-
|
|
155
|
-
if command -v node &> /dev/null; then
|
|
156
|
-
# Find close script
|
|
157
|
-
CLOSE_SCRIPT=""
|
|
158
|
-
if [ -f "$PROJECT_ROOT/plugins/specweave/lib/hooks/close-github-issues-abandoned.js" ]; then
|
|
159
|
-
CLOSE_SCRIPT="$PROJECT_ROOT/plugins/specweave/lib/hooks/close-github-issues-abandoned.js"
|
|
160
|
-
elif [ -f "$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/close-github-issues-abandoned.js" ]; then
|
|
161
|
-
CLOSE_SCRIPT="$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/close-github-issues-abandoned.js"
|
|
162
|
-
elif [ -f "$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/close-github-issues-abandoned.js" ]; then
|
|
163
|
-
CLOSE_SCRIPT="$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/close-github-issues-abandoned.js"
|
|
164
|
-
elif [ -n "${CLAUDE_PLUGIN_ROOT}" ] && [ -f "${CLAUDE_PLUGIN_ROOT}/lib/hooks/close-github-issues-abandoned.js" ]; then
|
|
165
|
-
CLOSE_SCRIPT="${CLAUDE_PLUGIN_ROOT}/lib/hooks/close-github-issues-abandoned.js"
|
|
166
|
-
fi
|
|
167
|
-
|
|
168
|
-
if [ -n "$CLOSE_SCRIPT" ]; then
|
|
169
|
-
# Load GITHUB_TOKEN from .env
|
|
170
|
-
if [ -f "$PROJECT_ROOT/.env" ]; then
|
|
171
|
-
GITHUB_TOKEN_FROM_ENV=$(grep -E '^GITHUB_TOKEN=' "$PROJECT_ROOT/.env" 2>/dev/null | head -1 | cut -d'=' -f2- | sed 's/^["'\'']//' | sed 's/["'\'']$//')
|
|
172
|
-
if [ -n "$GITHUB_TOKEN_FROM_ENV" ]; then
|
|
173
|
-
export GITHUB_TOKEN="$GITHUB_TOKEN_FROM_ENV"
|
|
174
|
-
fi
|
|
175
|
-
fi
|
|
176
|
-
|
|
177
|
-
echo "🗑️ Closing GitHub issues for abandoned increment..."
|
|
178
|
-
(cd "$PROJECT_ROOT" && node "$CLOSE_SCRIPT" "$INCREMENT_ID" "${REASON:-Increment abandoned}") 2>&1 | tee -a "$DEBUG_LOG" || {
|
|
179
|
-
echo "[$(date)] ⚠️ Failed to close issues (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
180
|
-
}
|
|
181
|
-
else
|
|
182
|
-
echo "[$(date)] ⚠️ close-github-issues-abandoned.js not found" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
183
|
-
fi
|
|
184
|
-
fi
|
|
185
|
-
;;
|
|
186
|
-
|
|
187
|
-
paused)
|
|
188
|
-
# Paused: Just post comment (no close/reopen)
|
|
189
|
-
echo "[$(date)] ⏸️ Status is paused - posting comment only" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
190
|
-
;;
|
|
191
|
-
esac
|
|
192
|
-
|
|
193
|
-
# ============================================================================
|
|
194
|
-
# POST COMMENT TO MAIN ISSUE (always, for all status changes)
|
|
195
|
-
# ============================================================================
|
|
196
|
-
|
|
197
|
-
if [ -n "$GITHUB_ISSUE" ]; then
|
|
198
|
-
echo "[$(date)] 🔄 Posting status change to GitHub issue #$GITHUB_ISSUE" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
199
|
-
|
|
200
|
-
# Build comment based on status
|
|
201
|
-
EMOJI=""
|
|
202
|
-
TITLE=""
|
|
203
|
-
case "$NEW_STATUS" in
|
|
204
|
-
paused)
|
|
205
|
-
EMOJI="⏸️"
|
|
206
|
-
TITLE="Increment Paused"
|
|
207
|
-
;;
|
|
208
|
-
resumed|active|in-progress)
|
|
209
|
-
EMOJI="▶️"
|
|
210
|
-
TITLE="Increment Resumed"
|
|
211
|
-
;;
|
|
212
|
-
abandoned)
|
|
213
|
-
EMOJI="🗑️"
|
|
214
|
-
TITLE="Increment Abandoned"
|
|
215
|
-
;;
|
|
216
|
-
esac
|
|
217
|
-
|
|
218
|
-
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
219
|
-
|
|
220
|
-
COMMENT="$EMOJI **$TITLE**
|
|
221
|
-
|
|
222
|
-
**Reason**: ${REASON:-Not specified}
|
|
223
|
-
|
|
224
|
-
**Timestamp**: $TIMESTAMP
|
|
225
|
-
|
|
226
|
-
---
|
|
227
|
-
🤖 Auto-updated by SpecWeave"
|
|
228
|
-
|
|
229
|
-
# Post comment
|
|
230
|
-
echo "$COMMENT" | gh issue comment "$GITHUB_ISSUE" --body-file - 2>&1 | tee -a "$DEBUG_LOG" >/dev/null || {
|
|
231
|
-
echo "[$(date)] ⚠️ Failed to post comment (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
echo "[$(date)] ✅ Status change synced to GitHub" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
235
|
-
else
|
|
236
|
-
echo "[$(date)] ℹ️ No main GitHub issue linked" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
237
|
-
fi
|
|
238
|
-
|
|
239
|
-
# Update status line cache (status changed - may affect which increment is "current")
|
|
240
|
-
bash "$HOOK_DIR/lib/update-status-line.sh" 2>/dev/null || true
|
|
241
|
-
|
|
242
|
-
# Return success (non-blocking)
|
|
243
|
-
exit 0
|
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
#
|
|
3
|
-
# Post-Metadata-Change Hook: Dispatcher for Increment Lifecycle Events
|
|
4
|
-
#
|
|
5
|
-
# Triggers: After Write/Edit modifies metadata.json
|
|
6
|
-
# Purpose: Detect WHAT changed in metadata and call appropriate lifecycle hook
|
|
7
|
-
#
|
|
8
|
-
# Architecture:
|
|
9
|
-
# - metadata.json is the source of truth for increment state
|
|
10
|
-
# - Different state changes require different actions:
|
|
11
|
-
# * status: "completed" → Call post-increment-completion.sh
|
|
12
|
-
# * status: "paused"|"resumed"|"abandoned" → Call post-increment-status-change.sh
|
|
13
|
-
# * other changes → Update status line only
|
|
14
|
-
#
|
|
15
|
-
# This fixes the critical bug where status line never updates on increment closure
|
|
16
|
-
# because post-increment-completion.sh was orphaned (never registered or called).
|
|
17
|
-
#
|
|
18
|
-
# Related Incident: 2025-11-20 - Increment 0047 completed but status line still shows active
|
|
19
|
-
# Root Cause: metadata.json writes don't trigger status line refresh
|
|
20
|
-
# Fix: This hook dispatches to post-increment-completion.sh which updates status line
|
|
21
|
-
|
|
22
|
-
# EMERGENCY FIX v0.24.3: Remove set -e - it causes Claude Code crashes!
|
|
23
|
-
set +e
|
|
24
|
-
|
|
25
|
-
# Find project root (must be BEFORE recursion guard to get PROJECT_ROOT)
|
|
26
|
-
find_project_root() {
|
|
27
|
-
local dir="$PWD"
|
|
28
|
-
while [[ "$dir" != "/" ]]; do
|
|
29
|
-
if [[ -d "$dir/.specweave" ]]; then
|
|
30
|
-
echo "$dir"
|
|
31
|
-
return 0
|
|
32
|
-
fi
|
|
33
|
-
dir=$(dirname "$dir")
|
|
34
|
-
done
|
|
35
|
-
echo "$PWD"
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
PROJECT_ROOT=$(find_project_root)
|
|
39
|
-
LOGS_DIR="$PROJECT_ROOT/.specweave/logs"
|
|
40
|
-
DEBUG_LOG="$LOGS_DIR/hooks-debug.log"
|
|
41
|
-
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
42
|
-
|
|
43
|
-
# ============================================================================
|
|
44
|
-
# RECURSION PREVENTION (CRITICAL - v0.26.0 - FILE-BASED GUARD)
|
|
45
|
-
# ============================================================================
|
|
46
|
-
# NEW SOLUTION (v0.26.0): File-based recursion guard
|
|
47
|
-
# See: ADR-0073 (Hook Recursion Prevention Strategy)
|
|
48
|
-
|
|
49
|
-
RECURSION_GUARD_FILE="$PROJECT_ROOT/.specweave/state/.hook-recursion-guard"
|
|
50
|
-
|
|
51
|
-
if [[ -f "$RECURSION_GUARD_FILE" ]]; then
|
|
52
|
-
# Silent exit - we're already inside a hook chain
|
|
53
|
-
exit 0
|
|
54
|
-
fi
|
|
55
|
-
|
|
56
|
-
# EMERGENCY KILL SWITCH
|
|
57
|
-
if [[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]]; then
|
|
58
|
-
exit 0
|
|
59
|
-
fi
|
|
60
|
-
|
|
61
|
-
# Ensure logs directory exists
|
|
62
|
-
mkdir -p "$LOGS_DIR" 2>/dev/null || true
|
|
63
|
-
|
|
64
|
-
# ============================================================================
|
|
65
|
-
# CREATE RECURSION GUARD (v0.28.2 - CRITICAL FIX)
|
|
66
|
-
# ============================================================================
|
|
67
|
-
# PROBLEM FIXED: Guard was only created in post-increment-completion.sh, but
|
|
68
|
-
# post-increment-status-change.sh had NO guard protection.
|
|
69
|
-
#
|
|
70
|
-
# SOLUTION: Create guard here in the dispatcher so ALL dispatches are protected.
|
|
71
|
-
# The sub-hooks (post-increment-completion.sh, post-increment-status-change.sh)
|
|
72
|
-
# will see this guard and exit early if they're called recursively.
|
|
73
|
-
#
|
|
74
|
-
# This prevents infinite loops if any sync operation modifies metadata.json.
|
|
75
|
-
|
|
76
|
-
mkdir -p "$PROJECT_ROOT/.specweave/state" 2>/dev/null || true
|
|
77
|
-
touch "$RECURSION_GUARD_FILE"
|
|
78
|
-
|
|
79
|
-
# Ensure guard file is ALWAYS removed when script exits (even on error)
|
|
80
|
-
trap 'rm -f "$RECURSION_GUARD_FILE" 2>/dev/null || true' EXIT SIGINT SIGTERM
|
|
81
|
-
|
|
82
|
-
# ============================================================================
|
|
83
|
-
# READ STDIN (v0.26.1 - CRITICAL FIX)
|
|
84
|
-
# ============================================================================
|
|
85
|
-
# PostToolUse hooks receive JSON data from Claude Code via STDIN, NOT env vars!
|
|
86
|
-
# This was the critical bug: hook was looking for environment variables that don't exist.
|
|
87
|
-
#
|
|
88
|
-
# Example STDIN data:
|
|
89
|
-
# {"tool": "Edit", "tool_input": {"file_path": "/path/to/file.md", ...}}
|
|
90
|
-
# {"tool": "Write", "tool_input": {"file_path": "/path/to/file.md", ...}}
|
|
91
|
-
|
|
92
|
-
STDIN_DATA=$(mktemp)
|
|
93
|
-
cat > "$STDIN_DATA"
|
|
94
|
-
|
|
95
|
-
echo "[$(date)] post-metadata-change: Hook fired" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
96
|
-
echo "[$(date)] Input JSON:" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
97
|
-
cat "$STDIN_DATA" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
98
|
-
echo "" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
99
|
-
|
|
100
|
-
# ============================================================================
|
|
101
|
-
# EARLY EXIT OPTIMIZATION (v0.25.0): Ultra-Fast Rejection of Non-Metadata Changes
|
|
102
|
-
# ============================================================================
|
|
103
|
-
# This hook should ONLY run for metadata.json changes.
|
|
104
|
-
# 99.9% of Edit/Write operations are NOT metadata.json.
|
|
105
|
-
# Do fastest possible check first to minimize overhead.
|
|
106
|
-
|
|
107
|
-
# Quick check: If STDIN doesn't contain "metadata.json", exit immediately
|
|
108
|
-
if ! grep -q "metadata\.json" "$STDIN_DATA" 2>/dev/null; then
|
|
109
|
-
echo "[$(date)] post-metadata-change: Not metadata.json - exiting" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
110
|
-
rm -f "$STDIN_DATA"
|
|
111
|
-
exit 0 # Fast path: Not metadata.json
|
|
112
|
-
fi
|
|
113
|
-
|
|
114
|
-
echo "[$(date)] post-metadata-change: metadata.json detected in input" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
115
|
-
|
|
116
|
-
# ============================================================================
|
|
117
|
-
# EXTRACT FILE PATH FROM STDIN JSON
|
|
118
|
-
# ============================================================================
|
|
119
|
-
|
|
120
|
-
# Parse file_path from JSON (handles both Edit and Write tool formats)
|
|
121
|
-
MODIFIED_FILE=$(grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' "$STDIN_DATA" | head -1 | sed 's/.*"\([^"]*\)".*/\1/' || echo "")
|
|
122
|
-
|
|
123
|
-
# Clean up temp file
|
|
124
|
-
rm -f "$STDIN_DATA"
|
|
125
|
-
|
|
126
|
-
echo "[$(date)] post-metadata-change: Detected file: ${MODIFIED_FILE:-<none>}" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
127
|
-
|
|
128
|
-
# Check if this is a metadata.json change in an increment folder
|
|
129
|
-
if [[ -z "$MODIFIED_FILE" ]] || [[ "$MODIFIED_FILE" != *"/metadata.json" ]] || [[ "$MODIFIED_FILE" != *"/.specweave/increments/"* ]]; then
|
|
130
|
-
# Not a metadata.json change in increments folder - exit silently
|
|
131
|
-
exit 0
|
|
132
|
-
fi
|
|
133
|
-
|
|
134
|
-
# Exclude archived increments (shouldn't affect status line)
|
|
135
|
-
if [[ "$MODIFIED_FILE" == *"/_archive/"* ]]; then
|
|
136
|
-
echo "[$(date)] post-metadata-change: Archived increment - skipping" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
137
|
-
exit 0
|
|
138
|
-
fi
|
|
139
|
-
|
|
140
|
-
echo "[$(date)] post-metadata-change: metadata.json changed - analyzing..." >> "$DEBUG_LOG" 2>/dev/null || true
|
|
141
|
-
|
|
142
|
-
# Extract increment ID from path
|
|
143
|
-
# Path format: /path/to/project/.specweave/increments/0047-name/metadata.json
|
|
144
|
-
INCREMENT_ID=$(echo "$MODIFIED_FILE" | grep -o '\.specweave/increments/[^/]*' | sed 's/\.specweave\/increments\///')
|
|
145
|
-
|
|
146
|
-
if [[ -z "$INCREMENT_ID" ]]; then
|
|
147
|
-
echo "[$(date)] post-metadata-change: Could not extract increment ID from path: $MODIFIED_FILE" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
148
|
-
exit 0
|
|
149
|
-
fi
|
|
150
|
-
|
|
151
|
-
echo "[$(date)] post-metadata-change: Increment ID: $INCREMENT_ID" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
152
|
-
|
|
153
|
-
# Read the metadata.json to detect what changed
|
|
154
|
-
METADATA_PATH="$PROJECT_ROOT/.specweave/increments/$INCREMENT_ID/metadata.json"
|
|
155
|
-
|
|
156
|
-
if [[ ! -f "$METADATA_PATH" ]]; then
|
|
157
|
-
echo "[$(date)] post-metadata-change: metadata.json not found at $METADATA_PATH" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
158
|
-
exit 0
|
|
159
|
-
fi
|
|
160
|
-
|
|
161
|
-
# Check if jq is available for parsing JSON
|
|
162
|
-
if ! command -v jq &> /dev/null; then
|
|
163
|
-
echo "[$(date)] post-metadata-change: jq not found - updating status line as fallback" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
164
|
-
bash "$HOOK_DIR/lib/update-status-line.sh" 2>/dev/null || true
|
|
165
|
-
exit 0
|
|
166
|
-
fi
|
|
167
|
-
|
|
168
|
-
# Extract current status
|
|
169
|
-
CURRENT_STATUS=$(jq -r '.status // "unknown"' "$METADATA_PATH" 2>/dev/null)
|
|
170
|
-
|
|
171
|
-
echo "[$(date)] post-metadata-change: Current status: $CURRENT_STATUS" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
172
|
-
|
|
173
|
-
# ============================================================================
|
|
174
|
-
# CRITICAL FIX (v0.28.12): Remove guard BEFORE calling sub-hooks
|
|
175
|
-
# ============================================================================
|
|
176
|
-
# PROBLEM: Sub-hooks (post-increment-completion.sh) check for recursion guard
|
|
177
|
-
# and exit immediately if it exists. But the guard was created HERE (line 77),
|
|
178
|
-
# so sub-hooks NEVER ran - causing status line to never update!
|
|
179
|
-
#
|
|
180
|
-
# SOLUTION: Temporarily remove the guard before calling sub-hooks.
|
|
181
|
-
# The guard's purpose is to prevent THIS hook from being called recursively
|
|
182
|
-
# (if sub-hooks modify metadata.json). Sub-hooks have their OWN guards.
|
|
183
|
-
#
|
|
184
|
-
# See: Root cause analysis 2025-11-25 - status line never updates after /done
|
|
185
|
-
rm -f "$RECURSION_GUARD_FILE" 2>/dev/null || true
|
|
186
|
-
|
|
187
|
-
# Dispatch to appropriate lifecycle hook based on status
|
|
188
|
-
case "$CURRENT_STATUS" in
|
|
189
|
-
completed)
|
|
190
|
-
# Increment completed - call post-increment-completion.sh
|
|
191
|
-
# This hook handles:
|
|
192
|
-
# - Closing GitHub issues
|
|
193
|
-
# - Syncing living docs
|
|
194
|
-
# - Updating status line
|
|
195
|
-
echo "[$(date)] post-metadata-change: Increment completed - calling post-increment-completion.sh" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
196
|
-
|
|
197
|
-
if [[ -x "$HOOK_DIR/post-increment-completion.sh" ]]; then
|
|
198
|
-
bash "$HOOK_DIR/post-increment-completion.sh" "$INCREMENT_ID" 2>&1 | tee -a "$DEBUG_LOG" >/dev/null || {
|
|
199
|
-
echo "[$(date)] post-metadata-change: post-increment-completion.sh failed (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
200
|
-
}
|
|
201
|
-
else
|
|
202
|
-
echo "[$(date)] post-metadata-change: post-increment-completion.sh not found or not executable" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
203
|
-
fi
|
|
204
|
-
# ALWAYS update status line after completion (force bypass TTL cache)
|
|
205
|
-
bash "$HOOK_DIR/lib/update-status-line.sh" --force 2>/dev/null || true
|
|
206
|
-
;;
|
|
207
|
-
|
|
208
|
-
paused|resumed|abandoned)
|
|
209
|
-
# Status change - call post-increment-status-change.sh
|
|
210
|
-
# Note: This typically gets called manually by /sw:pause commands
|
|
211
|
-
# But we handle it here for completeness
|
|
212
|
-
echo "[$(date)] post-metadata-change: Status changed to $CURRENT_STATUS - calling post-increment-status-change.sh" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
213
|
-
|
|
214
|
-
if [[ -x "$HOOK_DIR/post-increment-status-change.sh" ]]; then
|
|
215
|
-
# Extract reason if available
|
|
216
|
-
REASON=$(jq -r '.statusReason // "Not specified"' "$METADATA_PATH" 2>/dev/null)
|
|
217
|
-
bash "$HOOK_DIR/post-increment-status-change.sh" "$INCREMENT_ID" "$CURRENT_STATUS" "$REASON" 2>&1 | tee -a "$DEBUG_LOG" >/dev/null || {
|
|
218
|
-
echo "[$(date)] post-metadata-change: post-increment-status-change.sh failed (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
219
|
-
}
|
|
220
|
-
else
|
|
221
|
-
echo "[$(date)] post-metadata-change: post-increment-status-change.sh not found" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
222
|
-
fi
|
|
223
|
-
# ALWAYS update status line after status change (force bypass TTL cache)
|
|
224
|
-
bash "$HOOK_DIR/lib/update-status-line.sh" --force 2>/dev/null || true
|
|
225
|
-
;;
|
|
226
|
-
|
|
227
|
-
active|planning|backlog|ready_for_review)
|
|
228
|
-
# Increment became active - MUST register in active-increment.json!
|
|
229
|
-
# CRITICAL FIX (v0.26.15): post-task-completion.sh depends on this file
|
|
230
|
-
# Without registration, ALL sync operations are skipped!
|
|
231
|
-
echo "[$(date)] post-metadata-change: Status is $CURRENT_STATUS - registering as active + updating status line" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
232
|
-
bash "$HOOK_DIR/lib/update-active-increment.sh" 2>/dev/null || true
|
|
233
|
-
bash "$HOOK_DIR/lib/update-status-line.sh" 2>/dev/null || true
|
|
234
|
-
;;
|
|
235
|
-
|
|
236
|
-
*)
|
|
237
|
-
# Other metadata changes (e.g., task completion count, AC count)
|
|
238
|
-
# Just update status line to reflect new progress
|
|
239
|
-
echo "[$(date)] post-metadata-change: Status is $CURRENT_STATUS - updating status line only" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
240
|
-
bash "$HOOK_DIR/lib/update-status-line.sh" 2>/dev/null || true
|
|
241
|
-
;;
|
|
242
|
-
esac
|
|
243
|
-
|
|
244
|
-
echo "[$(date)] post-metadata-change: Complete" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
245
|
-
|
|
246
|
-
exit 0
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
###############################################################################
|
|
4
|
-
# SpecWeave Post-Spec-Update Hook
|
|
5
|
-
#
|
|
6
|
-
# CRITICAL ARCHITECTURE:
|
|
7
|
-
# - Fires when .specweave/docs/internal/specs/spec-*.md is updated
|
|
8
|
-
# - Auto-syncs to linked external tool (GitHub Project/Jira Epic/ADO Feature)
|
|
9
|
-
# - Replaces increment-based sync (which was wrong!)
|
|
10
|
-
#
|
|
11
|
-
# Trigger Points:
|
|
12
|
-
# 1. After /sw:sync-docs update (spec updated from increment)
|
|
13
|
-
# 2. After manual spec.md edits (user updates living docs directly)
|
|
14
|
-
# 3. After bidirectional sync from external tool
|
|
15
|
-
#
|
|
16
|
-
# What It Does:
|
|
17
|
-
# - Detects which spec was updated
|
|
18
|
-
# - Checks if spec is linked to external tool (frontmatter: externalLinks)
|
|
19
|
-
# - Syncs changes to GitHub Project / Jira Epic / ADO Feature
|
|
20
|
-
# - Updates user stories, acceptance criteria, progress
|
|
21
|
-
#
|
|
22
|
-
# Usage:
|
|
23
|
-
# post-spec-update.sh <spec-file-path>
|
|
24
|
-
#
|
|
25
|
-
# Example:
|
|
26
|
-
# post-spec-update.sh .specweave/docs/internal/specs/spec-001-core-framework.md
|
|
27
|
-
#
|
|
28
|
-
###############################################################################
|
|
29
|
-
|
|
30
|
-
set +e # EMERGENCY FIX: Changed from set -euo pipefail to prevent Claude Code crashes
|
|
31
|
-
|
|
32
|
-
# Arguments
|
|
33
|
-
SPEC_FILE_PATH="${1:-}"
|
|
34
|
-
|
|
35
|
-
# Validate arguments
|
|
36
|
-
if [[ -z "$SPEC_FILE_PATH" ]]; then
|
|
37
|
-
echo "❌ Error: Spec file path required"
|
|
38
|
-
echo "Usage: post-spec-update.sh <spec-file-path>"
|
|
39
|
-
exit 1
|
|
40
|
-
fi
|
|
41
|
-
|
|
42
|
-
# Check if spec file exists
|
|
43
|
-
if [[ ! -f "$SPEC_FILE_PATH" ]]; then
|
|
44
|
-
echo "❌ Error: Spec file not found: $SPEC_FILE_PATH"
|
|
45
|
-
exit 1
|
|
46
|
-
fi
|
|
47
|
-
|
|
48
|
-
# Extract spec ID from file path
|
|
49
|
-
# Example: .specweave/docs/internal/specs/spec-001-core-framework.md → spec-001
|
|
50
|
-
SPEC_ID=$(basename "$SPEC_FILE_PATH" .md)
|
|
51
|
-
|
|
52
|
-
echo ""
|
|
53
|
-
echo "🔗 Post-Spec-Update Hook"
|
|
54
|
-
echo " Spec: $SPEC_ID"
|
|
55
|
-
echo " File: $SPEC_FILE_PATH"
|
|
56
|
-
|
|
57
|
-
# Load config to check if auto-sync is enabled
|
|
58
|
-
CONFIG_FILE=".specweave/config.json"
|
|
59
|
-
if [[ ! -f "$CONFIG_FILE" ]]; then
|
|
60
|
-
echo " ℹ️ No config file found, skipping auto-sync"
|
|
61
|
-
exit 0
|
|
62
|
-
fi
|
|
63
|
-
|
|
64
|
-
# Check if auto-sync is enabled
|
|
65
|
-
AUTO_SYNC=$(jq -r '.hooks.post_spec_update.auto_sync // true' "$CONFIG_FILE")
|
|
66
|
-
|
|
67
|
-
if [[ "$AUTO_SYNC" != "true" ]]; then
|
|
68
|
-
echo " ℹ️ Auto-sync disabled in config, skipping"
|
|
69
|
-
exit 0
|
|
70
|
-
fi
|
|
71
|
-
|
|
72
|
-
# Parse spec frontmatter to detect external links
|
|
73
|
-
# Use gray-matter or similar to extract YAML frontmatter
|
|
74
|
-
# For now, use simple grep/sed approach
|
|
75
|
-
|
|
76
|
-
# Check if GitHub link exists
|
|
77
|
-
GITHUB_LINK=$(grep -A 10 "^externalLinks:" "$SPEC_FILE_PATH" | grep -A 5 "github:" | grep "projectId:" | sed 's/.*projectId: *//; s/ *$//' || echo "")
|
|
78
|
-
|
|
79
|
-
# Check if Jira link exists
|
|
80
|
-
JIRA_LINK=$(grep -A 10 "^externalLinks:" "$SPEC_FILE_PATH" | grep -A 5 "jira:" | grep "epicKey:" | sed 's/.*epicKey: *//; s/ *$//' || echo "")
|
|
81
|
-
|
|
82
|
-
# Check if ADO link exists
|
|
83
|
-
ADO_LINK=$(grep -A 10 "^externalLinks:" "$SPEC_FILE_PATH" | grep -A 5 "ado:" | grep "featureId:" | sed 's/.*featureId: *//; s/ *$//' || echo "")
|
|
84
|
-
|
|
85
|
-
# Determine which provider to sync
|
|
86
|
-
PROVIDER=""
|
|
87
|
-
if [[ -n "$GITHUB_LINK" ]]; then
|
|
88
|
-
PROVIDER="github"
|
|
89
|
-
EXTERNAL_ID="$GITHUB_LINK"
|
|
90
|
-
elif [[ -n "$JIRA_LINK" ]]; then
|
|
91
|
-
PROVIDER="jira"
|
|
92
|
-
EXTERNAL_ID="$JIRA_LINK"
|
|
93
|
-
elif [[ -n "$ADO_LINK" ]]; then
|
|
94
|
-
PROVIDER="ado"
|
|
95
|
-
EXTERNAL_ID="$ADO_LINK"
|
|
96
|
-
fi
|
|
97
|
-
|
|
98
|
-
# No external link found - skip sync
|
|
99
|
-
if [[ -z "$PROVIDER" ]]; then
|
|
100
|
-
echo " ℹ️ Spec not linked to external tool, skipping sync"
|
|
101
|
-
echo " Hint: Use /sw-github:sync-spec or /sw-jira:sync-spec to link"
|
|
102
|
-
exit 0
|
|
103
|
-
fi
|
|
104
|
-
|
|
105
|
-
echo " 🔗 Detected external link: $PROVIDER (ID: $EXTERNAL_ID)"
|
|
106
|
-
|
|
107
|
-
# Sync to external tool based on provider
|
|
108
|
-
case "$PROVIDER" in
|
|
109
|
-
github)
|
|
110
|
-
echo " 🔄 Syncing to GitHub Project..."
|
|
111
|
-
|
|
112
|
-
# Check if GitHub CLI is available
|
|
113
|
-
if ! command -v gh &> /dev/null; then
|
|
114
|
-
echo " ⚠️ GitHub CLI (gh) not found, skipping sync"
|
|
115
|
-
exit 0
|
|
116
|
-
fi
|
|
117
|
-
|
|
118
|
-
# TODO: Call GitHub spec sync
|
|
119
|
-
# For now, just log the action
|
|
120
|
-
echo " ✅ GitHub sync queued (implementation pending)"
|
|
121
|
-
;;
|
|
122
|
-
|
|
123
|
-
jira)
|
|
124
|
-
echo " 🔄 Syncing to Jira Epic..."
|
|
125
|
-
|
|
126
|
-
# Check if Jira config exists
|
|
127
|
-
if [[ -z "${JIRA_DOMAIN:-}" ]]; then
|
|
128
|
-
echo " ⚠️ Jira not configured (.env), skipping sync"
|
|
129
|
-
exit 0
|
|
130
|
-
fi
|
|
131
|
-
|
|
132
|
-
# TODO: Call Jira spec sync
|
|
133
|
-
echo " ✅ Jira sync queued (implementation pending)"
|
|
134
|
-
;;
|
|
135
|
-
|
|
136
|
-
ado)
|
|
137
|
-
echo " 🔄 Syncing to Azure DevOps Feature..."
|
|
138
|
-
|
|
139
|
-
# Check if ADO config exists
|
|
140
|
-
if [[ -z "${ADO_ORGANIZATION:-}" ]]; then
|
|
141
|
-
echo " ⚠️ ADO not configured (.env), skipping sync"
|
|
142
|
-
exit 0
|
|
143
|
-
fi
|
|
144
|
-
|
|
145
|
-
# TODO: Call ADO spec sync
|
|
146
|
-
echo " ✅ ADO sync queued (implementation pending)"
|
|
147
|
-
;;
|
|
148
|
-
|
|
149
|
-
*)
|
|
150
|
-
echo " ⚠️ Unknown provider: $PROVIDER"
|
|
151
|
-
exit 0
|
|
152
|
-
;;
|
|
153
|
-
esac
|
|
154
|
-
|
|
155
|
-
echo " ✅ Post-spec-update hook complete"
|
|
156
|
-
echo ""
|
|
157
|
-
|
|
158
|
-
exit 0
|