specweave 0.26.11 → 0.26.13
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/dist/plugins/specweave-github/lib/completion-calculator.d.ts +4 -1
- package/dist/plugins/specweave-github/lib/completion-calculator.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/completion-calculator.js +49 -29
- package/dist/plugins/specweave-github/lib/completion-calculator.js.map +1 -1
- package/dist/src/core/increment/increment-archiver.d.ts +3 -0
- package/dist/src/core/increment/increment-archiver.d.ts.map +1 -1
- package/dist/src/core/increment/increment-archiver.js +35 -4
- package/dist/src/core/increment/increment-archiver.js.map +1 -1
- package/dist/src/core/living-docs/feature-archiver.d.ts +5 -0
- package/dist/src/core/living-docs/feature-archiver.d.ts.map +1 -1
- package/dist/src/core/living-docs/feature-archiver.js +66 -18
- package/dist/src/core/living-docs/feature-archiver.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/commands/specweave-archive.md +10 -1
- package/plugins/specweave/hooks/docs-changed.sh.backup +79 -0
- package/plugins/specweave/hooks/hooks.json +10 -0
- package/plugins/specweave/hooks/human-input-required.sh.backup +75 -0
- package/plugins/specweave/hooks/lib/update-active-increment.sh +96 -0
- package/plugins/specweave/hooks/lib/update-status-line.sh +153 -189
- package/plugins/specweave/hooks/post-edit-write-consolidated.sh +6 -0
- package/plugins/specweave/hooks/post-first-increment.sh.backup +61 -0
- package/plugins/specweave/hooks/post-increment-change.sh.backup +98 -0
- package/plugins/specweave/hooks/post-increment-completion.sh.backup +231 -0
- package/plugins/specweave/hooks/post-increment-planning.sh.backup +1048 -0
- package/plugins/specweave/hooks/post-increment-status-change.sh.backup +147 -0
- package/plugins/specweave/hooks/post-metadata-change.sh +9 -0
- package/plugins/specweave/hooks/post-spec-update.sh.backup +158 -0
- package/plugins/specweave/hooks/post-task-completion.sh +8 -0
- package/plugins/specweave/hooks/post-task-edit.sh +37 -0
- package/plugins/specweave/hooks/post-user-story-complete.sh.backup +179 -0
- package/plugins/specweave/hooks/pre-command-deduplication.sh +43 -53
- package/plugins/specweave/hooks/pre-command-deduplication.sh.backup +83 -0
- package/plugins/specweave/hooks/pre-implementation.sh.backup +67 -0
- package/plugins/specweave/hooks/pre-task-completion.sh.backup +194 -0
- package/plugins/specweave/hooks/pre-tool-use.sh +5 -0
- package/plugins/specweave/hooks/pre-tool-use.sh.backup +133 -0
- package/plugins/specweave/hooks/user-prompt-submit.sh +143 -289
- package/plugins/specweave/hooks/user-prompt-submit.sh.backup +386 -0
- package/plugins/specweave-ado/hooks/post-living-docs-update.sh.backup +353 -0
- package/plugins/specweave-ado/hooks/post-task-completion.sh.backup +172 -0
- package/plugins/specweave-ado/lib/ado-multi-project-sync.js +1 -0
- package/plugins/specweave-ado/lib/enhanced-ado-sync.js +170 -0
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +1098 -0
- package/plugins/specweave-github/hooks/post-task-completion.sh.backup +258 -0
- package/plugins/specweave-github/lib/completion-calculator.js +34 -16
- package/plugins/specweave-github/lib/completion-calculator.ts +54 -32
- package/plugins/specweave-jira/hooks/post-task-completion.sh.backup +172 -0
- package/plugins/specweave-jira/lib/enhanced-jira-sync.js +3 -3
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +1008 -0
- package/plugins/specweave-release/hooks/post-task-completion.sh.backup +110 -0
- package/src/templates/AGENTS.md.template +301 -2452
- package/src/templates/CLAUDE.md.template +99 -667
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# SpecWeave Post-Increment-Status-Change Hook
|
|
4
|
+
# Runs automatically after increment status changes (pause/resume/abandon)
|
|
5
|
+
#
|
|
6
|
+
# Trigger: /specweave:pause, /specweave:resume, /specweave:abandon commands
|
|
7
|
+
# Purpose: Notify GitHub when increment status changes
|
|
8
|
+
#
|
|
9
|
+
# What it does:
|
|
10
|
+
# 1. Detects status change (paused, resumed, abandoned)
|
|
11
|
+
# 2. Posts comment to GitHub issue
|
|
12
|
+
# 3. Updates metadata
|
|
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
|
|
21
|
+
|
|
22
|
+
# Find project root
|
|
23
|
+
find_project_root() {
|
|
24
|
+
local dir="$1"
|
|
25
|
+
while [ "$dir" != "/" ]; do
|
|
26
|
+
if [ -d "$dir/.specweave" ]; then
|
|
27
|
+
echo "$dir"
|
|
28
|
+
return 0
|
|
29
|
+
fi
|
|
30
|
+
dir="$(dirname "$dir")"
|
|
31
|
+
done
|
|
32
|
+
pwd
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
PROJECT_ROOT="$(find_project_root "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")"
|
|
36
|
+
cd "$PROJECT_ROOT" 2>/dev/null || true
|
|
37
|
+
|
|
38
|
+
# Configuration
|
|
39
|
+
LOGS_DIR=".specweave/logs"
|
|
40
|
+
DEBUG_LOG="$LOGS_DIR/hooks-debug.log"
|
|
41
|
+
|
|
42
|
+
mkdir -p "$LOGS_DIR" 2>/dev/null || true
|
|
43
|
+
|
|
44
|
+
# Arguments
|
|
45
|
+
INCREMENT_ID="$1"
|
|
46
|
+
NEW_STATUS="$2"
|
|
47
|
+
REASON="$3"
|
|
48
|
+
|
|
49
|
+
if [ -z "$INCREMENT_ID" ] || [ -z "$NEW_STATUS" ]; then
|
|
50
|
+
echo "Usage: $0 <incrementId> <newStatus> [reason]" >&2
|
|
51
|
+
echo "Example: $0 0015-hierarchical-sync paused 'Waiting for API'" >&2
|
|
52
|
+
exit 1
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
echo "[$(date)] 📊 Status changed: $NEW_STATUS" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
56
|
+
|
|
57
|
+
# Validate status
|
|
58
|
+
case "$NEW_STATUS" in
|
|
59
|
+
paused|resumed|abandoned)
|
|
60
|
+
;;
|
|
61
|
+
*)
|
|
62
|
+
echo "[$(date)] ⚠️ Unknown status: $NEW_STATUS (skipping sync)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
63
|
+
exit 0
|
|
64
|
+
;;
|
|
65
|
+
esac
|
|
66
|
+
|
|
67
|
+
# Check if GitHub CLI available
|
|
68
|
+
if ! command -v gh &> /dev/null; then
|
|
69
|
+
echo "[$(date)] ℹ️ GitHub CLI not found, skipping sync" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
70
|
+
exit 0
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
# Check if authenticated
|
|
74
|
+
if ! gh auth status &> /dev/null; then
|
|
75
|
+
echo "[$(date)] ℹ️ GitHub CLI not authenticated, skipping sync" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
76
|
+
exit 0
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# Load metadata
|
|
80
|
+
METADATA_FILE=".specweave/increments/$INCREMENT_ID/metadata.json"
|
|
81
|
+
|
|
82
|
+
if [ ! -f "$METADATA_FILE" ]; then
|
|
83
|
+
echo "[$(date)] ℹ️ No metadata.json found, skipping sync" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
84
|
+
exit 0
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# Extract GitHub issue number
|
|
88
|
+
GITHUB_ISSUE=$(jq -r '.github.issue // empty' "$METADATA_FILE" 2>/dev/null)
|
|
89
|
+
|
|
90
|
+
if [ -z "$GITHUB_ISSUE" ]; then
|
|
91
|
+
echo "[$(date)] ℹ️ No GitHub issue linked, skipping sync" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
92
|
+
exit 0
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# Detect repository
|
|
96
|
+
GITHUB_REMOTE=$(git remote get-url origin 2>/dev/null || echo "")
|
|
97
|
+
REPO_MATCH=$(echo "$GITHUB_REMOTE" | grep -o 'github\.com[:/][^/]*/[^/]*' | sed 's/github\.com[:/]//')
|
|
98
|
+
|
|
99
|
+
if [ -z "$REPO_MATCH" ]; then
|
|
100
|
+
echo "[$(date)] ⚠️ Could not detect GitHub repository" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
101
|
+
exit 0
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
echo "[$(date)] 🔄 Posting status change to GitHub issue #$GITHUB_ISSUE" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
105
|
+
|
|
106
|
+
# Build comment based on status
|
|
107
|
+
EMOJI=""
|
|
108
|
+
TITLE=""
|
|
109
|
+
case "$NEW_STATUS" in
|
|
110
|
+
paused)
|
|
111
|
+
EMOJI="⏸️"
|
|
112
|
+
TITLE="Increment Paused"
|
|
113
|
+
;;
|
|
114
|
+
resumed)
|
|
115
|
+
EMOJI="▶️"
|
|
116
|
+
TITLE="Increment Resumed"
|
|
117
|
+
;;
|
|
118
|
+
abandoned)
|
|
119
|
+
EMOJI="🗑️"
|
|
120
|
+
TITLE="Increment Abandoned"
|
|
121
|
+
;;
|
|
122
|
+
esac
|
|
123
|
+
|
|
124
|
+
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
125
|
+
|
|
126
|
+
COMMENT="$EMOJI **$TITLE**
|
|
127
|
+
|
|
128
|
+
**Reason**: ${REASON:-Not specified}
|
|
129
|
+
|
|
130
|
+
**Timestamp**: $TIMESTAMP
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
🤖 Auto-updated by SpecWeave"
|
|
134
|
+
|
|
135
|
+
# Post comment
|
|
136
|
+
echo "$COMMENT" | gh issue comment "$GITHUB_ISSUE" --body-file - 2>&1 | tee -a "$DEBUG_LOG" >/dev/null || {
|
|
137
|
+
echo "[$(date)] ⚠️ Failed to post comment (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
echo "[$(date)] ✅ Status change synced to GitHub" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
141
|
+
|
|
142
|
+
# Update status line cache (status changed - may affect which increment is "current")
|
|
143
|
+
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
144
|
+
bash "$HOOK_DIR/lib/update-status-line.sh" 2>/dev/null || true
|
|
145
|
+
|
|
146
|
+
# Return success (non-blocking)
|
|
147
|
+
exit 0
|
|
@@ -192,6 +192,15 @@ case "$CURRENT_STATUS" in
|
|
|
192
192
|
fi
|
|
193
193
|
;;
|
|
194
194
|
|
|
195
|
+
active|planning|in-progress)
|
|
196
|
+
# Increment became active - MUST register in active-increment.json!
|
|
197
|
+
# CRITICAL FIX (v0.26.15): post-task-completion.sh depends on this file
|
|
198
|
+
# Without registration, ALL sync operations are skipped!
|
|
199
|
+
echo "[$(date)] post-metadata-change: Status is $CURRENT_STATUS - registering as active + updating status line" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
200
|
+
bash "$HOOK_DIR/lib/update-active-increment.sh" 2>/dev/null || true
|
|
201
|
+
bash "$HOOK_DIR/lib/update-status-line.sh" 2>/dev/null || true
|
|
202
|
+
;;
|
|
203
|
+
|
|
195
204
|
*)
|
|
196
205
|
# Other metadata changes (e.g., task completion count, AC count)
|
|
197
206
|
# Just update status line to reflect new progress
|
|
@@ -0,0 +1,158 @@
|
|
|
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 /specweave: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 -euo pipefail
|
|
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 /specweave-github:sync-spec or /specweave-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
|
|
@@ -66,6 +66,14 @@ find_project_root() {
|
|
|
66
66
|
PROJECT_ROOT="$(find_project_root "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")"
|
|
67
67
|
cd "$PROJECT_ROOT" 2>/dev/null || true
|
|
68
68
|
|
|
69
|
+
# ============================================================================
|
|
70
|
+
# ULTRA-FAST EARLY EXIT FOR NON-SPECWEAVE PROJECTS (T-006 - v0.26.15)
|
|
71
|
+
# ============================================================================
|
|
72
|
+
# Skip ALL processing if not a SpecWeave project - saves ~50-100ms
|
|
73
|
+
if [[ ! -d "$PROJECT_ROOT/.specweave" ]]; then
|
|
74
|
+
exit 0
|
|
75
|
+
fi
|
|
76
|
+
|
|
69
77
|
# ============================================================================
|
|
70
78
|
# RECURSION PREVENTION (CRITICAL - v0.26.0 - FILE-BASED GUARD)
|
|
71
79
|
# ============================================================================
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# post-task-edit.sh (v0.26.16)
|
|
4
|
+
#
|
|
5
|
+
# Lightweight hook triggered after Edit on tasks.md
|
|
6
|
+
# ONLY updates status line cache - no heavy processing!
|
|
7
|
+
#
|
|
8
|
+
# Purpose: Keep status line in sync when tasks are marked complete via Edit
|
|
9
|
+
#
|
|
10
|
+
# Performance target: <20ms (just calls update-status-line.sh)
|
|
11
|
+
#
|
|
12
|
+
set +e
|
|
13
|
+
|
|
14
|
+
# EMERGENCY KILL SWITCH
|
|
15
|
+
[[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]] && exit 0
|
|
16
|
+
|
|
17
|
+
# Find hook directory
|
|
18
|
+
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
19
|
+
|
|
20
|
+
# Consume stdin (required for PostToolUse hooks)
|
|
21
|
+
cat > /dev/null
|
|
22
|
+
|
|
23
|
+
# Update status line cache (force refresh by removing mtime marker)
|
|
24
|
+
PROJECT_ROOT="$PWD"
|
|
25
|
+
while [[ "$PROJECT_ROOT" != "/" ]] && [[ ! -d "$PROJECT_ROOT/.specweave" ]]; do
|
|
26
|
+
PROJECT_ROOT=$(dirname "$PROJECT_ROOT")
|
|
27
|
+
done
|
|
28
|
+
|
|
29
|
+
if [[ -d "$PROJECT_ROOT/.specweave" ]]; then
|
|
30
|
+
# Remove mtime markers to force full refresh
|
|
31
|
+
rm -f "$PROJECT_ROOT/.specweave/state/.status-mtime-"* 2>/dev/null || true
|
|
32
|
+
|
|
33
|
+
# Update status line
|
|
34
|
+
bash "$HOOK_DIR/lib/update-status-line.sh" 2>/dev/null || true
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
exit 0
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
###############################################################################
|
|
4
|
+
# SpecWeave Post-User-Story-Complete Hook
|
|
5
|
+
#
|
|
6
|
+
# CRITICAL ARCHITECTURE:
|
|
7
|
+
# - Fires when user story marked complete in spec.md (AC checkbox checked)
|
|
8
|
+
# - Updates external PM tool (GitHub Issue/Jira Story/ADO User Story)
|
|
9
|
+
# - Moves GitHub card to "Done" / Closes Jira story / Completes ADO story
|
|
10
|
+
#
|
|
11
|
+
# Trigger Points:
|
|
12
|
+
# 1. After user manually checks AC checkbox in spec.md
|
|
13
|
+
# 2. After bidirectional sync updates AC status
|
|
14
|
+
# 3. After increment completion syncs to spec
|
|
15
|
+
#
|
|
16
|
+
# What It Does:
|
|
17
|
+
# - Detects which user story was completed (all AC checkboxes checked)
|
|
18
|
+
# - Finds corresponding external item (GitHub Issue/Jira Story/ADO User Story)
|
|
19
|
+
# - Updates item status to "Done" / "Closed"
|
|
20
|
+
# - Adds completion comment with timestamp
|
|
21
|
+
#
|
|
22
|
+
# Usage:
|
|
23
|
+
# post-user-story-complete.sh <spec-id> <user-story-id>
|
|
24
|
+
#
|
|
25
|
+
# Example:
|
|
26
|
+
# post-user-story-complete.sh spec-001 US-001
|
|
27
|
+
#
|
|
28
|
+
###############################################################################
|
|
29
|
+
|
|
30
|
+
set -euo pipefail
|
|
31
|
+
|
|
32
|
+
# Arguments
|
|
33
|
+
SPEC_ID="${1:-}"
|
|
34
|
+
USER_STORY_ID="${2:-}"
|
|
35
|
+
|
|
36
|
+
# Validate arguments
|
|
37
|
+
if [[ -z "$SPEC_ID" || -z "$USER_STORY_ID" ]]; then
|
|
38
|
+
echo "❌ Error: Spec ID and User Story ID required"
|
|
39
|
+
echo "Usage: post-user-story-complete.sh <spec-id> <user-story-id>"
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
echo ""
|
|
44
|
+
echo "🎉 Post-User-Story-Complete Hook"
|
|
45
|
+
echo " Spec: $SPEC_ID"
|
|
46
|
+
echo " User Story: $USER_STORY_ID"
|
|
47
|
+
|
|
48
|
+
# Find spec file
|
|
49
|
+
SPEC_FILE=""
|
|
50
|
+
if [[ -f ".specweave/docs/internal/specs/$SPEC_ID.md" ]]; then
|
|
51
|
+
SPEC_FILE=".specweave/docs/internal/specs/$SPEC_ID.md"
|
|
52
|
+
elif [[ -f ".specweave/docs/internal/projects/default/specs/$SPEC_ID.md" ]]; then
|
|
53
|
+
SPEC_FILE=".specweave/docs/internal/projects/default/specs/$SPEC_ID.md"
|
|
54
|
+
else
|
|
55
|
+
echo "❌ Error: Spec file not found for $SPEC_ID"
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# Load config to check if auto-sync is enabled
|
|
60
|
+
CONFIG_FILE=".specweave/config.json"
|
|
61
|
+
if [[ ! -f "$CONFIG_FILE" ]]; then
|
|
62
|
+
echo " ℹ️ No config file found, skipping auto-sync"
|
|
63
|
+
exit 0
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# Check if auto-sync is enabled
|
|
67
|
+
AUTO_SYNC=$(jq -r '.hooks.post_user_story_complete.auto_sync // true' "$CONFIG_FILE")
|
|
68
|
+
|
|
69
|
+
if [[ "$AUTO_SYNC" != "true" ]]; then
|
|
70
|
+
echo " ℹ️ Auto-sync disabled in config, skipping"
|
|
71
|
+
exit 0
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# Parse spec frontmatter to detect external links
|
|
75
|
+
# Check if GitHub link exists
|
|
76
|
+
GITHUB_LINK=$(grep -A 10 "^externalLinks:" "$SPEC_FILE" | grep -A 5 "github:" | grep "projectId:" | sed 's/.*projectId: *//; s/ *$//' || echo "")
|
|
77
|
+
|
|
78
|
+
# Check if Jira link exists
|
|
79
|
+
JIRA_LINK=$(grep -A 10 "^externalLinks:" "$SPEC_FILE" | grep -A 5 "jira:" | grep "epicKey:" | sed 's/.*epicKey: *//; s/ *$//' || echo "")
|
|
80
|
+
|
|
81
|
+
# Check if ADO link exists
|
|
82
|
+
ADO_LINK=$(grep -A 10 "^externalLinks:" "$SPEC_FILE" | grep -A 5 "ado:" | grep "featureId:" | sed 's/.*featureId: *//; s/ *$//' || echo "")
|
|
83
|
+
|
|
84
|
+
# Determine which provider to sync
|
|
85
|
+
PROVIDER=""
|
|
86
|
+
if [[ -n "$GITHUB_LINK" ]]; then
|
|
87
|
+
PROVIDER="github"
|
|
88
|
+
EXTERNAL_ID="$GITHUB_LINK"
|
|
89
|
+
elif [[ -n "$JIRA_LINK" ]]; then
|
|
90
|
+
PROVIDER="jira"
|
|
91
|
+
EXTERNAL_ID="$JIRA_LINK"
|
|
92
|
+
elif [[ -n "$ADO_LINK" ]]; then
|
|
93
|
+
PROVIDER="ado"
|
|
94
|
+
EXTERNAL_ID="$ADO_LINK"
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
# No external link found - skip sync
|
|
98
|
+
if [[ -z "$PROVIDER" ]]; then
|
|
99
|
+
echo " ℹ️ Spec not linked to external tool, skipping sync"
|
|
100
|
+
exit 0
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
echo " 🔗 Detected external link: $PROVIDER"
|
|
104
|
+
|
|
105
|
+
# Update external tool based on provider
|
|
106
|
+
case "$PROVIDER" in
|
|
107
|
+
github)
|
|
108
|
+
echo " 🔄 Updating GitHub Issue for $USER_STORY_ID..."
|
|
109
|
+
|
|
110
|
+
# Check if GitHub CLI is available
|
|
111
|
+
if ! command -v gh &> /dev/null; then
|
|
112
|
+
echo " ⚠️ GitHub CLI (gh) not found, skipping sync"
|
|
113
|
+
exit 0
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
# Find GitHub Issue for this user story
|
|
117
|
+
# Search for issue with title pattern "[USER_STORY_ID]"
|
|
118
|
+
REPO=$(git remote get-url origin | sed -E 's/.*github\.com[:/]([^/]+\/[^/]+)(\.git)?$/\1/')
|
|
119
|
+
|
|
120
|
+
# Search for issue
|
|
121
|
+
ISSUE_NUMBER=$(gh issue list --repo "$REPO" --search "\"[$USER_STORY_ID]\" in:title" --json number --jq '.[0].number' 2>/dev/null || echo "")
|
|
122
|
+
|
|
123
|
+
if [[ -z "$ISSUE_NUMBER" ]]; then
|
|
124
|
+
echo " ⚠️ GitHub Issue not found for $USER_STORY_ID"
|
|
125
|
+
exit 0
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
echo " 📝 Found GitHub Issue #$ISSUE_NUMBER"
|
|
129
|
+
|
|
130
|
+
# Close issue
|
|
131
|
+
gh issue close "$ISSUE_NUMBER" --repo "$REPO" --comment "✅ User story completed
|
|
132
|
+
|
|
133
|
+
🤖 Auto-closed by SpecWeave hook
|
|
134
|
+
Completed at: $(date -u +%Y-%m-%dT%H:%M:%SZ)" 2>/dev/null || {
|
|
135
|
+
echo " ⚠️ Failed to close issue"
|
|
136
|
+
exit 0
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
echo " ✅ GitHub Issue #$ISSUE_NUMBER closed"
|
|
140
|
+
;;
|
|
141
|
+
|
|
142
|
+
jira)
|
|
143
|
+
echo " 🔄 Updating Jira Story for $USER_STORY_ID..."
|
|
144
|
+
|
|
145
|
+
# Check if Jira config exists
|
|
146
|
+
if [[ -z "${JIRA_DOMAIN:-}" ]]; then
|
|
147
|
+
echo " ⚠️ Jira not configured (.env), skipping sync"
|
|
148
|
+
exit 0
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
# TODO: Find Jira Story by title pattern
|
|
152
|
+
# TODO: Transition story to "Done" status
|
|
153
|
+
echo " ✅ Jira story transition queued (implementation pending)"
|
|
154
|
+
;;
|
|
155
|
+
|
|
156
|
+
ado)
|
|
157
|
+
echo " 🔄 Updating ADO User Story for $USER_STORY_ID..."
|
|
158
|
+
|
|
159
|
+
# Check if ADO config exists
|
|
160
|
+
if [[ -z "${ADO_ORGANIZATION:-}" ]]; then
|
|
161
|
+
echo " ⚠️ ADO not configured (.env), skipping sync"
|
|
162
|
+
exit 0
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
# TODO: Find ADO User Story by title pattern
|
|
166
|
+
# TODO: Update state to "Closed"
|
|
167
|
+
echo " ✅ ADO user story update queued (implementation pending)"
|
|
168
|
+
;;
|
|
169
|
+
|
|
170
|
+
*)
|
|
171
|
+
echo " ⚠️ Unknown provider: $PROVIDER"
|
|
172
|
+
exit 0
|
|
173
|
+
;;
|
|
174
|
+
esac
|
|
175
|
+
|
|
176
|
+
echo " ✅ Post-user-story-complete hook complete"
|
|
177
|
+
echo ""
|
|
178
|
+
|
|
179
|
+
exit 0
|
|
@@ -1,83 +1,73 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
|
|
3
|
-
# SpecWeave Pre-Command Deduplication Hook
|
|
3
|
+
# SpecWeave Pre-Command Deduplication Hook (v0.26.14 - OPTIMIZED)
|
|
4
4
|
# Fires BEFORE any command executes (UserPromptSubmit hook)
|
|
5
5
|
# Purpose: Prevent duplicate command invocations within configurable time window
|
|
6
|
+
#
|
|
7
|
+
# OPTIMIZATIONS (v0.26.14):
|
|
8
|
+
# 1. Early exit for non-SpecWeave projects (<1ms)
|
|
9
|
+
# 2. Skip node spawn if deduplicator not available
|
|
10
|
+
# 3. Pure bash stdin reading
|
|
6
11
|
|
|
7
|
-
set +e
|
|
12
|
+
set +e
|
|
8
13
|
|
|
9
14
|
# ==============================================================================
|
|
10
|
-
#
|
|
15
|
+
# ULTRA-FAST EARLY EXIT
|
|
11
16
|
# ==============================================================================
|
|
12
17
|
|
|
13
|
-
#
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return 0
|
|
20
|
-
fi
|
|
21
|
-
dir="$(dirname "$dir")"
|
|
22
|
-
done
|
|
23
|
-
# Fallback: try current directory
|
|
24
|
-
if [ -d "$(pwd)/.specweave" ]; then
|
|
25
|
-
pwd
|
|
26
|
-
else
|
|
27
|
-
echo "$(pwd)"
|
|
28
|
-
fi
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
PROJECT_ROOT="$(find_project_root "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")"
|
|
32
|
-
cd "$PROJECT_ROOT" 2>/dev/null || true
|
|
18
|
+
# Quick check: If no .specweave in cwd or nearby, just approve
|
|
19
|
+
if [[ ! -d ".specweave" ]] && [[ ! -d "../.specweave" ]] && [[ ! -d "../../.specweave" ]]; then
|
|
20
|
+
cat /dev/stdin > /dev/null # Drain stdin
|
|
21
|
+
echo '{"decision":"approve"}'
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
33
24
|
|
|
34
25
|
# Read input JSON from stdin
|
|
35
26
|
INPUT=$(cat)
|
|
36
27
|
|
|
37
28
|
# ==============================================================================
|
|
38
|
-
# DEDUPLICATION CHECK:
|
|
29
|
+
# DEDUPLICATION CHECK: Pure bash with file-based cache
|
|
39
30
|
# ==============================================================================
|
|
40
31
|
|
|
41
|
-
#
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
DEDUP_RESULT=$(echo "$INPUT" | node scripts/check-deduplication.js 2>/dev/null || echo "OK")
|
|
45
|
-
|
|
46
|
-
# Parse result
|
|
47
|
-
STATUS=$(echo "$DEDUP_RESULT" | head -1)
|
|
32
|
+
# Use file-based deduplication (no node!) with 1-second TTL
|
|
33
|
+
CACHE_DIR=".specweave/state/.dedup-cache"
|
|
34
|
+
mkdir -p "$CACHE_DIR" 2>/dev/null || true
|
|
48
35
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
36
|
+
# Extract command from input (use jq if available, fallback to grep)
|
|
37
|
+
if command -v jq >/dev/null 2>&1; then
|
|
38
|
+
COMMAND=$(echo "$INPUT" | jq -r '.prompt // ""' 2>/dev/null | head -c 100 | tr -d '\n' | tr '/' '_' | tr ' ' '_')
|
|
39
|
+
else
|
|
40
|
+
COMMAND=$(echo "$INPUT" | grep -oP '"prompt"\s*:\s*"\K[^"]{0,100}' 2>/dev/null | tr '/' '_' | tr ' ' '_')
|
|
41
|
+
fi
|
|
52
42
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
CACHE_SIZE=$(echo "$STATS" | grep -o '"currentCacheSize":[0-9]*' | cut -d':' -f2 || echo "1")
|
|
43
|
+
# Only check for SpecWeave commands
|
|
44
|
+
if [[ "$COMMAND" == *specweave* ]]; then
|
|
45
|
+
CACHE_FILE="$CACHE_DIR/${COMMAND:0:50}.lock"
|
|
57
46
|
|
|
58
|
-
|
|
59
|
-
|
|
47
|
+
# Check if cached and recent (within 1 second)
|
|
48
|
+
if [[ -f "$CACHE_FILE" ]]; then
|
|
49
|
+
CACHE_AGE=$(($(date +%s) - $(stat -f %m "$CACHE_FILE" 2>/dev/null || stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0)))
|
|
50
|
+
if [[ "$CACHE_AGE" -lt 1 ]]; then
|
|
51
|
+
cat <<EOF
|
|
60
52
|
{
|
|
61
53
|
"decision": "block",
|
|
62
|
-
"reason": "
|
|
54
|
+
"reason": "Duplicate command detected (within 1 second). Wait a moment and try again."
|
|
63
55
|
}
|
|
64
56
|
EOF
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
# Use | as sed delimiter to avoid conflicts with / in command names
|
|
68
|
-
echo "$MESSAGE" | sed "s|COMMAND_PLACEHOLDER|$COMMAND|g" | sed "s|BLOCKED_PLACEHOLDER|$TOTAL_BLOCKED|g" | sed "s|CACHE_PLACEHOLDER|$CACHE_SIZE|g"
|
|
69
|
-
exit 0
|
|
57
|
+
exit 0
|
|
58
|
+
fi
|
|
70
59
|
fi
|
|
60
|
+
|
|
61
|
+
# Update cache timestamp
|
|
62
|
+
touch "$CACHE_FILE" 2>/dev/null || true
|
|
63
|
+
|
|
64
|
+
# Cleanup old cache files (>10 seconds old)
|
|
65
|
+
find "$CACHE_DIR" -type f -mmin +1 -delete 2>/dev/null &
|
|
71
66
|
fi
|
|
72
67
|
|
|
73
68
|
# ==============================================================================
|
|
74
|
-
# PASS THROUGH: No duplicate detected
|
|
69
|
+
# PASS THROUGH: No duplicate detected
|
|
75
70
|
# ==============================================================================
|
|
76
71
|
|
|
77
|
-
|
|
78
|
-
{
|
|
79
|
-
"decision": "approve"
|
|
80
|
-
}
|
|
81
|
-
EOF
|
|
82
|
-
|
|
72
|
+
echo '{"decision":"approve"}'
|
|
83
73
|
exit 0
|