specweave 0.32.0 → 0.32.2
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 +176 -2
- package/README.md +22 -0
- package/bin/specweave.js +18 -1
- package/dist/src/cli/commands/cache.d.ts +17 -0
- package/dist/src/cli/commands/cache.d.ts.map +1 -0
- package/dist/src/cli/commands/cache.js +126 -0
- package/dist/src/cli/commands/cache.js.map +1 -0
- package/dist/src/cli/commands/init.js +1 -1
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/plan/increment-detector.js +2 -2
- package/dist/src/cli/commands/plan/increment-detector.js.map +1 -1
- package/dist/src/cli/commands/sync-spec-commits.js +1 -1
- package/dist/src/cli/commands/sync-spec-commits.js.map +1 -1
- package/dist/src/cli/commands/sync-specs.js +2 -2
- package/dist/src/cli/commands/sync-specs.js.map +1 -1
- package/dist/src/cli/helpers/github/increment-profile-selector.js +1 -1
- package/dist/src/cli/helpers/github/increment-profile-selector.js.map +1 -1
- package/dist/src/cli/workers/living-docs-worker.js +66 -1
- package/dist/src/cli/workers/living-docs-worker.js.map +1 -1
- package/dist/src/config/types.d.ts +203 -1208
- package/dist/src/config/types.d.ts.map +1 -1
- package/dist/src/core/discrepancy/increment-generator.d.ts.map +1 -1
- package/dist/src/core/discrepancy/increment-generator.js +5 -2
- package/dist/src/core/discrepancy/increment-generator.js.map +1 -1
- package/dist/src/core/increment/duplicate-detector.js +2 -2
- package/dist/src/core/increment/duplicate-detector.js.map +1 -1
- package/dist/src/core/increment/increment-archiver.d.ts +24 -0
- package/dist/src/core/increment/increment-archiver.d.ts.map +1 -1
- package/dist/src/core/increment/increment-archiver.js +59 -2
- package/dist/src/core/increment/increment-archiver.js.map +1 -1
- package/dist/src/core/increment/increment-status.js +2 -2
- package/dist/src/core/increment/increment-status.js.map +1 -1
- package/dist/src/core/increment/increment-utils.d.ts +85 -0
- package/dist/src/core/increment/increment-utils.d.ts.map +1 -1
- package/dist/src/core/increment/increment-utils.js +102 -4
- package/dist/src/core/increment/increment-utils.js.map +1 -1
- package/dist/src/core/increment/metadata-validator.js +1 -1
- package/dist/src/core/increment/metadata-validator.js.map +1 -1
- package/dist/src/core/living-docs/feature-id-manager.js +1 -1
- package/dist/src/core/living-docs/feature-id-manager.js.map +1 -1
- package/dist/src/core/living-docs/hierarchy-mapper.js +3 -3
- package/dist/src/core/living-docs/hierarchy-mapper.js.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts +18 -0
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js +247 -0
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.d.ts +15 -0
- package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js +138 -0
- package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.d.ts +24 -0
- package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.js +198 -0
- package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.d.ts +17 -0
- package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.js +241 -0
- package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/index.d.ts +28 -0
- package/dist/src/core/living-docs/intelligent-analyzer/index.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/index.js +197 -0
- package/dist/src/core/living-docs/intelligent-analyzer/index.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts +18 -0
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js +154 -0
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.d.ts +42 -0
- package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.js +343 -0
- package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts +146 -0
- package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/types.js +7 -0
- package/dist/src/core/living-docs/intelligent-analyzer/types.js.map +1 -0
- package/dist/src/core/living-docs/living-docs-sync.d.ts +5 -0
- package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.js +36 -2
- package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
- package/dist/src/core/sync/spec-increment-mapper.js +3 -3
- package/dist/src/core/sync/spec-increment-mapper.js.map +1 -1
- package/dist/src/importers/item-converter.d.ts +25 -0
- package/dist/src/importers/item-converter.d.ts.map +1 -1
- package/dist/src/importers/item-converter.js +135 -5
- package/dist/src/importers/item-converter.js.map +1 -1
- package/dist/src/init/architecture/types.d.ts +33 -140
- package/dist/src/init/architecture/types.d.ts.map +1 -1
- package/dist/src/init/compliance/types.d.ts +30 -27
- package/dist/src/init/compliance/types.d.ts.map +1 -1
- package/dist/src/init/repo/types.d.ts +11 -34
- package/dist/src/init/repo/types.d.ts.map +1 -1
- package/dist/src/init/research/src/config/types.d.ts +15 -82
- package/dist/src/init/research/src/config/types.d.ts.map +1 -1
- package/dist/src/init/research/types.d.ts +38 -93
- package/dist/src/init/research/types.d.ts.map +1 -1
- package/dist/src/init/team/types.d.ts +4 -42
- package/dist/src/init/team/types.d.ts.map +1 -1
- package/dist/src/types/dashboard-cache.d.ts +181 -0
- package/dist/src/types/dashboard-cache.d.ts.map +1 -0
- package/dist/src/types/dashboard-cache.js +65 -0
- package/dist/src/types/dashboard-cache.js.map +1 -0
- package/dist/src/utils/docs-validator.d.ts +131 -0
- package/dist/src/utils/docs-validator.d.ts.map +1 -0
- package/dist/src/utils/docs-validator.js +529 -0
- package/dist/src/utils/docs-validator.js.map +1 -0
- package/dist/src/utils/feature-id-collision.js +1 -1
- package/dist/src/utils/feature-id-collision.js.map +1 -1
- package/dist/src/utils/html-to-mdx.d.ts +1 -0
- package/dist/src/utils/html-to-mdx.d.ts.map +1 -1
- package/dist/src/utils/html-to-mdx.js +43 -5
- package/dist/src/utils/html-to-mdx.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/agents/pm/AGENT.md +10 -7
- package/plugins/specweave/commands/specweave-archive-features.md +5 -7
- package/plugins/specweave/commands/specweave-archive.md +2 -1
- package/plugins/specweave/commands/specweave-do.md +35 -1
- package/plugins/specweave/commands/specweave-done.md +96 -0
- package/plugins/specweave/commands/specweave-import-external.md +45 -18
- package/plugins/specweave/commands/specweave-increment.md +331 -33
- package/plugins/specweave/commands/specweave-jobs.md +2 -2
- package/plugins/specweave/commands/specweave-progress.md +4 -4
- package/plugins/specweave/commands/specweave-restore-feature.md +5 -4
- package/plugins/specweave/commands/specweave-sync-docs.md +1 -1
- package/plugins/specweave/commands/specweave-sync-specs.md +216 -322
- package/plugins/specweave/commands/specweave-validate-features.md +13 -8
- package/plugins/specweave/hooks/docs-changed.sh.backup +79 -0
- package/plugins/specweave/hooks/hooks.json +33 -4
- package/plugins/specweave/hooks/human-input-required.sh.backup +75 -0
- package/plugins/specweave/hooks/lib/common-setup.sh +375 -0
- package/plugins/specweave/hooks/lib/crash-prevention.sh +336 -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-spec-update.sh.backup +158 -0
- package/plugins/specweave/hooks/post-task-completion.sh +4 -23
- package/plugins/specweave/hooks/post-user-story-complete.sh.backup +179 -0
- package/plugins/specweave/hooks/pre-command-deduplication.sh +1 -6
- 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 +8 -37
- package/plugins/specweave/hooks/pre-task-completion.sh.backup +194 -0
- package/plugins/specweave/hooks/pre-tool-use.sh +2 -11
- package/plugins/specweave/hooks/pre-tool-use.sh.backup +133 -0
- package/plugins/specweave/hooks/universal/dispatcher.mjs +135 -42
- package/plugins/specweave/hooks/universal/fail-fast-wrapper.sh +183 -0
- package/plugins/specweave/hooks/user-prompt-submit.sh +140 -38
- package/plugins/specweave/hooks/user-prompt-submit.sh.backup +386 -0
- package/plugins/specweave/hooks/v2/dispatchers/post-tool-use.sh +12 -0
- package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +89 -0
- package/plugins/specweave/hooks/v2/guards/bash-file-guard.sh +211 -0
- package/plugins/specweave/hooks/v2/guards/bash-file-guard.test.sh +163 -0
- package/plugins/specweave/hooks/v2/guards/completion-guard.sh +26 -28
- package/plugins/specweave/hooks/v2/guards/features-folder-guard.sh +50 -0
- package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js +2 -2
- package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js.map +1 -1
- package/plugins/specweave/scripts/README.md +166 -0
- package/plugins/specweave/scripts/cleanup-state.sh +142 -0
- package/plugins/specweave/scripts/force-kill.sh +142 -0
- package/plugins/specweave/scripts/jobs.js +171 -0
- package/plugins/specweave/scripts/progress.js +170 -0
- package/plugins/specweave/scripts/read-costs.sh +132 -0
- package/plugins/specweave/scripts/read-jobs.sh +324 -0
- package/plugins/specweave/scripts/read-progress.sh +185 -0
- package/plugins/specweave/scripts/read-status.sh +146 -0
- package/plugins/specweave/scripts/read-workflow.sh +173 -0
- package/plugins/specweave/scripts/rebuild-dashboard-cache.sh +327 -0
- package/plugins/specweave/scripts/session-watchdog.sh +192 -0
- package/plugins/specweave/scripts/status.js +154 -0
- package/plugins/specweave/scripts/update-dashboard-cache.sh +281 -0
- package/plugins/specweave/skills/increment-planner/SKILL.md +333 -24
- package/plugins/specweave/skills/increment-planner/templates/spec-multi-project.md +17 -9
- package/plugins/specweave/skills/increment-planner/templates/spec-single-project.md +6 -2
- package/plugins/specweave/skills/instant-status/SKILL.md +70 -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/enhanced-ado-sync.js +170 -0
- package/plugins/specweave-docs/commands/build.md +32 -4
- package/plugins/specweave-docs/commands/preview.md +43 -1
- package/plugins/specweave-docs/commands/validate.md +250 -0
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +1262 -0
- package/plugins/specweave-github/hooks/post-task-completion.sh.backup +258 -0
- package/plugins/specweave-github/lib/enhanced-github-sync.js +220 -0
- package/plugins/specweave-jira/hooks/post-task-completion.sh.backup +172 -0
- package/plugins/specweave-jira/lib/enhanced-jira-sync.js +134 -0
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +1254 -0
- package/plugins/specweave-release/hooks/post-task-completion.sh.backup +110 -0
- package/plugins/specweave/hooks/post-edit-spec.sh +0 -265
- package/plugins/specweave/hooks/post-write-spec.sh +0 -267
- package/plugins/specweave/hooks/pre-edit-spec.sh +0 -151
- package/plugins/specweave/hooks/pre-write-spec.sh +0 -151
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Post-Increment-Completion Hook - DORA Metrics Tracking
|
|
3
|
+
#
|
|
4
|
+
# Fires after: /specweave:done completes
|
|
5
|
+
# Purpose: Automatically track DORA metrics and update living docs dashboard
|
|
6
|
+
#
|
|
7
|
+
# Integration: plugins/specweave-release/hooks/hooks.json
|
|
8
|
+
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
# Constants
|
|
12
|
+
SPECWEAVE_ROOT="${SPECWEAVE_ROOT:-$(pwd)}"
|
|
13
|
+
METRICS_DIR="${SPECWEAVE_ROOT}/.specweave/metrics"
|
|
14
|
+
HISTORY_FILE="${METRICS_DIR}/dora-history.jsonl"
|
|
15
|
+
DASHBOARD_FILE="${SPECWEAVE_ROOT}/.specweave/docs/internal/delivery/dora-dashboard.md"
|
|
16
|
+
DORA_CALCULATOR="${SPECWEAVE_ROOT}/dist/src/metrics/dora-calculator.js"
|
|
17
|
+
LATEST_FILE="${METRICS_DIR}/dora-latest.json"
|
|
18
|
+
|
|
19
|
+
# Logging
|
|
20
|
+
LOG_FILE="${SPECWEAVE_ROOT}/.specweave/logs/dora-tracking.log"
|
|
21
|
+
mkdir -p "$(dirname "$LOG_FILE")"
|
|
22
|
+
|
|
23
|
+
log() {
|
|
24
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
log "🎯 Post-Increment-Completion Hook Triggered"
|
|
28
|
+
|
|
29
|
+
# Check if DORA calculator exists
|
|
30
|
+
if [[ ! -f "$DORA_CALCULATOR" ]]; then
|
|
31
|
+
log "⚠️ DORA calculator not found at $DORA_CALCULATOR"
|
|
32
|
+
log " Run: npm run build"
|
|
33
|
+
exit 0 # Non-blocking
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# Check if GitHub token is available
|
|
37
|
+
if [[ -z "${GITHUB_TOKEN:-}" ]]; then
|
|
38
|
+
log "⚠️ GITHUB_TOKEN not set. DORA metrics require GitHub API access."
|
|
39
|
+
log " Set GITHUB_TOKEN in environment or .env file"
|
|
40
|
+
exit 0 # Non-blocking
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# Step 1: Calculate DORA metrics
|
|
44
|
+
log "📊 Calculating DORA metrics..."
|
|
45
|
+
if ! node "$DORA_CALCULATOR"; then
|
|
46
|
+
log "❌ Failed to calculate DORA metrics"
|
|
47
|
+
exit 0 # Non-blocking
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# Step 2: Append to history (JSONL format)
|
|
51
|
+
log "💾 Appending metrics to history..."
|
|
52
|
+
mkdir -p "$METRICS_DIR"
|
|
53
|
+
|
|
54
|
+
if [[ -f "$LATEST_FILE" ]]; then
|
|
55
|
+
cat "$LATEST_FILE" >> "$HISTORY_FILE"
|
|
56
|
+
log " ✓ Appended to $HISTORY_FILE"
|
|
57
|
+
else
|
|
58
|
+
log "⚠️ Latest metrics file not found: $LATEST_FILE"
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# Step 3: Update living docs dashboard
|
|
62
|
+
log "📝 Updating DORA dashboard..."
|
|
63
|
+
DASHBOARD_GENERATOR="${SPECWEAVE_ROOT}/dist/plugins/specweave-release/lib/dashboard-generator.js"
|
|
64
|
+
|
|
65
|
+
if [[ -f "$DASHBOARD_GENERATOR" ]]; then
|
|
66
|
+
if node "$DASHBOARD_GENERATOR"; then
|
|
67
|
+
log " ✓ Dashboard updated: $DASHBOARD_FILE"
|
|
68
|
+
else
|
|
69
|
+
log "⚠️ Failed to update dashboard"
|
|
70
|
+
fi
|
|
71
|
+
else
|
|
72
|
+
log "⚠️ Dashboard generator not found: $DASHBOARD_GENERATOR"
|
|
73
|
+
log " Manual dashboard update required"
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
# Step 4: Check for degradation (optional)
|
|
77
|
+
log "🔍 Checking for metric degradation..."
|
|
78
|
+
|
|
79
|
+
# Calculate 30-day average and compare with current
|
|
80
|
+
# (This would be implemented in a TypeScript utility)
|
|
81
|
+
# For now, we'll just log a reminder
|
|
82
|
+
log " ℹ️ Degradation detection: Manual review recommended"
|
|
83
|
+
log " See: $DASHBOARD_FILE for trends"
|
|
84
|
+
|
|
85
|
+
# Step 5: Update main DORA metrics doc
|
|
86
|
+
DORA_METRICS_DOC="${SPECWEAVE_ROOT}/.specweave/docs/internal/delivery/dora-metrics.md"
|
|
87
|
+
if [[ -f "$DORA_METRICS_DOC" && -f "$LATEST_FILE" ]]; then
|
|
88
|
+
log "📄 Updating dora-metrics.md with latest values..."
|
|
89
|
+
|
|
90
|
+
# Extract current values from latest metrics
|
|
91
|
+
# (This is a simplified version - production would use jq for proper parsing)
|
|
92
|
+
TIMESTAMP=$(date '+%Y-%m-%d %H:%M UTC')
|
|
93
|
+
|
|
94
|
+
# Update "Last Calculated" timestamp
|
|
95
|
+
if command -v sed &> /dev/null; then
|
|
96
|
+
sed -i.bak "s/Last Calculated: .*/Last Calculated: $TIMESTAMP/" "$DORA_METRICS_DOC" 2>/dev/null || true
|
|
97
|
+
rm -f "${DORA_METRICS_DOC}.bak" 2>/dev/null || true
|
|
98
|
+
log " ✓ Updated timestamp in dora-metrics.md"
|
|
99
|
+
fi
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
log "✅ DORA metrics tracking complete!"
|
|
103
|
+
log ""
|
|
104
|
+
log "📊 Next steps:"
|
|
105
|
+
log " 1. Review dashboard: $DASHBOARD_FILE"
|
|
106
|
+
log " 2. Check trends: Are metrics improving?"
|
|
107
|
+
log " 3. Take action: Address any degradation"
|
|
108
|
+
log ""
|
|
109
|
+
|
|
110
|
+
exit 0
|
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
#
|
|
3
|
-
# Post-Edit Hook: Update Status Line After spec.md or tasks.md Edits
|
|
4
|
-
#
|
|
5
|
-
# Triggers: After Edit tool modifies spec.md (AC updates) or tasks.md (task completion)
|
|
6
|
-
# Action: Updates status line cache to reflect latest AC/task progress
|
|
7
|
-
#
|
|
8
|
-
# This ensures status line stays in sync when ACs are marked complete via Edit tool
|
|
9
|
-
# (not just TodoWrite, which only tracks internal todo lists)
|
|
10
|
-
#
|
|
11
|
-
# EMERGENCY FIXES (v0.24.3):
|
|
12
|
-
# - Kill switch: Set SPECWEAVE_DISABLE_HOOKS=1 to disable ALL hooks
|
|
13
|
-
# - Circuit breaker: Auto-disable after 3 consecutive failures
|
|
14
|
-
# - File locking: Prevent concurrent executions (max 1 at a time)
|
|
15
|
-
# - Aggressive debouncing: Increased from 1s to 5s
|
|
16
|
-
# - Complete error isolation: Never let errors reach Claude Code
|
|
17
|
-
#
|
|
18
|
-
# TIER 1 IMPROVEMENTS (v0.24.2):
|
|
19
|
-
# - Debouncing: Skip if updated less than 1 second ago (90% overhead reduction)
|
|
20
|
-
# - File mtime detection: Check recently modified spec.md/tasks.md as fallback
|
|
21
|
-
# - Non-blocking: Run update-status-line.sh in background
|
|
22
|
-
# - Smart detection: Only update if spec/tasks files actually changed
|
|
23
|
-
#
|
|
24
|
-
# Previous fix (v0.24.1): Enhanced file detection for increment completion
|
|
25
|
-
# - Detects edits via TOOL_USE_CONTENT, TOOL_RESULT, and argument parsing
|
|
26
|
-
# - Always updates status line for ANY spec.md/tasks.md edit in increments folder
|
|
27
|
-
|
|
28
|
-
# CRITICAL: Remove set -e to prevent hook errors from crashing Claude Code
|
|
29
|
-
set +e
|
|
30
|
-
|
|
31
|
-
# EMERGENCY KILL SWITCH: Disable all hooks if env variable set
|
|
32
|
-
if [[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]]; then
|
|
33
|
-
exit 0
|
|
34
|
-
fi
|
|
35
|
-
|
|
36
|
-
# Find project root
|
|
37
|
-
find_project_root() {
|
|
38
|
-
local dir="$PWD"
|
|
39
|
-
while [[ "$dir" != "/" ]]; do
|
|
40
|
-
if [[ -d "$dir/.specweave" ]]; then
|
|
41
|
-
echo "$dir"
|
|
42
|
-
return 0
|
|
43
|
-
fi
|
|
44
|
-
dir=$(dirname "$dir")
|
|
45
|
-
done
|
|
46
|
-
echo "$PWD"
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
PROJECT_ROOT=$(find_project_root)
|
|
50
|
-
LOGS_DIR="$PROJECT_ROOT/.specweave/logs"
|
|
51
|
-
DEBUG_LOG="$LOGS_DIR/hooks-debug.log"
|
|
52
|
-
|
|
53
|
-
# Ensure state and logs directories exist
|
|
54
|
-
mkdir -p "$PROJECT_ROOT/.specweave/state" "$LOGS_DIR" 2>/dev/null || true
|
|
55
|
-
|
|
56
|
-
# EMERGENCY CIRCUIT BREAKER: Track consecutive failures
|
|
57
|
-
CIRCUIT_BREAKER_FILE="$PROJECT_ROOT/.specweave/state/.hook-circuit-breaker"
|
|
58
|
-
CIRCUIT_BREAKER_THRESHOLD=3
|
|
59
|
-
|
|
60
|
-
if [[ -f "$CIRCUIT_BREAKER_FILE" ]]; then
|
|
61
|
-
FAILURE_COUNT=$(cat "$CIRCUIT_BREAKER_FILE" 2>/dev/null || echo 0)
|
|
62
|
-
if (( FAILURE_COUNT >= CIRCUIT_BREAKER_THRESHOLD )); then
|
|
63
|
-
echo "[$(date)] CIRCUIT BREAKER OPEN: Hooks disabled after $FAILURE_COUNT failures. Run: rm $CIRCUIT_BREAKER_FILE" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
64
|
-
exit 0
|
|
65
|
-
fi
|
|
66
|
-
fi
|
|
67
|
-
|
|
68
|
-
# EMERGENCY FILE LOCK: Prevent concurrent executions
|
|
69
|
-
LOCK_FILE="$PROJECT_ROOT/.specweave/state/.hook-post-edit.lock"
|
|
70
|
-
LOCK_TIMEOUT=5 # seconds
|
|
71
|
-
|
|
72
|
-
# Try to acquire lock with timeout
|
|
73
|
-
LOCK_ACQUIRED=false
|
|
74
|
-
for i in {1..5}; do
|
|
75
|
-
if mkdir "$LOCK_FILE" 2>/dev/null; then
|
|
76
|
-
LOCK_ACQUIRED=true
|
|
77
|
-
trap 'rmdir "$LOCK_FILE" 2>/dev/null || true' EXIT
|
|
78
|
-
break
|
|
79
|
-
fi
|
|
80
|
-
|
|
81
|
-
# Check if lock is stale (older than LOCK_TIMEOUT seconds)
|
|
82
|
-
if [[ -d "$LOCK_FILE" ]]; then
|
|
83
|
-
LOCK_AGE=$(($(date +%s) - $(stat -f "%m" "$LOCK_FILE" 2>/dev/null || echo 0)))
|
|
84
|
-
if (( LOCK_AGE > LOCK_TIMEOUT )); then
|
|
85
|
-
rmdir "$LOCK_FILE" 2>/dev/null || true
|
|
86
|
-
continue
|
|
87
|
-
fi
|
|
88
|
-
fi
|
|
89
|
-
|
|
90
|
-
sleep 0.2
|
|
91
|
-
done
|
|
92
|
-
|
|
93
|
-
if [[ "$LOCK_ACQUIRED" == "false" ]]; then
|
|
94
|
-
echo "[$(date)] post-edit-spec: Could not acquire lock, skipping" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
95
|
-
exit 0
|
|
96
|
-
fi
|
|
97
|
-
|
|
98
|
-
# Log rotation: Keep debug log under 100KB
|
|
99
|
-
if [[ -f "$DEBUG_LOG" ]] && [[ $(wc -c < "$DEBUG_LOG" 2>/dev/null || echo 0) -gt 102400 ]]; then
|
|
100
|
-
tail -100 "$DEBUG_LOG" > "$DEBUG_LOG.tmp" 2>/dev/null || true
|
|
101
|
-
mv "$DEBUG_LOG.tmp" "$DEBUG_LOG" 2>/dev/null || true
|
|
102
|
-
echo "[$(date)] Log rotated" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
103
|
-
fi
|
|
104
|
-
|
|
105
|
-
# ============================================================================
|
|
106
|
-
# TIER 1 FIX: Debouncing (Prevent Redundant Updates)
|
|
107
|
-
# ============================================================================
|
|
108
|
-
# Skip update if we updated less than 5 seconds ago (INCREASED FROM 1s)
|
|
109
|
-
# This handles rapid consecutive edits (e.g., 10 tasks marked complete quickly)
|
|
110
|
-
LAST_UPDATE_FILE="$PROJECT_ROOT/.specweave/state/.last-status-update"
|
|
111
|
-
DEBOUNCE_SECONDS=5
|
|
112
|
-
|
|
113
|
-
if [[ -f "$LAST_UPDATE_FILE" ]]; then
|
|
114
|
-
LAST_UPDATE=$(cat "$LAST_UPDATE_FILE" 2>/dev/null || echo 0)
|
|
115
|
-
NOW=$(date +%s)
|
|
116
|
-
TIME_SINCE_UPDATE=$((NOW - LAST_UPDATE))
|
|
117
|
-
|
|
118
|
-
if (( TIME_SINCE_UPDATE < DEBOUNCE_SECONDS )); then
|
|
119
|
-
echo "[$(date)] post-edit-spec: Debounced (${TIME_SINCE_UPDATE}s since last update)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
120
|
-
exit 0 # Skip this update
|
|
121
|
-
fi
|
|
122
|
-
fi
|
|
123
|
-
|
|
124
|
-
# ============================================================================
|
|
125
|
-
# TIER 2: Check for PreToolUse Signal (Primary Detection Method)
|
|
126
|
-
# ============================================================================
|
|
127
|
-
PENDING_FILE="$PROJECT_ROOT/.specweave/state/.pending-status-update"
|
|
128
|
-
METRICS_FILE="$PROJECT_ROOT/.specweave/state/hook-metrics.jsonl"
|
|
129
|
-
EDITED_FILE=""
|
|
130
|
-
DETECTION_METHOD="none"
|
|
131
|
-
|
|
132
|
-
# First, check if PreToolUse hook left a signal
|
|
133
|
-
if [[ -f "$PENDING_FILE" ]]; then
|
|
134
|
-
EDITED_FILE=$(cat "$PENDING_FILE" 2>/dev/null || echo "")
|
|
135
|
-
# Delete pending file immediately (consume signal)
|
|
136
|
-
rm "$PENDING_FILE" 2>/dev/null || true
|
|
137
|
-
|
|
138
|
-
if [[ -n "$EDITED_FILE" ]]; then
|
|
139
|
-
DETECTION_METHOD="pretooluse"
|
|
140
|
-
echo "[$(date)] post-edit-spec: File from PreToolUse signal: $EDITED_FILE" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
141
|
-
|
|
142
|
-
# Record Tier 2 success metric
|
|
143
|
-
TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
144
|
-
echo "{\"timestamp\":\"$TIMESTAMP\",\"hook\":\"post-edit-spec\",\"event\":\"tier2_success\",\"method\":\"pretooluse\"}" >> "$METRICS_FILE" 2>/dev/null || true
|
|
145
|
-
fi
|
|
146
|
-
fi
|
|
147
|
-
|
|
148
|
-
# ============================================================================
|
|
149
|
-
# TIER 1 FALLBACK: Environment Variable Detection
|
|
150
|
-
# ============================================================================
|
|
151
|
-
# If PreToolUse didn't provide signal, fall back to Tier 1 methods
|
|
152
|
-
if [[ -z "$EDITED_FILE" ]]; then
|
|
153
|
-
# Method 1: TOOL_USE_CONTENT environment variable
|
|
154
|
-
if [[ -n "${TOOL_USE_CONTENT:-}" ]]; then
|
|
155
|
-
EDITED_FILE="$TOOL_USE_CONTENT"
|
|
156
|
-
DETECTION_METHOD="env_content"
|
|
157
|
-
fi
|
|
158
|
-
|
|
159
|
-
# Method 2: TOOL_RESULT environment variable
|
|
160
|
-
if [[ -z "$EDITED_FILE" ]] && [[ -n "${TOOL_RESULT:-}" ]]; then
|
|
161
|
-
EDITED_FILE=$(echo "$TOOL_RESULT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/' || echo "")
|
|
162
|
-
DETECTION_METHOD="env_result"
|
|
163
|
-
fi
|
|
164
|
-
|
|
165
|
-
# Method 3: TOOL_USE_ARGS
|
|
166
|
-
if [[ -z "$EDITED_FILE" ]] && [[ -n "${TOOL_USE_ARGS:-}" ]]; then
|
|
167
|
-
EDITED_FILE=$(echo "$TOOL_USE_ARGS" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/' || echo "")
|
|
168
|
-
DETECTION_METHOD="env_args"
|
|
169
|
-
fi
|
|
170
|
-
|
|
171
|
-
# Log env var detection (for metrics)
|
|
172
|
-
if [[ -n "$EDITED_FILE" ]]; then
|
|
173
|
-
echo "[$(date)] post-edit-spec: File from env vars ($DETECTION_METHOD): $EDITED_FILE" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
174
|
-
fi
|
|
175
|
-
fi
|
|
176
|
-
|
|
177
|
-
# Check if we detected a spec.md or tasks.md edit in increments folder
|
|
178
|
-
SHOULD_UPDATE=false
|
|
179
|
-
|
|
180
|
-
if [[ -n "$EDITED_FILE" ]]; then
|
|
181
|
-
# Check if the file is spec.md or tasks.md
|
|
182
|
-
if [[ "$EDITED_FILE" == *"/spec.md" ]] || [[ "$EDITED_FILE" == *"/tasks.md" ]]; then
|
|
183
|
-
# Check if it's in an increment folder
|
|
184
|
-
if [[ "$EDITED_FILE" == *"/.specweave/increments/"* ]]; then
|
|
185
|
-
SHOULD_UPDATE=true
|
|
186
|
-
echo "[$(date)] post-edit-spec: Increment file edited - will update status line" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
187
|
-
fi
|
|
188
|
-
fi
|
|
189
|
-
fi
|
|
190
|
-
|
|
191
|
-
# ============================================================================
|
|
192
|
-
# TIER 1 FIX: File Modification Time Detection (Fallback)
|
|
193
|
-
# ============================================================================
|
|
194
|
-
# If we couldn't detect the file via environment variables, check which files
|
|
195
|
-
# were modified recently (within last 2 seconds) instead of blindly updating
|
|
196
|
-
if [[ -z "$EDITED_FILE" ]]; then
|
|
197
|
-
echo "[$(date)] post-edit-spec: Env vars empty - checking file mtimes" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
198
|
-
|
|
199
|
-
NOW=$(date +%s)
|
|
200
|
-
INCREMENTS_DIR="$PROJECT_ROOT/.specweave/increments"
|
|
201
|
-
|
|
202
|
-
# Check for recently modified spec.md or tasks.md files
|
|
203
|
-
if [[ -d "$INCREMENTS_DIR" ]]; then
|
|
204
|
-
for file in "$INCREMENTS_DIR"/*/spec.md "$INCREMENTS_DIR"/*/tasks.md; do
|
|
205
|
-
if [[ -f "$file" ]]; then
|
|
206
|
-
# Get file modification time (platform-specific)
|
|
207
|
-
if [[ "$(uname)" == "Darwin" ]]; then
|
|
208
|
-
MTIME=$(stat -f "%m" "$file" 2>/dev/null || echo 0)
|
|
209
|
-
else
|
|
210
|
-
MTIME=$(stat -c "%Y" "$file" 2>/dev/null || echo 0)
|
|
211
|
-
fi
|
|
212
|
-
|
|
213
|
-
# If file was modified in last 2 seconds, consider it the edited file
|
|
214
|
-
TIME_DIFF=$((NOW - MTIME))
|
|
215
|
-
if (( TIME_DIFF <= 2 )); then
|
|
216
|
-
EDITED_FILE="$file"
|
|
217
|
-
echo "[$(date)] post-edit-spec: Detected recent modification: $file (${TIME_DIFF}s ago)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
218
|
-
SHOULD_UPDATE=true
|
|
219
|
-
break
|
|
220
|
-
fi
|
|
221
|
-
fi
|
|
222
|
-
done
|
|
223
|
-
fi
|
|
224
|
-
|
|
225
|
-
# If still no file detected, skip update (not a spec/tasks edit)
|
|
226
|
-
if [[ -z "$EDITED_FILE" ]]; then
|
|
227
|
-
echo "[$(date)] post-edit-spec: No spec/tasks modifications detected - skipping" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
228
|
-
exit 0
|
|
229
|
-
fi
|
|
230
|
-
fi
|
|
231
|
-
|
|
232
|
-
# ============================================================================
|
|
233
|
-
# TIER 1 FIX: Non-Blocking Background Update with COMPLETE ERROR ISOLATION
|
|
234
|
-
# ============================================================================
|
|
235
|
-
# Update status line if needed
|
|
236
|
-
if [[ "$SHOULD_UPDATE" == "true" ]]; then
|
|
237
|
-
echo "[$(date)] post-edit-spec: Running update-status-line.sh (background)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
238
|
-
|
|
239
|
-
# Record update time BEFORE spawning background process
|
|
240
|
-
# This ensures debouncing works even if update hasn't completed yet
|
|
241
|
-
echo "$(date +%s)" > "$LAST_UPDATE_FILE"
|
|
242
|
-
|
|
243
|
-
# Run status line update in background with COMPLETE error isolation
|
|
244
|
-
# This prevents Edit tool from waiting for status line computation
|
|
245
|
-
(
|
|
246
|
-
set +e # Disable error propagation
|
|
247
|
-
|
|
248
|
-
if "$PROJECT_ROOT/plugins/specweave/hooks/lib/update-status-line.sh" 2>&1 | tee -a "$DEBUG_LOG" >/dev/null; then
|
|
249
|
-
echo "[$(date)] post-edit-spec: Status line updated successfully" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
250
|
-
# Reset circuit breaker on success
|
|
251
|
-
echo "0" > "$CIRCUIT_BREAKER_FILE" 2>/dev/null || true
|
|
252
|
-
else
|
|
253
|
-
echo "[$(date)] post-edit-spec: Warning - status line update failed (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
254
|
-
# Increment circuit breaker
|
|
255
|
-
CURRENT_FAILURES=$(cat "$CIRCUIT_BREAKER_FILE" 2>/dev/null || echo 0)
|
|
256
|
-
echo "$((CURRENT_FAILURES + 1))" > "$CIRCUIT_BREAKER_FILE" 2>/dev/null || true
|
|
257
|
-
fi
|
|
258
|
-
) &
|
|
259
|
-
|
|
260
|
-
# Disown the background process so it's not killed when hook exits
|
|
261
|
-
disown 2>/dev/null || true
|
|
262
|
-
fi
|
|
263
|
-
|
|
264
|
-
# Always exit 0 to prevent hook errors from crashing Claude Code
|
|
265
|
-
exit 0
|
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
#
|
|
3
|
-
# Post-Write Hook: Update Status Line After spec.md or tasks.md Writes
|
|
4
|
-
#
|
|
5
|
-
# Triggers: After Write tool creates/replaces spec.md or tasks.md
|
|
6
|
-
# Action: Updates status line cache to reflect latest AC/task progress
|
|
7
|
-
#
|
|
8
|
-
# EMERGENCY FIXES (v0.24.3):
|
|
9
|
-
# - Kill switch: Set SPECWEAVE_DISABLE_HOOKS=1 to disable ALL hooks
|
|
10
|
-
# - Circuit breaker: Auto-disable after 3 consecutive failures
|
|
11
|
-
# - File locking: Prevent concurrent executions (max 1 at a time)
|
|
12
|
-
# - Aggressive debouncing: Increased from 1s to 5s
|
|
13
|
-
# - Complete error isolation: Never let errors reach Claude Code
|
|
14
|
-
#
|
|
15
|
-
# TIER 1 IMPROVEMENTS (v0.24.2):
|
|
16
|
-
# - Debouncing: Skip if updated less than 1 second ago (90% overhead reduction)
|
|
17
|
-
# - File mtime detection: Check recently modified spec.md/tasks.md as fallback
|
|
18
|
-
# - Non-blocking: Run update-status-line.sh in background
|
|
19
|
-
# - Smart detection: Only update if spec/tasks files actually changed
|
|
20
|
-
#
|
|
21
|
-
# Previous fix (v0.24.1): Enhanced file detection for increment completion
|
|
22
|
-
# - Detects writes via TOOL_USE_CONTENT, TOOL_RESULT, and argument parsing
|
|
23
|
-
# - Always updates status line for ANY spec.md/tasks.md write in increments folder
|
|
24
|
-
|
|
25
|
-
# CRITICAL: Remove set -e to prevent hook errors from crashing Claude Code
|
|
26
|
-
set +e
|
|
27
|
-
|
|
28
|
-
# EMERGENCY KILL SWITCH: Disable all hooks if env variable set
|
|
29
|
-
if [[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]]; then
|
|
30
|
-
exit 0
|
|
31
|
-
fi
|
|
32
|
-
|
|
33
|
-
# Find project root
|
|
34
|
-
find_project_root() {
|
|
35
|
-
local dir="$PWD"
|
|
36
|
-
while [[ "$dir" != "/" ]]; do
|
|
37
|
-
if [[ -d "$dir/.specweave" ]]; then
|
|
38
|
-
echo "$dir"
|
|
39
|
-
return 0
|
|
40
|
-
fi
|
|
41
|
-
dir=$(dirname "$dir")
|
|
42
|
-
done
|
|
43
|
-
echo "$PWD"
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
PROJECT_ROOT=$(find_project_root)
|
|
47
|
-
LOGS_DIR="$PROJECT_ROOT/.specweave/logs"
|
|
48
|
-
DEBUG_LOG="$LOGS_DIR/hooks-debug.log"
|
|
49
|
-
|
|
50
|
-
# Ensure state and logs directories exist
|
|
51
|
-
mkdir -p "$PROJECT_ROOT/.specweave/state" "$LOGS_DIR" 2>/dev/null || true
|
|
52
|
-
|
|
53
|
-
# EMERGENCY CIRCUIT BREAKER: Track consecutive failures
|
|
54
|
-
CIRCUIT_BREAKER_FILE="$PROJECT_ROOT/.specweave/state/.hook-circuit-breaker"
|
|
55
|
-
CIRCUIT_BREAKER_THRESHOLD=3
|
|
56
|
-
|
|
57
|
-
if [[ -f "$CIRCUIT_BREAKER_FILE" ]]; then
|
|
58
|
-
FAILURE_COUNT=$(cat "$CIRCUIT_BREAKER_FILE" 2>/dev/null || echo 0)
|
|
59
|
-
if (( FAILURE_COUNT >= CIRCUIT_BREAKER_THRESHOLD )); then
|
|
60
|
-
echo "[$(date)] CIRCUIT BREAKER OPEN: Hooks disabled after $FAILURE_COUNT failures. Run: rm $CIRCUIT_BREAKER_FILE" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
61
|
-
exit 0
|
|
62
|
-
fi
|
|
63
|
-
fi
|
|
64
|
-
|
|
65
|
-
# EMERGENCY FILE LOCK: Prevent concurrent executions
|
|
66
|
-
LOCK_FILE="$PROJECT_ROOT/.specweave/state/.hook-post-write.lock"
|
|
67
|
-
LOCK_TIMEOUT=5 # seconds
|
|
68
|
-
|
|
69
|
-
# Try to acquire lock with timeout
|
|
70
|
-
LOCK_ACQUIRED=false
|
|
71
|
-
for i in {1..5}; do
|
|
72
|
-
if mkdir "$LOCK_FILE" 2>/dev/null; then
|
|
73
|
-
LOCK_ACQUIRED=true
|
|
74
|
-
trap 'rmdir "$LOCK_FILE" 2>/dev/null || true' EXIT
|
|
75
|
-
break
|
|
76
|
-
fi
|
|
77
|
-
|
|
78
|
-
# Check if lock is stale (older than LOCK_TIMEOUT seconds)
|
|
79
|
-
if [[ -d "$LOCK_FILE" ]]; then
|
|
80
|
-
LOCK_AGE=$(($(date +%s) - $(stat -f "%m" "$LOCK_FILE" 2>/dev/null || echo 0)))
|
|
81
|
-
if (( LOCK_AGE > LOCK_TIMEOUT )); then
|
|
82
|
-
rmdir "$LOCK_FILE" 2>/dev/null || true
|
|
83
|
-
continue
|
|
84
|
-
fi
|
|
85
|
-
fi
|
|
86
|
-
|
|
87
|
-
sleep 0.2
|
|
88
|
-
done
|
|
89
|
-
|
|
90
|
-
if [[ "$LOCK_ACQUIRED" == "false" ]]; then
|
|
91
|
-
echo "[$(date)] post-write-spec: Could not acquire lock, skipping" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
92
|
-
exit 0
|
|
93
|
-
fi
|
|
94
|
-
|
|
95
|
-
# Log rotation: Keep debug log under 100KB
|
|
96
|
-
if [[ -f "$DEBUG_LOG" ]] && [[ $(wc -c < "$DEBUG_LOG" 2>/dev/null || echo 0) -gt 102400 ]]; then
|
|
97
|
-
tail -100 "$DEBUG_LOG" > "$DEBUG_LOG.tmp" 2>/dev/null || true
|
|
98
|
-
mv "$DEBUG_LOG.tmp" "$DEBUG_LOG" 2>/dev/null || true
|
|
99
|
-
echo "[$(date)] Log rotated" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
100
|
-
fi
|
|
101
|
-
|
|
102
|
-
# ============================================================================
|
|
103
|
-
# TIER 1 FIX: Debouncing (Prevent Redundant Updates)
|
|
104
|
-
# ============================================================================
|
|
105
|
-
# Skip update if we updated less than 5 seconds ago (INCREASED FROM 1s)
|
|
106
|
-
# This handles rapid consecutive writes (e.g., spec.md regeneration)
|
|
107
|
-
LAST_UPDATE_FILE="$PROJECT_ROOT/.specweave/state/.last-status-update"
|
|
108
|
-
DEBOUNCE_SECONDS=5
|
|
109
|
-
|
|
110
|
-
if [[ -f "$LAST_UPDATE_FILE" ]]; then
|
|
111
|
-
LAST_UPDATE=$(cat "$LAST_UPDATE_FILE" 2>/dev/null || echo 0)
|
|
112
|
-
NOW=$(date +%s)
|
|
113
|
-
TIME_SINCE_UPDATE=$((NOW - LAST_UPDATE))
|
|
114
|
-
|
|
115
|
-
if (( TIME_SINCE_UPDATE < DEBOUNCE_SECONDS )); then
|
|
116
|
-
echo "[$(date)] post-write-spec: Debounced (${TIME_SINCE_UPDATE}s since last update)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
117
|
-
exit 0 # Skip this update
|
|
118
|
-
fi
|
|
119
|
-
fi
|
|
120
|
-
|
|
121
|
-
# ============================================================================
|
|
122
|
-
# TIER 2: Check for PreToolUse Signal (Primary Detection Method)
|
|
123
|
-
# ============================================================================
|
|
124
|
-
PENDING_FILE="$PROJECT_ROOT/.specweave/state/.pending-status-update"
|
|
125
|
-
METRICS_FILE="$PROJECT_ROOT/.specweave/state/hook-metrics.jsonl"
|
|
126
|
-
WRITTEN_FILE=""
|
|
127
|
-
DETECTION_METHOD="none"
|
|
128
|
-
|
|
129
|
-
# First, check if PreToolUse hook left a signal
|
|
130
|
-
if [[ -f "$PENDING_FILE" ]]; then
|
|
131
|
-
WRITTEN_FILE=$(cat "$PENDING_FILE" 2>/dev/null || echo "")
|
|
132
|
-
# Delete pending file immediately (consume signal)
|
|
133
|
-
rm "$PENDING_FILE" 2>/dev/null || true
|
|
134
|
-
|
|
135
|
-
if [[ -n "$WRITTEN_FILE" ]]; then
|
|
136
|
-
DETECTION_METHOD="pretooluse"
|
|
137
|
-
echo "[$(date)] post-write-spec: File from PreToolUse signal: $WRITTEN_FILE" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
138
|
-
|
|
139
|
-
# Record Tier 2 success metric
|
|
140
|
-
TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
141
|
-
echo "{\"timestamp\":\"$TIMESTAMP\",\"hook\":\"post-write-spec\",\"event\":\"tier2_success\",\"method\":\"pretooluse\"}" >> "$METRICS_FILE" 2>/dev/null || true
|
|
142
|
-
fi
|
|
143
|
-
fi
|
|
144
|
-
|
|
145
|
-
# ============================================================================
|
|
146
|
-
# TIER 1 FALLBACK: Environment Variable Detection
|
|
147
|
-
# ============================================================================
|
|
148
|
-
# If PreToolUse didn't provide signal, fall back to Tier 1 methods
|
|
149
|
-
if [[ -z "$WRITTEN_FILE" ]]; then
|
|
150
|
-
# Method 1: TOOL_USE_CONTENT environment variable
|
|
151
|
-
if [[ -n "${TOOL_USE_CONTENT:-}" ]]; then
|
|
152
|
-
WRITTEN_FILE="$TOOL_USE_CONTENT"
|
|
153
|
-
DETECTION_METHOD="env_content"
|
|
154
|
-
fi
|
|
155
|
-
|
|
156
|
-
# Method 2: TOOL_RESULT environment variable
|
|
157
|
-
if [[ -z "$WRITTEN_FILE" ]] && [[ -n "${TOOL_RESULT:-}" ]]; then
|
|
158
|
-
WRITTEN_FILE=$(echo "$TOOL_RESULT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/' || echo "")
|
|
159
|
-
DETECTION_METHOD="env_result"
|
|
160
|
-
fi
|
|
161
|
-
|
|
162
|
-
# Method 3: TOOL_USE_ARGS
|
|
163
|
-
if [[ -z "$WRITTEN_FILE" ]] && [[ -n "${TOOL_USE_ARGS:-}" ]]; then
|
|
164
|
-
WRITTEN_FILE=$(echo "$TOOL_USE_ARGS" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/' || echo "")
|
|
165
|
-
DETECTION_METHOD="env_args"
|
|
166
|
-
fi
|
|
167
|
-
|
|
168
|
-
# Log env var detection (for metrics)
|
|
169
|
-
if [[ -n "$WRITTEN_FILE" ]]; then
|
|
170
|
-
echo "[$(date)] post-write-spec: File from env vars ($DETECTION_METHOD): $WRITTEN_FILE" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
171
|
-
fi
|
|
172
|
-
fi
|
|
173
|
-
|
|
174
|
-
# Log detection attempt (only log if we actually detected a file, to reduce noise)
|
|
175
|
-
if [[ -n "$WRITTEN_FILE" ]]; then
|
|
176
|
-
echo "[$(date)] post-write-spec: Detected file: $WRITTEN_FILE" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
177
|
-
fi
|
|
178
|
-
|
|
179
|
-
# Check if we detected a spec.md or tasks.md write in increments folder
|
|
180
|
-
SHOULD_UPDATE=false
|
|
181
|
-
|
|
182
|
-
if [[ -n "$WRITTEN_FILE" ]]; then
|
|
183
|
-
# Check if the file is spec.md or tasks.md
|
|
184
|
-
if [[ "$WRITTEN_FILE" == *"/spec.md" ]] || [[ "$WRITTEN_FILE" == *"/tasks.md" ]]; then
|
|
185
|
-
# Check if it's in an increment folder
|
|
186
|
-
if [[ "$WRITTEN_FILE" == *"/.specweave/increments/"* ]]; then
|
|
187
|
-
SHOULD_UPDATE=true
|
|
188
|
-
echo "[$(date)] post-write-spec: Increment file written - will update status line" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
189
|
-
fi
|
|
190
|
-
fi
|
|
191
|
-
fi
|
|
192
|
-
|
|
193
|
-
# ============================================================================
|
|
194
|
-
# TIER 1 FIX: File Modification Time Detection (Fallback)
|
|
195
|
-
# ============================================================================
|
|
196
|
-
# If we couldn't detect the file via environment variables, check which files
|
|
197
|
-
# were modified recently (within last 2 seconds) instead of blindly updating
|
|
198
|
-
if [[ -z "$WRITTEN_FILE" ]]; then
|
|
199
|
-
echo "[$(date)] post-write-spec: Env vars empty - checking file mtimes" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
200
|
-
|
|
201
|
-
NOW=$(date +%s)
|
|
202
|
-
INCREMENTS_DIR="$PROJECT_ROOT/.specweave/increments"
|
|
203
|
-
|
|
204
|
-
# Check for recently modified spec.md or tasks.md files
|
|
205
|
-
if [[ -d "$INCREMENTS_DIR" ]]; then
|
|
206
|
-
for file in "$INCREMENTS_DIR"/*/spec.md "$INCREMENTS_DIR"/*/tasks.md; do
|
|
207
|
-
if [[ -f "$file" ]]; then
|
|
208
|
-
# Get file modification time (platform-specific)
|
|
209
|
-
if [[ "$(uname)" == "Darwin" ]]; then
|
|
210
|
-
MTIME=$(stat -f "%m" "$file" 2>/dev/null || echo 0)
|
|
211
|
-
else
|
|
212
|
-
MTIME=$(stat -c "%Y" "$file" 2>/dev/null || echo 0)
|
|
213
|
-
fi
|
|
214
|
-
|
|
215
|
-
# If file was modified in last 2 seconds, consider it the written file
|
|
216
|
-
TIME_DIFF=$((NOW - MTIME))
|
|
217
|
-
if (( TIME_DIFF <= 2 )); then
|
|
218
|
-
WRITTEN_FILE="$file"
|
|
219
|
-
echo "[$(date)] post-write-spec: Detected recent modification: $file (${TIME_DIFF}s ago)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
220
|
-
SHOULD_UPDATE=true
|
|
221
|
-
break
|
|
222
|
-
fi
|
|
223
|
-
fi
|
|
224
|
-
done
|
|
225
|
-
fi
|
|
226
|
-
|
|
227
|
-
# If still no file detected, skip update (not a spec/tasks write)
|
|
228
|
-
if [[ -z "$WRITTEN_FILE" ]]; then
|
|
229
|
-
echo "[$(date)] post-write-spec: No spec/tasks modifications detected - skipping" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
230
|
-
exit 0
|
|
231
|
-
fi
|
|
232
|
-
fi
|
|
233
|
-
|
|
234
|
-
# ============================================================================
|
|
235
|
-
# TIER 1 FIX: Non-Blocking Background Update with COMPLETE ERROR ISOLATION
|
|
236
|
-
# ============================================================================
|
|
237
|
-
# Update status line if needed
|
|
238
|
-
if [[ "$SHOULD_UPDATE" == "true" ]]; then
|
|
239
|
-
echo "[$(date)] post-write-spec: Running update-status-line.sh (background)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
240
|
-
|
|
241
|
-
# Record update time BEFORE spawning background process
|
|
242
|
-
# This ensures debouncing works even if update hasn't completed yet
|
|
243
|
-
echo "$(date +%s)" > "$LAST_UPDATE_FILE"
|
|
244
|
-
|
|
245
|
-
# Run status line update in background with COMPLETE error isolation
|
|
246
|
-
# This prevents Write tool from waiting for status line computation
|
|
247
|
-
(
|
|
248
|
-
set +e # Disable error propagation
|
|
249
|
-
|
|
250
|
-
if "$PROJECT_ROOT/plugins/specweave/hooks/lib/update-status-line.sh" 2>&1 | tee -a "$DEBUG_LOG" >/dev/null; then
|
|
251
|
-
echo "[$(date)] post-write-spec: Status line updated successfully" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
252
|
-
# Reset circuit breaker on success
|
|
253
|
-
echo "0" > "$CIRCUIT_BREAKER_FILE" 2>/dev/null || true
|
|
254
|
-
else
|
|
255
|
-
echo "[$(date)] post-write-spec: Warning - status line update failed (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
256
|
-
# Increment circuit breaker
|
|
257
|
-
CURRENT_FAILURES=$(cat "$CIRCUIT_BREAKER_FILE" 2>/dev/null || echo 0)
|
|
258
|
-
echo "$((CURRENT_FAILURES + 1))" > "$CIRCUIT_BREAKER_FILE" 2>/dev/null || true
|
|
259
|
-
fi
|
|
260
|
-
) &
|
|
261
|
-
|
|
262
|
-
# Disown the background process so it's not killed when hook exits
|
|
263
|
-
disown 2>/dev/null || true
|
|
264
|
-
fi
|
|
265
|
-
|
|
266
|
-
# Always exit 0 to prevent hook errors from crashing Claude Code
|
|
267
|
-
exit 0
|