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,183 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# fail-fast-wrapper.sh - HARD TIMEOUT wrapper for all hooks
|
|
3
|
+
# If ANY hook takes longer than HOOK_TIMEOUT, it gets KILLED.
|
|
4
|
+
#
|
|
5
|
+
# Usage: bash fail-fast-wrapper.sh <hook-script> [args...]
|
|
6
|
+
#
|
|
7
|
+
# Environment:
|
|
8
|
+
# HOOK_TIMEOUT - max seconds (default: 5)
|
|
9
|
+
# HOOK_DEBUG - set to 1 for verbose logging
|
|
10
|
+
#
|
|
11
|
+
# Exit behavior:
|
|
12
|
+
# - Returns hook output on success
|
|
13
|
+
# - Returns safe JSON on timeout ({"continue":true} or {"decision":"approve"})
|
|
14
|
+
# - NEVER hangs - timeout is enforced with SIGKILL
|
|
15
|
+
#
|
|
16
|
+
# CRASH PREVENTION:
|
|
17
|
+
# - Integrates with crash-prevention.sh for process storm detection
|
|
18
|
+
# - Auto-kills zombie processes on timeout
|
|
19
|
+
# - Records failures for circuit breaker
|
|
20
|
+
#
|
|
21
|
+
# v0.33.0 - Enhanced with crash prevention integration
|
|
22
|
+
|
|
23
|
+
set -o pipefail
|
|
24
|
+
|
|
25
|
+
# === Configuration ===
|
|
26
|
+
HOOK_TIMEOUT="${HOOK_TIMEOUT:-5}" # 5 seconds - more than enough for any hook
|
|
27
|
+
HOOK_DEBUG="${HOOK_DEBUG:-0}"
|
|
28
|
+
LOG_FILE="${HOME}/.claude/hook-failures.log"
|
|
29
|
+
|
|
30
|
+
# === Crash Prevention Integration ===
|
|
31
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
32
|
+
CRASH_PREVENTION="${SCRIPT_DIR}/../lib/crash-prevention.sh"
|
|
33
|
+
|
|
34
|
+
# Source crash prevention if available (non-blocking)
|
|
35
|
+
if [[ -f "$CRASH_PREVENTION" ]]; then
|
|
36
|
+
source "$CRASH_PREVENTION" 2>/dev/null || true
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# === Helper functions ===
|
|
40
|
+
log_debug() {
|
|
41
|
+
[[ "$HOOK_DEBUG" == "1" ]] && echo "[DEBUG $(date +%H:%M:%S)] $*" >&2
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
log_failure() {
|
|
45
|
+
local msg="$1"
|
|
46
|
+
mkdir -p "$(dirname "$LOG_FILE")"
|
|
47
|
+
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] HOOK TIMEOUT: $msg" >> "$LOG_FILE"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# === Safe JSON output based on hook type ===
|
|
51
|
+
get_safe_output() {
|
|
52
|
+
local script="$1"
|
|
53
|
+
# PreToolUse hooks need "decision" format
|
|
54
|
+
if [[ "$script" == *"guard"* ]] || [[ "$script" == *"validator"* ]] || [[ "$script" == *"PreToolUse"* ]]; then
|
|
55
|
+
echo '{"decision":"allow"}'
|
|
56
|
+
else
|
|
57
|
+
echo '{"continue":true}'
|
|
58
|
+
fi
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# === Read stdin with timeout ===
|
|
62
|
+
# Critical: stdin can block forever if not handled properly
|
|
63
|
+
read_stdin_with_timeout() {
|
|
64
|
+
local stdin_content=""
|
|
65
|
+
|
|
66
|
+
# Use read with timeout (integer seconds for bash compatibility)
|
|
67
|
+
if read -t 1 -r line; then
|
|
68
|
+
stdin_content="$line"
|
|
69
|
+
# Continue reading remaining lines (no timeout - stdin should be closed)
|
|
70
|
+
while IFS= read -r line; do
|
|
71
|
+
stdin_content="${stdin_content}"$'\n'"${line}"
|
|
72
|
+
done
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
echo "$stdin_content"
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# === Main execution ===
|
|
79
|
+
main() {
|
|
80
|
+
local script="$1"
|
|
81
|
+
shift
|
|
82
|
+
local args=("$@")
|
|
83
|
+
|
|
84
|
+
if [[ -z "$script" ]]; then
|
|
85
|
+
echo '{"continue":true}'
|
|
86
|
+
exit 0
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
if [[ ! -f "$script" ]]; then
|
|
90
|
+
log_debug "Script not found: $script"
|
|
91
|
+
echo '{"continue":true}'
|
|
92
|
+
exit 0
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# === CRASH PREVENTION: Process Storm Detection ===
|
|
96
|
+
# If too many hooks are running, skip this one to prevent cascade
|
|
97
|
+
if type detect_process_storm &>/dev/null; then
|
|
98
|
+
local storm_status
|
|
99
|
+
storm_status=$(detect_process_storm 25)
|
|
100
|
+
if [[ "$storm_status" == STORM* ]]; then
|
|
101
|
+
log_failure "$script - BLOCKED due to process storm: $storm_status"
|
|
102
|
+
get_safe_output "$script"
|
|
103
|
+
exit 0
|
|
104
|
+
fi
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
log_debug "Executing: $script (timeout: ${HOOK_TIMEOUT}s)"
|
|
108
|
+
|
|
109
|
+
# Read stdin first (with its own timeout)
|
|
110
|
+
local stdin_content
|
|
111
|
+
stdin_content=$(read_stdin_with_timeout)
|
|
112
|
+
|
|
113
|
+
# Execute the hook with hard timeout
|
|
114
|
+
# Using timeout with --kill-after to ensure SIGKILL if SIGTERM doesn't work
|
|
115
|
+
local output
|
|
116
|
+
local exit_code
|
|
117
|
+
|
|
118
|
+
# Create temp file for output (avoid subshell issues)
|
|
119
|
+
local tmp_out
|
|
120
|
+
tmp_out=$(mktemp)
|
|
121
|
+
|
|
122
|
+
# Run with timeout - kill entire process group on timeout
|
|
123
|
+
if command -v gtimeout >/dev/null 2>&1; then
|
|
124
|
+
# macOS with coreutils
|
|
125
|
+
echo "$stdin_content" | gtimeout --kill-after=2 "$HOOK_TIMEOUT" bash "$script" "${args[@]}" > "$tmp_out" 2>/dev/null
|
|
126
|
+
exit_code=$?
|
|
127
|
+
elif command -v timeout >/dev/null 2>&1; then
|
|
128
|
+
# Linux
|
|
129
|
+
echo "$stdin_content" | timeout --kill-after=2 "$HOOK_TIMEOUT" bash "$script" "${args[@]}" > "$tmp_out" 2>/dev/null
|
|
130
|
+
exit_code=$?
|
|
131
|
+
else
|
|
132
|
+
# Fallback: manual timeout using background process
|
|
133
|
+
(
|
|
134
|
+
echo "$stdin_content" | bash "$script" "${args[@]}" > "$tmp_out" 2>/dev/null
|
|
135
|
+
) &
|
|
136
|
+
local pid=$!
|
|
137
|
+
|
|
138
|
+
# Wait with timeout
|
|
139
|
+
local count=0
|
|
140
|
+
while kill -0 "$pid" 2>/dev/null && [[ $count -lt $((HOOK_TIMEOUT * 10)) ]]; do
|
|
141
|
+
sleep 0.1
|
|
142
|
+
count=$((count + 1))
|
|
143
|
+
done
|
|
144
|
+
|
|
145
|
+
if kill -0 "$pid" 2>/dev/null; then
|
|
146
|
+
# Still running - kill it!
|
|
147
|
+
kill -9 "$pid" 2>/dev/null
|
|
148
|
+
wait "$pid" 2>/dev/null
|
|
149
|
+
exit_code=124 # timeout exit code
|
|
150
|
+
else
|
|
151
|
+
wait "$pid"
|
|
152
|
+
exit_code=$?
|
|
153
|
+
fi
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
output=$(cat "$tmp_out" 2>/dev/null)
|
|
157
|
+
rm -f "$tmp_out"
|
|
158
|
+
|
|
159
|
+
# Handle timeout (exit code 124 or 137)
|
|
160
|
+
if [[ $exit_code -eq 124 ]] || [[ $exit_code -eq 137 ]]; then
|
|
161
|
+
log_failure "$script - killed after ${HOOK_TIMEOUT}s"
|
|
162
|
+
log_debug "TIMEOUT: $script killed after ${HOOK_TIMEOUT}s"
|
|
163
|
+
|
|
164
|
+
# === CRASH PREVENTION: Clean up potential zombie processes ===
|
|
165
|
+
if type kill_zombie_heredocs &>/dev/null; then
|
|
166
|
+
kill_zombie_heredocs 2>/dev/null || true
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
get_safe_output "$script"
|
|
170
|
+
exit 0
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
# Return output or safe default
|
|
174
|
+
if [[ -n "$output" ]]; then
|
|
175
|
+
echo "$output"
|
|
176
|
+
else
|
|
177
|
+
get_safe_output "$script"
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
exit 0
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
main "$@"
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
|
|
3
|
-
# SpecWeave UserPromptSubmit Hook (v0.
|
|
3
|
+
# SpecWeave UserPromptSubmit Hook (v0.33.0 - SCRIPT DELEGATION)
|
|
4
4
|
# Fires BEFORE user's command executes (prompt-based hook)
|
|
5
5
|
# Purpose: Discipline validation, context injection, command suggestions
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
7
|
+
# FEATURES:
|
|
8
|
+
# - v0.33.0: Script delegation - status commands bypass LLM entirely (<1s)
|
|
9
|
+
# - v0.26.13: jq for JSON parsing (10x faster than node -e)
|
|
10
|
+
# - Single active increment detection (cached, not 4x!)
|
|
11
|
+
# - Deferred heavy checks (SpecSyncManager only when needed)
|
|
12
|
+
# - Ultra-fast early exits
|
|
13
13
|
#
|
|
14
|
-
# Performance: <
|
|
14
|
+
# Performance: Status commands <1s (was 3+ min), other prompts <10ms
|
|
15
15
|
|
|
16
16
|
set +e
|
|
17
17
|
|
|
@@ -45,6 +45,131 @@ if [[ ! -d "$SPECWEAVE_DIR" ]]; then
|
|
|
45
45
|
exit 0
|
|
46
46
|
fi
|
|
47
47
|
|
|
48
|
+
# ==============================================================================
|
|
49
|
+
# INSTANT SCRIPT EXECUTION: Status commands bypass LLM entirely (v0.33.0)
|
|
50
|
+
# ==============================================================================
|
|
51
|
+
# These commands need NO LLM reasoning - execute scripts directly for <1s response
|
|
52
|
+
# Pattern: Detect command → Execute script → Return output via "block" → Exit
|
|
53
|
+
|
|
54
|
+
PLUGIN_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
55
|
+
SCRIPTS_DIR="$PLUGIN_ROOT/scripts"
|
|
56
|
+
|
|
57
|
+
# Helper: Escape output for JSON (handles newlines, quotes, backslashes)
|
|
58
|
+
escape_json() {
|
|
59
|
+
local input="$1"
|
|
60
|
+
# Escape backslashes, then quotes, then newlines
|
|
61
|
+
echo "$input" | sed 's/\\/\\\\/g; s/"/\\"/g' | awk '{printf "%s\\n", $0}' | sed 's/\\n$//'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# /specweave:jobs → Execute read-jobs.sh (pure bash, ~2ms)
|
|
65
|
+
if echo "$PROMPT" | grep -qE "^/specweave:jobs($| )"; then
|
|
66
|
+
ARGS=$(echo "$PROMPT" | sed 's|^/specweave:jobs\s*||')
|
|
67
|
+
if [[ -f "$SCRIPTS_DIR/read-jobs.sh" ]]; then
|
|
68
|
+
OUTPUT=$(cd "$(pwd)" && bash "$SCRIPTS_DIR/read-jobs.sh" $ARGS 2>&1)
|
|
69
|
+
elif [[ -f "$SCRIPTS_DIR/jobs.js" ]] && command -v node >/dev/null 2>&1; then
|
|
70
|
+
OUTPUT=$(cd "$(pwd)" && node "$SCRIPTS_DIR/jobs.js" $ARGS 2>&1)
|
|
71
|
+
else
|
|
72
|
+
OUTPUT="❌ No jobs script available"
|
|
73
|
+
fi
|
|
74
|
+
OUTPUT_ESCAPED=$(escape_json "$OUTPUT")
|
|
75
|
+
printf '{"decision":"block","reason":"%s"}\n' "$OUTPUT_ESCAPED"
|
|
76
|
+
exit 0
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# /specweave:progress → Execute read-progress.sh (pure bash, ~30ms)
|
|
80
|
+
if echo "$PROMPT" | grep -qE "^/specweave:progress($| )"; then
|
|
81
|
+
ARGS=$(echo "$PROMPT" | sed 's|^/specweave:progress\s*||')
|
|
82
|
+
if [[ -f "$SCRIPTS_DIR/read-progress.sh" ]]; then
|
|
83
|
+
OUTPUT=$(cd "$(pwd)" && bash "$SCRIPTS_DIR/read-progress.sh" $ARGS 2>&1)
|
|
84
|
+
elif [[ -f "$SCRIPTS_DIR/progress.js" ]] && command -v node >/dev/null 2>&1; then
|
|
85
|
+
OUTPUT=$(cd "$(pwd)" && node "$SCRIPTS_DIR/progress.js" $ARGS 2>&1)
|
|
86
|
+
else
|
|
87
|
+
OUTPUT="❌ No progress script available"
|
|
88
|
+
fi
|
|
89
|
+
OUTPUT_ESCAPED=$(escape_json "$OUTPUT")
|
|
90
|
+
printf '{"decision":"block","reason":"%s"}\n' "$OUTPUT_ESCAPED"
|
|
91
|
+
exit 0
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# /specweave:status → Execute read-status.sh (pure bash, ~150ms)
|
|
95
|
+
if echo "$PROMPT" | grep -qE "^/specweave:status($| )"; then
|
|
96
|
+
ARGS=$(echo "$PROMPT" | sed 's|^/specweave:status\s*||')
|
|
97
|
+
if [[ -f "$SCRIPTS_DIR/read-status.sh" ]]; then
|
|
98
|
+
OUTPUT=$(cd "$(pwd)" && bash "$SCRIPTS_DIR/read-status.sh" $ARGS 2>&1)
|
|
99
|
+
elif [[ -f "$SCRIPTS_DIR/status.js" ]] && command -v node >/dev/null 2>&1; then
|
|
100
|
+
OUTPUT=$(cd "$(pwd)" && node "$SCRIPTS_DIR/status.js" $ARGS 2>&1)
|
|
101
|
+
else
|
|
102
|
+
OUTPUT="❌ No status script available"
|
|
103
|
+
fi
|
|
104
|
+
OUTPUT_ESCAPED=$(escape_json "$OUTPUT")
|
|
105
|
+
printf '{"decision":"block","reason":"%s"}\n' "$OUTPUT_ESCAPED"
|
|
106
|
+
exit 0
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
# /specweave:workflow → Execute read-workflow.sh (pure bash, ~100ms)
|
|
110
|
+
if echo "$PROMPT" | grep -qE "^/specweave:workflow($| )"; then
|
|
111
|
+
ARGS=$(echo "$PROMPT" | sed 's|^/specweave:workflow\s*||')
|
|
112
|
+
if [[ -f "$SCRIPTS_DIR/read-workflow.sh" ]]; then
|
|
113
|
+
OUTPUT=$(cd "$(pwd)" && bash "$SCRIPTS_DIR/read-workflow.sh" $ARGS 2>&1)
|
|
114
|
+
else
|
|
115
|
+
OUTPUT="❌ No workflow script available"
|
|
116
|
+
fi
|
|
117
|
+
OUTPUT_ESCAPED=$(escape_json "$OUTPUT")
|
|
118
|
+
printf '{"decision":"block","reason":"%s"}\n' "$OUTPUT_ESCAPED"
|
|
119
|
+
exit 0
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
# /specweave:costs → Execute read-costs.sh (pure bash, ~50ms)
|
|
123
|
+
if echo "$PROMPT" | grep -qE "^/specweave:costs($| )"; then
|
|
124
|
+
ARGS=$(echo "$PROMPT" | sed 's|^/specweave:costs\s*||')
|
|
125
|
+
if [[ -f "$SCRIPTS_DIR/read-costs.sh" ]]; then
|
|
126
|
+
OUTPUT=$(cd "$(pwd)" && bash "$SCRIPTS_DIR/read-costs.sh" $ARGS 2>&1)
|
|
127
|
+
else
|
|
128
|
+
OUTPUT="❌ No costs script available"
|
|
129
|
+
fi
|
|
130
|
+
OUTPUT_ESCAPED=$(escape_json "$OUTPUT")
|
|
131
|
+
printf '{"decision":"block","reason":"%s"}\n' "$OUTPUT_ESCAPED"
|
|
132
|
+
exit 0
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
# ==============================================================================
|
|
136
|
+
# TASK COUNT GUARD: Block /specweave:do for oversized increments (v0.32.2+)
|
|
137
|
+
# ==============================================================================
|
|
138
|
+
# >8 tasks = context explosion = CRASH (per CLAUDE.md)
|
|
139
|
+
MAX_TASKS=8
|
|
140
|
+
|
|
141
|
+
if echo "$PROMPT" | grep -qE "^/specweave:do($| )"; then
|
|
142
|
+
# Extract increment ID from prompt
|
|
143
|
+
DO_INCREMENT_ID=$(echo "$PROMPT" | grep -oE "[0-9]{4}[a-zA-Z0-9-]*" | head -1)
|
|
144
|
+
|
|
145
|
+
# If no ID provided, find active increment
|
|
146
|
+
if [[ -z "$DO_INCREMENT_ID" ]]; then
|
|
147
|
+
for meta in "$SPECWEAVE_DIR/increments"/*/metadata.json; do
|
|
148
|
+
[[ -f "$meta" ]] || continue
|
|
149
|
+
if command -v jq >/dev/null 2>&1; then
|
|
150
|
+
status=$(jq -r '.status // "unknown"' "$meta" 2>/dev/null)
|
|
151
|
+
else
|
|
152
|
+
status=$(grep -oP '"status"\s*:\s*"\K[^"]*' "$meta" 2>/dev/null || echo "unknown")
|
|
153
|
+
fi
|
|
154
|
+
if [[ "$status" == "active" || "$status" == "in-progress" ]]; then
|
|
155
|
+
DO_INCREMENT_ID=$(basename "$(dirname "$meta")")
|
|
156
|
+
break
|
|
157
|
+
fi
|
|
158
|
+
done
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
if [[ -n "$DO_INCREMENT_ID" ]]; then
|
|
162
|
+
TASKS_FILE="$SPECWEAVE_DIR/increments/$DO_INCREMENT_ID/tasks.md"
|
|
163
|
+
if [[ -f "$TASKS_FILE" ]]; then
|
|
164
|
+
TASK_COUNT=$(grep -c "^### T-" "$TASKS_FILE" 2>/dev/null || echo "0")
|
|
165
|
+
if [[ "$TASK_COUNT" -gt "$MAX_TASKS" ]]; then
|
|
166
|
+
printf '{"decision":"block","reason":"❌ TASK COUNT EXCEEDS LIMIT\\n\\nIncrement %s has %s tasks (maximum: %s)\\n\\n>8 tasks = context explosion = CRASH\\n\\n💡 REQUIRED: Split into smaller increments:\\n\\n Pattern: %s/ → Split into:\\n • %s-part1/ (T-001 to T-004)\\n • Next increment (T-005 to T-008)\\n • Next increment (T-009+)\\n\\n⚠️ DO NOT PROCEED until tasks.md has ≤8 tasks!"}\n' "$DO_INCREMENT_ID" "$TASK_COUNT" "$MAX_TASKS" "$DO_INCREMENT_ID" "$DO_INCREMENT_ID"
|
|
167
|
+
exit 0
|
|
168
|
+
fi
|
|
169
|
+
fi
|
|
170
|
+
fi
|
|
171
|
+
fi
|
|
172
|
+
|
|
48
173
|
# ==============================================================================
|
|
49
174
|
# CACHED ACTIVE INCREMENT DETECTION (ONCE - reused throughout!)
|
|
50
175
|
# ==============================================================================
|
|
@@ -101,23 +226,13 @@ if echo "$PROMPT" | grep -q "/specweave:increment"; then
|
|
|
101
226
|
|
|
102
227
|
# Above hard cap: strong warning but NOT a block (user decides!)
|
|
103
228
|
if [[ "$ACTIVE_COUNT" -ge "$HARD_CAP" ]]; then
|
|
104
|
-
|
|
105
|
-
{
|
|
106
|
-
"decision": "approve",
|
|
107
|
-
"systemMessage": "⚠️ WIP LIMIT EXCEEDED (${ACTIVE_COUNT}/${HARD_CAP})\n\nYou have $ACTIVE_COUNT active increments (configured maximum: $HARD_CAP)\n\nActive increments:\n$ACTIVE_LIST\n\n🧠 Research shows 3+ concurrent tasks = 40% slower + more bugs\n\n💡 Options:\n 1️⃣ Complete an increment: /specweave:done <id>\n 2️⃣ Pause an increment: /specweave:pause <id>\n 3️⃣ Increase limit: Edit .specweave/config.json limits.hardCap\n 4️⃣ Continue anyway (not recommended)\n\n📝 To proceed anyway, just confirm your intent."
|
|
108
|
-
}
|
|
109
|
-
EOF
|
|
229
|
+
printf '{"decision":"approve","systemMessage":"⚠️ WIP LIMIT EXCEEDED (%s/%s)\\n\\nYou have %s active increments (configured maximum: %s)\\n\\nActive increments:\\n%s\\n\\n🧠 Research shows 3+ concurrent tasks = 40%% slower + more bugs\\n\\n💡 Options:\\n 1️⃣ Complete an increment: /specweave:done <id>\\n 2️⃣ Pause an increment: /specweave:pause <id>\\n 3️⃣ Increase limit: Edit .specweave/config.json limits.hardCap\\n 4️⃣ Continue anyway (not recommended)\\n\\n📝 To proceed anyway, just confirm your intent."}\n' "$ACTIVE_COUNT" "$HARD_CAP" "$ACTIVE_COUNT" "$HARD_CAP" "$ACTIVE_LIST"
|
|
110
230
|
exit 0
|
|
111
231
|
fi
|
|
112
232
|
|
|
113
233
|
# At soft limit: mild warning, approve
|
|
114
234
|
if [[ "$ACTIVE_COUNT" -ge "$SOFT_LIMIT" ]]; then
|
|
115
|
-
|
|
116
|
-
{
|
|
117
|
-
"decision": "approve",
|
|
118
|
-
"systemMessage": "⚠️ WIP LIMIT REACHED (${ACTIVE_COUNT}/${SOFT_LIMIT})\n\nYou have $ACTIVE_COUNT active increment(s) (recommended limit: $SOFT_LIMIT)\n\nActive increments:\n$ACTIVE_LIST\n\n🧠 Focus Principle: Fewer active increments = maximum productivity\n\n💡 Consider:\n 1️⃣ Complete current work (recommended)\n 2️⃣ Pause current work (/specweave:pause)\n 3️⃣ Continue anyway\n\n⚠️ Emergency hotfix/bug? Use --type=hotfix or --type=bug"
|
|
119
|
-
}
|
|
120
|
-
EOF
|
|
235
|
+
printf '{"decision":"approve","systemMessage":"⚠️ WIP LIMIT REACHED (%s/%s)\\n\\nYou have %s active increment(s) (recommended limit: %s)\\n\\nActive increments:\\n%s\\n\\n🧠 Focus Principle: Fewer active increments = maximum productivity\\n\\n💡 Consider:\\n 1️⃣ Complete current work (recommended)\\n 2️⃣ Pause current work (/specweave:pause)\\n 3️⃣ Continue anyway\\n\\n⚠️ Emergency hotfix/bug? Use --type=hotfix or --type=bug"}\n' "$ACTIVE_COUNT" "$SOFT_LIMIT" "$ACTIVE_COUNT" "$SOFT_LIMIT" "$ACTIVE_LIST"
|
|
121
236
|
exit 0
|
|
122
237
|
fi
|
|
123
238
|
fi
|
|
@@ -163,12 +278,7 @@ if [[ -n "$ACTIVE_INCREMENT" ]] && echo "$PROMPT" | grep -qE "/(specweave:)?(syn
|
|
|
163
278
|
if [[ -f "$SPEC_FILE" ]] && [[ -f "$PLAN_FILE" ]]; then
|
|
164
279
|
# Check if spec is newer than plan (indicates spec changes need sync)
|
|
165
280
|
if [[ -n $(find "$SPEC_FILE" -newer "$PLAN_FILE" 2>/dev/null) ]]; then
|
|
166
|
-
|
|
167
|
-
{
|
|
168
|
-
"decision": "approve",
|
|
169
|
-
"systemMessage": "⚠️ Spec changes detected in $ACTIVE_INCREMENT\n\nspec.md has been modified after plan.md.\nConsider running /specweave:sync-docs to update living documentation."
|
|
170
|
-
}
|
|
171
|
-
EOF
|
|
281
|
+
printf '{"decision":"approve","systemMessage":"⚠️ Spec changes detected in %s\\n\\nspec.md has been modified after plan.md.\\nConsider running /specweave:sync-docs to update living documentation."}\n' "$ACTIVE_INCREMENT"
|
|
172
282
|
exit 0
|
|
173
283
|
fi
|
|
174
284
|
fi
|
|
@@ -253,19 +363,11 @@ fi
|
|
|
253
363
|
# ==============================================================================
|
|
254
364
|
|
|
255
365
|
if [[ -n "$CONTEXT" ]]; then
|
|
256
|
-
|
|
257
|
-
{
|
|
258
|
-
"decision":
|
|
259
|
-
"systemMessage": "$CONTEXT"
|
|
260
|
-
}
|
|
261
|
-
EOF
|
|
366
|
+
# Escape context for JSON (newlines, quotes)
|
|
367
|
+
CONTEXT_ESCAPED=$(echo "$CONTEXT" | sed 's/\\/\\\\/g; s/"/\\"/g' | awk '{printf "%s\\n", $0}' | sed 's/\\n$//')
|
|
368
|
+
printf '{"decision":"approve","systemMessage":"%s"}\n' "$CONTEXT_ESCAPED"
|
|
262
369
|
else
|
|
263
|
-
|
|
264
|
-
cat <<EOF
|
|
265
|
-
{
|
|
266
|
-
"decision": "approve"
|
|
267
|
-
}
|
|
268
|
-
EOF
|
|
370
|
+
echo '{"decision":"approve"}'
|
|
269
371
|
fi
|
|
270
372
|
|
|
271
373
|
exit 0
|