specweave 1.0.550 → 1.0.552
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 +1 -1
- package/bin/specweave.js +23 -1
- package/dist/src/cli/commands/hook.d.ts +15 -0
- package/dist/src/cli/commands/hook.d.ts.map +1 -0
- package/dist/src/cli/commands/hook.js +61 -0
- package/dist/src/cli/commands/hook.js.map +1 -0
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +5 -0
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/refresh-plugins.d.ts.map +1 -1
- package/dist/src/cli/commands/refresh-plugins.js +11 -1
- package/dist/src/cli/commands/refresh-plugins.js.map +1 -1
- package/dist/src/cli/commands/sync-setup.d.ts.map +1 -1
- package/dist/src/cli/commands/sync-setup.js +7 -3
- package/dist/src/cli/commands/sync-setup.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/project-mapping-wizard.d.ts +9 -0
- package/dist/src/cli/helpers/issue-tracker/project-mapping-wizard.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/project-mapping-wizard.js +9 -3
- package/dist/src/cli/helpers/issue-tracker/project-mapping-wizard.js.map +1 -1
- package/dist/src/config/types.d.ts +2 -2
- package/dist/src/core/config/types.d.ts +18 -2
- package/dist/src/core/config/types.d.ts.map +1 -1
- package/dist/src/core/config/types.js.map +1 -1
- package/dist/src/core/hooks/handlers/hook-router.d.ts +19 -0
- package/dist/src/core/hooks/handlers/hook-router.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/hook-router.js +75 -0
- package/dist/src/core/hooks/handlers/hook-router.js.map +1 -0
- package/dist/src/core/hooks/handlers/index.d.ts +10 -0
- package/dist/src/core/hooks/handlers/index.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/index.js +9 -0
- package/dist/src/core/hooks/handlers/index.js.map +1 -0
- package/dist/src/core/hooks/handlers/post-tool-use-analytics.d.ts +11 -0
- package/dist/src/core/hooks/handlers/post-tool-use-analytics.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/post-tool-use-analytics.js +73 -0
- package/dist/src/core/hooks/handlers/post-tool-use-analytics.js.map +1 -0
- package/dist/src/core/hooks/handlers/post-tool-use.d.ts +11 -0
- package/dist/src/core/hooks/handlers/post-tool-use.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/post-tool-use.js +76 -0
- package/dist/src/core/hooks/handlers/post-tool-use.js.map +1 -0
- package/dist/src/core/hooks/handlers/pre-compact.d.ts +11 -0
- package/dist/src/core/hooks/handlers/pre-compact.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/pre-compact.js +77 -0
- package/dist/src/core/hooks/handlers/pre-compact.js.map +1 -0
- package/dist/src/core/hooks/handlers/pre-tool-use.d.ts +11 -0
- package/dist/src/core/hooks/handlers/pre-tool-use.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/pre-tool-use.js +318 -0
- package/dist/src/core/hooks/handlers/pre-tool-use.js.map +1 -0
- package/dist/src/core/hooks/handlers/session-start.d.ts +9 -0
- package/dist/src/core/hooks/handlers/session-start.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/session-start.js +111 -0
- package/dist/src/core/hooks/handlers/session-start.js.map +1 -0
- package/dist/src/core/hooks/handlers/stop-auto.d.ts +16 -0
- package/dist/src/core/hooks/handlers/stop-auto.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/stop-auto.js +122 -0
- package/dist/src/core/hooks/handlers/stop-auto.js.map +1 -0
- package/dist/src/core/hooks/handlers/stop-reflect.d.ts +14 -0
- package/dist/src/core/hooks/handlers/stop-reflect.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/stop-reflect.js +43 -0
- package/dist/src/core/hooks/handlers/stop-reflect.js.map +1 -0
- package/dist/src/core/hooks/handlers/stop-sync.d.ts +15 -0
- package/dist/src/core/hooks/handlers/stop-sync.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/stop-sync.js +68 -0
- package/dist/src/core/hooks/handlers/stop-sync.js.map +1 -0
- package/dist/src/core/hooks/handlers/types.d.ts +63 -0
- package/dist/src/core/hooks/handlers/types.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/types.js +27 -0
- package/dist/src/core/hooks/handlers/types.js.map +1 -0
- package/dist/src/core/hooks/handlers/user-prompt-submit.d.ts +14 -0
- package/dist/src/core/hooks/handlers/user-prompt-submit.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/user-prompt-submit.js +173 -0
- package/dist/src/core/hooks/handlers/user-prompt-submit.js.map +1 -0
- package/dist/src/core/hooks/handlers/utils.d.ts +25 -0
- package/dist/src/core/hooks/handlers/utils.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/utils.js +64 -0
- package/dist/src/core/hooks/handlers/utils.js.map +1 -0
- package/dist/src/core/increment/completion-validator.d.ts.map +1 -1
- package/dist/src/core/increment/completion-validator.js +32 -0
- package/dist/src/core/increment/completion-validator.js.map +1 -1
- package/dist/src/init/research/types.d.ts +1 -1
- package/dist/src/sync/sync-target-resolver.js.map +1 -1
- package/dist/src/utils/lock-manager.d.ts.map +1 -1
- package/dist/src/utils/lock-manager.js +5 -0
- package/dist/src/utils/lock-manager.js.map +1 -1
- package/dist/src/utils/plugin-copier.d.ts +10 -0
- package/dist/src/utils/plugin-copier.d.ts.map +1 -1
- package/dist/src/utils/plugin-copier.js +63 -35
- package/dist/src/utils/plugin-copier.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/agents/sw-closer.md +3 -2
- package/plugins/specweave/hooks/hooks.json +10 -10
- package/plugins/specweave/skills/code-reviewer/SKILL.md +180 -16
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-comments.md +83 -0
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-silent-failures.md +19 -0
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-spec-compliance.md +19 -0
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-tests.md +101 -0
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-types.md +20 -0
- package/plugins/specweave/skills/done/SKILL.md +56 -21
- package/plugins/specweave/skills/grill/SKILL.md +1 -1
- package/plugins/specweave/skills/team-lead/agents/reviewer-logic.md +19 -0
- package/plugins/specweave/skills/team-lead/agents/reviewer-performance.md +20 -0
- package/plugins/specweave/skills/team-lead/agents/reviewer-security.md +20 -0
- package/src/templates/CLAUDE.md.template +7 -4
- package/plugins/specweave/hooks/README.md +0 -493
- package/plugins/specweave/hooks/_archive/stop-auto-v4-legacy.sh +0 -1319
- package/plugins/specweave/hooks/lib/common-setup.sh +0 -144
- package/plugins/specweave/hooks/lib/hook-errors.sh +0 -414
- package/plugins/specweave/hooks/lib/migrate-increment-work.sh +0 -245
- package/plugins/specweave/hooks/lib/resolve-package.sh +0 -146
- package/plugins/specweave/hooks/lib/scheduler-startup.sh +0 -135
- package/plugins/specweave/hooks/lib/score-increment.sh +0 -87
- package/plugins/specweave/hooks/lib/sync-spec-content.sh +0 -193
- package/plugins/specweave/hooks/lib/update-active-increment.sh +0 -95
- package/plugins/specweave/hooks/lib/update-status-line.sh +0 -233
- package/plugins/specweave/hooks/lib/validate-spec-status.sh +0 -171
- package/plugins/specweave/hooks/llm-judge-validator.sh +0 -219
- package/plugins/specweave/hooks/log-decision.sh +0 -168
- package/plugins/specweave/hooks/pre-compact.sh +0 -64
- package/plugins/specweave/hooks/startup-health-check.sh +0 -64
- package/plugins/specweave/hooks/stop-auto-v5.sh +0 -276
- package/plugins/specweave/hooks/stop-reflect.sh +0 -336
- package/plugins/specweave/hooks/stop-sync.sh +0 -283
- package/plugins/specweave/hooks/tests/test-auto-context-integration.sh +0 -126
- package/plugins/specweave/hooks/tests/test-stop-auto-enriched.sh +0 -128
- package/plugins/specweave/hooks/universal/dispatcher.mjs +0 -336
- package/plugins/specweave/hooks/universal/fail-fast-wrapper.sh +0 -325
- package/plugins/specweave/hooks/universal/hook-wrapper.cmd +0 -26
- package/plugins/specweave/hooks/universal/hook-wrapper.sh +0 -69
- package/plugins/specweave/hooks/universal/run-hook.sh +0 -20
- package/plugins/specweave/hooks/universal/session-start.cmd +0 -16
- package/plugins/specweave/hooks/universal/session-start.ps1 +0 -16
- package/plugins/specweave/hooks/user-prompt-submit.sh +0 -2550
- package/plugins/specweave/hooks/v2/detectors/lifecycle-detector.sh +0 -87
- package/plugins/specweave/hooks/v2/detectors/us-completion-detector.sh +0 -186
- package/plugins/specweave/hooks/v2/dispatchers/post-tool-use-analytics.sh +0 -83
- package/plugins/specweave/hooks/v2/dispatchers/post-tool-use.sh +0 -447
- package/plugins/specweave/hooks/v2/dispatchers/pre-tool-use.sh +0 -104
- package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +0 -270
- package/plugins/specweave/hooks/v2/guards/completion-guard.sh +0 -14
- package/plugins/specweave/hooks/v2/guards/increment-duplicate-guard.sh +0 -14
- package/plugins/specweave/hooks/v2/guards/increment-existence-guard.sh +0 -240
- package/plugins/specweave/hooks/v2/guards/interview-enforcement-guard.sh +0 -171
- package/plugins/specweave/hooks/v2/guards/metadata-json-guard.sh +0 -14
- package/plugins/specweave/hooks/v2/guards/skill-chain-enforcement-guard.sh +0 -222
- package/plugins/specweave/hooks/v2/guards/spec-template-enforcement-guard.sh +0 -21
- package/plugins/specweave/hooks/v2/guards/spec-validation-guard.sh +0 -14
- package/plugins/specweave/hooks/v2/guards/status-completion-guard.sh +0 -84
- package/plugins/specweave/hooks/v2/guards/task-ac-sync-guard.sh +0 -475
- package/plugins/specweave/hooks/v2/guards/tdd-enforcement-guard.sh +0 -268
- package/plugins/specweave/hooks/v2/handlers/ac-sync-dispatcher.sh +0 -332
- package/plugins/specweave/hooks/v2/handlers/ac-validation-handler.sh +0 -50
- package/plugins/specweave/hooks/v2/handlers/github-sync-handler.sh +0 -347
- package/plugins/specweave/hooks/v2/handlers/living-docs-handler.sh +0 -83
- package/plugins/specweave/hooks/v2/handlers/living-specs-handler.sh +0 -268
- package/plugins/specweave/hooks/v2/handlers/project-bridge-handler.sh +0 -104
- package/plugins/specweave/hooks/v2/handlers/status-line-handler.sh +0 -165
- package/plugins/specweave/hooks/v2/handlers/status-update.sh +0 -61
- package/plugins/specweave/hooks/v2/handlers/universal-auto-create-dispatcher.sh +0 -270
- package/plugins/specweave/hooks/v2/integrations/ado-post-living-docs-update.sh +0 -367
- package/plugins/specweave/hooks/v2/integrations/ado-post-task.sh +0 -179
- package/plugins/specweave/hooks/v2/integrations/github-auto-create-handler.sh +0 -553
- package/plugins/specweave/hooks/v2/integrations/github-post-task.sh +0 -345
- package/plugins/specweave/hooks/v2/integrations/jira-post-task.sh +0 -180
- package/plugins/specweave/hooks/v2/lib/check-provider-enabled.sh +0 -52
- package/plugins/specweave/hooks/v2/queue/enqueue.sh +0 -81
- package/plugins/specweave/hooks/v2/session-end.sh +0 -139
- package/plugins/specweave/hooks/validate-skill-activations.sh +0 -227
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# common-setup.sh - Minimal Hook Library
|
|
3
|
-
#
|
|
4
|
-
# Only essential utilities for hooks. Crash prevention is handled by:
|
|
5
|
-
# - Claude Code's native tool timeouts
|
|
6
|
-
# - fail-fast-wrapper.sh (5s timeout for hook scripts)
|
|
7
|
-
#
|
|
8
|
-
# USAGE:
|
|
9
|
-
# source "${CLAUDE_PLUGIN_ROOT}/hooks/lib/common-setup.sh"
|
|
10
|
-
|
|
11
|
-
set +e # NEVER use set -e in hooks
|
|
12
|
-
|
|
13
|
-
# ============================================================================
|
|
14
|
-
# PROJECT ROOT DETECTION
|
|
15
|
-
# ============================================================================
|
|
16
|
-
|
|
17
|
-
find_project_root() {
|
|
18
|
-
local dir="${1:-$(pwd)}"
|
|
19
|
-
while [[ "$dir" != "/" ]]; do
|
|
20
|
-
if [[ -f "$dir/.specweave/config.json" ]]; then
|
|
21
|
-
echo "$dir"
|
|
22
|
-
return 0
|
|
23
|
-
fi
|
|
24
|
-
dir=$(dirname "$dir")
|
|
25
|
-
done
|
|
26
|
-
return 1
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
# ============================================================================
|
|
30
|
-
# SPECWEAVE INITIALIZATION GUARD
|
|
31
|
-
# ============================================================================
|
|
32
|
-
# CRITICAL RULE: .specweave/ folders must ONLY exist in project roots where
|
|
33
|
-
# `specweave init` was explicitly run. This function validates that config.json
|
|
34
|
-
# exists, proving explicit initialization.
|
|
35
|
-
#
|
|
36
|
-
# Usage: if is_specweave_initialized "$project_root"; then ... fi
|
|
37
|
-
# ============================================================================
|
|
38
|
-
|
|
39
|
-
is_specweave_initialized() {
|
|
40
|
-
local project_root="${1:-$(pwd)}"
|
|
41
|
-
local config_file="$project_root/.specweave/config.json"
|
|
42
|
-
[[ -f "$config_file" ]]
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
# Find the nearest initialized SpecWeave project root
|
|
46
|
-
# Returns empty string if not found (use with: root=$(find_initialized_specweave_root) && ...)
|
|
47
|
-
find_initialized_specweave_root() {
|
|
48
|
-
local dir="${1:-$(pwd)}"
|
|
49
|
-
while [[ "$dir" != "/" ]]; do
|
|
50
|
-
if is_specweave_initialized "$dir"; then
|
|
51
|
-
echo "$dir"
|
|
52
|
-
return 0
|
|
53
|
-
fi
|
|
54
|
-
dir=$(dirname "$dir")
|
|
55
|
-
done
|
|
56
|
-
return 1
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
# Ensure a directory exists ONLY if SpecWeave is initialized
|
|
60
|
-
# Returns 1 (failure) if not initialized - do NOT create directories in non-projects!
|
|
61
|
-
ensure_specweave_dir() {
|
|
62
|
-
local dir_path="$1"
|
|
63
|
-
local project_root="${2:-}"
|
|
64
|
-
|
|
65
|
-
# Auto-detect project root if not provided
|
|
66
|
-
if [[ -z "$project_root" ]]; then
|
|
67
|
-
project_root=$(find_initialized_specweave_root) || return 1
|
|
68
|
-
fi
|
|
69
|
-
|
|
70
|
-
if ! is_specweave_initialized "$project_root"; then
|
|
71
|
-
return 1 # NOT initialized - refuse to create directories
|
|
72
|
-
fi
|
|
73
|
-
|
|
74
|
-
mkdir -p "$dir_path" 2>/dev/null
|
|
75
|
-
return $?
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
# ============================================================================
|
|
79
|
-
# KILL SWITCH
|
|
80
|
-
# ============================================================================
|
|
81
|
-
|
|
82
|
-
check_kill_switch() {
|
|
83
|
-
[[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]]
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
# ============================================================================
|
|
87
|
-
# PRE-TOOL-USE GUARD HELPERS
|
|
88
|
-
# ============================================================================
|
|
89
|
-
|
|
90
|
-
# Global variables set by init_pretool_guard
|
|
91
|
-
HOOK_INPUT=""
|
|
92
|
-
HOOK_TOOL_NAME=""
|
|
93
|
-
HOOK_FILE_PATH=""
|
|
94
|
-
HOOK_CONTENT=""
|
|
95
|
-
|
|
96
|
-
# Initialize PreToolUse guard environment
|
|
97
|
-
# Returns 1 if hook should exit early (kill switch, no jq, etc.)
|
|
98
|
-
init_pretool_guard() {
|
|
99
|
-
if [[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]]; then
|
|
100
|
-
echo '{"decision":"allow"}'
|
|
101
|
-
return 1
|
|
102
|
-
fi
|
|
103
|
-
|
|
104
|
-
HOOK_INPUT=$(cat 2>/dev/null || echo '{}')
|
|
105
|
-
|
|
106
|
-
if ! command -v jq >/dev/null 2>&1; then
|
|
107
|
-
echo '{"decision":"allow"}'
|
|
108
|
-
return 1
|
|
109
|
-
fi
|
|
110
|
-
|
|
111
|
-
HOOK_TOOL_NAME=$(echo "$HOOK_INPUT" | jq -r '.tool_name // ""' 2>/dev/null || echo "")
|
|
112
|
-
HOOK_FILE_PATH=$(echo "$HOOK_INPUT" | jq -r '.tool_input.file_path // .file_path // ""' 2>/dev/null || echo "")
|
|
113
|
-
HOOK_CONTENT=$(echo "$HOOK_INPUT" | jq -r '.tool_input.content // .tool_input.new_string // ""' 2>/dev/null || echo "")
|
|
114
|
-
|
|
115
|
-
return 0
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
# Allow the tool call with optional message
|
|
119
|
-
guard_allow() {
|
|
120
|
-
local message="${1:-}"
|
|
121
|
-
if [[ -n "$message" ]]; then
|
|
122
|
-
printf '{"decision":"allow","message":"%s"}\n' "$message"
|
|
123
|
-
else
|
|
124
|
-
echo '{"decision":"allow"}'
|
|
125
|
-
fi
|
|
126
|
-
exit 0
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
# ============================================================================
|
|
130
|
-
# SIMPLE LOGGING (optional, writes to .specweave/logs/hooks.log)
|
|
131
|
-
# ============================================================================
|
|
132
|
-
|
|
133
|
-
log_hook() {
|
|
134
|
-
local level="$1"
|
|
135
|
-
shift
|
|
136
|
-
local msg="$*"
|
|
137
|
-
local project_root
|
|
138
|
-
# Use find_initialized_specweave_root to ensure we only log to initialized projects
|
|
139
|
-
project_root=$(find_initialized_specweave_root) || return
|
|
140
|
-
local log_file="$project_root/.specweave/logs/hooks.log"
|
|
141
|
-
# Use ensure_specweave_dir to create logs directory only if initialized
|
|
142
|
-
ensure_specweave_dir "$(dirname "$log_file")" "$project_root" || return
|
|
143
|
-
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [$level] $msg" >> "$log_file" 2>/dev/null
|
|
144
|
-
}
|
|
@@ -1,414 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# hook-errors.sh - Centralized hook error logging and warning output
|
|
3
|
-
#
|
|
4
|
-
# PRINCIPLE: Hooks MUST NEVER block operations. All errors become warnings.
|
|
5
|
-
#
|
|
6
|
-
# Usage:
|
|
7
|
-
# source "$HOOK_DIR/lib/hook-errors.sh"
|
|
8
|
-
# log_hook_warning "task-ac-sync" "Pattern not found in spec.md"
|
|
9
|
-
# log_hook_error "metadata-guard" "JSON parse failed" "$details"
|
|
10
|
-
#
|
|
11
|
-
# Environment:
|
|
12
|
-
# SPECWEAVE_HOOK_VERBOSE=1 - Show all warnings to user
|
|
13
|
-
# SPECWEAVE_HOOK_DEBUG=1 - Log debug info to files
|
|
14
|
-
#
|
|
15
|
-
# All functions are non-blocking and always return 0
|
|
16
|
-
|
|
17
|
-
set +e
|
|
18
|
-
|
|
19
|
-
# ============================================================================
|
|
20
|
-
# Configuration
|
|
21
|
-
# ============================================================================
|
|
22
|
-
|
|
23
|
-
HOOK_ERRORS_VERSION="1.0.0"
|
|
24
|
-
|
|
25
|
-
# Find project root (CRITICAL: must NOT fallback to pwd!)
|
|
26
|
-
_find_hook_project_root() {
|
|
27
|
-
local dir="${1:-$PWD}"
|
|
28
|
-
while [[ "$dir" != "/" ]]; do
|
|
29
|
-
if [[ -f "$dir/.specweave/config.json" ]]; then
|
|
30
|
-
echo "$dir"
|
|
31
|
-
return 0
|
|
32
|
-
fi
|
|
33
|
-
dir=$(dirname "$dir")
|
|
34
|
-
done
|
|
35
|
-
# Return empty string - NOT pwd (prevents .specweave pollution)
|
|
36
|
-
return 1
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
_HOOK_PROJECT_ROOT=$(_find_hook_project_root)
|
|
40
|
-
|
|
41
|
-
# Exit early if not a SpecWeave project (prevents .specweave pollution)
|
|
42
|
-
if [[ -z "$_HOOK_PROJECT_ROOT" ]] || [[ ! -f "$_HOOK_PROJECT_ROOT/.specweave/config.json" ]]; then
|
|
43
|
-
# Define no-op functions so sourcing scripts don't fail
|
|
44
|
-
log_hook_warning() { :; }
|
|
45
|
-
log_hook_error() { :; }
|
|
46
|
-
log_hook_debug() { :; }
|
|
47
|
-
reset_hook_errors() { :; }
|
|
48
|
-
count_hook_errors() { echo "0"; }
|
|
49
|
-
output_hook_warnings() { :; }
|
|
50
|
-
return 0 2>/dev/null || exit 0
|
|
51
|
-
fi
|
|
52
|
-
|
|
53
|
-
_HOOK_LOGS_DIR="$_HOOK_PROJECT_ROOT/.specweave/logs"
|
|
54
|
-
_HOOK_STATE_DIR="$_HOOK_PROJECT_ROOT/.specweave/state"
|
|
55
|
-
|
|
56
|
-
# Ensure directories exist (safe - project root validated above)
|
|
57
|
-
mkdir -p "$_HOOK_LOGS_DIR" 2>/dev/null || true
|
|
58
|
-
mkdir -p "$_HOOK_STATE_DIR" 2>/dev/null || true
|
|
59
|
-
|
|
60
|
-
# Log files
|
|
61
|
-
_HOOK_ERROR_LOG="$_HOOK_LOGS_DIR/hook-errors.log"
|
|
62
|
-
_HOOK_WARNING_LOG="$_HOOK_LOGS_DIR/hook-warnings.log"
|
|
63
|
-
_HOOK_DEBUG_LOG="$_HOOK_LOGS_DIR/hook-debug.log"
|
|
64
|
-
|
|
65
|
-
# ============================================================================
|
|
66
|
-
# Internal Helpers
|
|
67
|
-
# ============================================================================
|
|
68
|
-
|
|
69
|
-
_timestamp() {
|
|
70
|
-
date '+%Y-%m-%d %H:%M:%S'
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
_rotate_log_if_needed() {
|
|
74
|
-
local log_file="$1"
|
|
75
|
-
local max_size="${2:-1048576}" # 1MB default
|
|
76
|
-
|
|
77
|
-
if [[ -f "$log_file" ]]; then
|
|
78
|
-
local size=$(stat -f%z "$log_file" 2>/dev/null || stat -c%s "$log_file" 2>/dev/null || echo 0)
|
|
79
|
-
if [[ "$size" -gt "$max_size" ]]; then
|
|
80
|
-
mv "$log_file" "${log_file}.old" 2>/dev/null || true
|
|
81
|
-
fi
|
|
82
|
-
fi
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
# ============================================================================
|
|
86
|
-
# Public API: Logging Functions
|
|
87
|
-
# ============================================================================
|
|
88
|
-
|
|
89
|
-
# Log a warning - operation continues, user sees message if VERBOSE=1
|
|
90
|
-
# Usage: log_hook_warning "hook-name" "message"
|
|
91
|
-
log_hook_warning() {
|
|
92
|
-
local hook_name="${1:-unknown}"
|
|
93
|
-
local message="${2:-No message}"
|
|
94
|
-
local timestamp=$(_timestamp)
|
|
95
|
-
|
|
96
|
-
# Always log to file
|
|
97
|
-
echo "[$timestamp] WARNING [$hook_name]: $message" >> "$_HOOK_WARNING_LOG" 2>/dev/null || true
|
|
98
|
-
|
|
99
|
-
# Show to user if verbose mode
|
|
100
|
-
if [[ "${SPECWEAVE_HOOK_VERBOSE:-0}" == "1" ]]; then
|
|
101
|
-
echo ""
|
|
102
|
-
echo " WARNING [$hook_name]: $message"
|
|
103
|
-
echo ""
|
|
104
|
-
fi
|
|
105
|
-
|
|
106
|
-
return 0
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
# Log an error - operation continues, but this is significant
|
|
110
|
-
# Usage: log_hook_error "hook-name" "message" ["details"]
|
|
111
|
-
log_hook_error() {
|
|
112
|
-
local hook_name="${1:-unknown}"
|
|
113
|
-
local message="${2:-No message}"
|
|
114
|
-
local details="${3:-}"
|
|
115
|
-
local timestamp=$(_timestamp)
|
|
116
|
-
|
|
117
|
-
# Rotate log if too large
|
|
118
|
-
_rotate_log_if_needed "$_HOOK_ERROR_LOG"
|
|
119
|
-
|
|
120
|
-
# Always log to file with details
|
|
121
|
-
{
|
|
122
|
-
echo "============================================================"
|
|
123
|
-
echo "[$timestamp] ERROR [$hook_name]"
|
|
124
|
-
echo "Message: $message"
|
|
125
|
-
if [[ -n "$details" ]]; then
|
|
126
|
-
echo "Details: $details"
|
|
127
|
-
fi
|
|
128
|
-
echo "============================================================"
|
|
129
|
-
} >> "$_HOOK_ERROR_LOG" 2>/dev/null || true
|
|
130
|
-
|
|
131
|
-
# Always show errors to user (they're significant)
|
|
132
|
-
echo ""
|
|
133
|
-
echo " [Hook Warning] $hook_name: $message"
|
|
134
|
-
if [[ -n "$details" ]] && [[ "${SPECWEAVE_HOOK_DEBUG:-0}" == "1" ]]; then
|
|
135
|
-
echo " Details: $details"
|
|
136
|
-
fi
|
|
137
|
-
echo " (Operation continues - hook errors are non-blocking)"
|
|
138
|
-
echo ""
|
|
139
|
-
|
|
140
|
-
return 0
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
# Log debug info - only to file if DEBUG=1
|
|
144
|
-
# Usage: log_hook_debug "hook-name" "message"
|
|
145
|
-
log_hook_debug() {
|
|
146
|
-
local hook_name="${1:-unknown}"
|
|
147
|
-
local message="${2:-}"
|
|
148
|
-
|
|
149
|
-
if [[ "${SPECWEAVE_HOOK_DEBUG:-0}" == "1" ]]; then
|
|
150
|
-
local timestamp=$(_timestamp)
|
|
151
|
-
_rotate_log_if_needed "$_HOOK_DEBUG_LOG"
|
|
152
|
-
echo "[$timestamp] DEBUG [$hook_name]: $message" >> "$_HOOK_DEBUG_LOG" 2>/dev/null || true
|
|
153
|
-
fi
|
|
154
|
-
|
|
155
|
-
return 0
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
# Log hook start - for tracking which hooks ran
|
|
159
|
-
# Usage: log_hook_start "hook-name" ["context"]
|
|
160
|
-
log_hook_start() {
|
|
161
|
-
local hook_name="${1:-unknown}"
|
|
162
|
-
local context="${2:-}"
|
|
163
|
-
|
|
164
|
-
log_hook_debug "$hook_name" "START ${context:+- $context}"
|
|
165
|
-
return 0
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
# Log hook end - with success/failure status
|
|
169
|
-
# Usage: log_hook_end "hook-name" "success|failure" ["message"]
|
|
170
|
-
log_hook_end() {
|
|
171
|
-
local hook_name="${1:-unknown}"
|
|
172
|
-
local status="${2:-success}"
|
|
173
|
-
local message="${3:-}"
|
|
174
|
-
|
|
175
|
-
log_hook_debug "$hook_name" "END ($status) ${message:+- $message}"
|
|
176
|
-
return 0
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
# ============================================================================
|
|
180
|
-
# Public API: Safe Execution Helpers
|
|
181
|
-
# ============================================================================
|
|
182
|
-
|
|
183
|
-
# Run a command safely, logging errors but never failing
|
|
184
|
-
# Usage: safe_run "hook-name" command arg1 arg2...
|
|
185
|
-
safe_run() {
|
|
186
|
-
local hook_name="${1:-unknown}"
|
|
187
|
-
shift
|
|
188
|
-
|
|
189
|
-
local output
|
|
190
|
-
local exit_code
|
|
191
|
-
|
|
192
|
-
output=$("$@" 2>&1)
|
|
193
|
-
exit_code=$?
|
|
194
|
-
|
|
195
|
-
if [[ $exit_code -ne 0 ]]; then
|
|
196
|
-
log_hook_warning "$hook_name" "Command failed (exit $exit_code): $*"
|
|
197
|
-
log_hook_debug "$hook_name" "Output: $output"
|
|
198
|
-
fi
|
|
199
|
-
|
|
200
|
-
return 0 # Always succeed
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
# Run a file modification safely with backup
|
|
204
|
-
# Usage: safe_file_modify "hook-name" "file" "command with \$file placeholder"
|
|
205
|
-
safe_file_modify() {
|
|
206
|
-
local hook_name="${1:-unknown}"
|
|
207
|
-
local file="$2"
|
|
208
|
-
local command="$3"
|
|
209
|
-
|
|
210
|
-
if [[ ! -f "$file" ]]; then
|
|
211
|
-
log_hook_warning "$hook_name" "File not found: $file"
|
|
212
|
-
return 0
|
|
213
|
-
fi
|
|
214
|
-
|
|
215
|
-
# Create backup
|
|
216
|
-
local backup="${file}.hook-backup"
|
|
217
|
-
cp "$file" "$backup" 2>/dev/null || {
|
|
218
|
-
log_hook_warning "$hook_name" "Failed to create backup of $file"
|
|
219
|
-
return 0
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
# Run command (replace $file placeholder)
|
|
223
|
-
local actual_command="${command//\$file/$file}"
|
|
224
|
-
if ! eval "$actual_command" 2>/dev/null; then
|
|
225
|
-
log_hook_warning "$hook_name" "Modification failed, restoring backup"
|
|
226
|
-
mv "$backup" "$file" 2>/dev/null || true
|
|
227
|
-
return 0
|
|
228
|
-
fi
|
|
229
|
-
|
|
230
|
-
# Verify file is valid (not empty, not truncated)
|
|
231
|
-
if [[ ! -s "$file" ]]; then
|
|
232
|
-
log_hook_error "$hook_name" "File became empty after modification, restoring"
|
|
233
|
-
mv "$backup" "$file" 2>/dev/null || true
|
|
234
|
-
return 0
|
|
235
|
-
fi
|
|
236
|
-
|
|
237
|
-
# Success - remove backup
|
|
238
|
-
rm -f "$backup" 2>/dev/null || true
|
|
239
|
-
return 0
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
# ============================================================================
|
|
243
|
-
# Public API: Pattern Matching Helpers
|
|
244
|
-
# ============================================================================
|
|
245
|
-
|
|
246
|
-
# Check if a pattern exists in file (with logging)
|
|
247
|
-
# Usage: if pattern_exists "hook-name" "file" "pattern"; then ...
|
|
248
|
-
pattern_exists() {
|
|
249
|
-
local hook_name="${1:-unknown}"
|
|
250
|
-
local file="$2"
|
|
251
|
-
local pattern="$3"
|
|
252
|
-
|
|
253
|
-
if [[ ! -f "$file" ]]; then
|
|
254
|
-
log_hook_debug "$hook_name" "pattern_exists: file not found: $file"
|
|
255
|
-
return 1
|
|
256
|
-
fi
|
|
257
|
-
|
|
258
|
-
if grep -qE "$pattern" "$file" 2>/dev/null; then
|
|
259
|
-
log_hook_debug "$hook_name" "pattern_exists: found '$pattern' in $file"
|
|
260
|
-
return 0
|
|
261
|
-
else
|
|
262
|
-
log_hook_debug "$hook_name" "pattern_exists: NOT found '$pattern' in $file"
|
|
263
|
-
return 1
|
|
264
|
-
fi
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
# Safe sed replacement with pattern existence check
|
|
268
|
-
# Usage: safe_sed_replace "hook-name" "file" "pattern" "replacement"
|
|
269
|
-
safe_sed_replace() {
|
|
270
|
-
local hook_name="${1:-unknown}"
|
|
271
|
-
local file="$2"
|
|
272
|
-
local pattern="$3"
|
|
273
|
-
local replacement="$4"
|
|
274
|
-
|
|
275
|
-
if [[ ! -f "$file" ]]; then
|
|
276
|
-
log_hook_warning "$hook_name" "Cannot replace: file not found: $file"
|
|
277
|
-
return 0
|
|
278
|
-
fi
|
|
279
|
-
|
|
280
|
-
# Check if pattern exists first
|
|
281
|
-
if ! grep -qE "$pattern" "$file" 2>/dev/null; then
|
|
282
|
-
log_hook_debug "$hook_name" "Pattern not found (may already be updated): $pattern"
|
|
283
|
-
return 0 # Not an error - pattern may have been updated already
|
|
284
|
-
fi
|
|
285
|
-
|
|
286
|
-
# Try macOS sed first, then Linux
|
|
287
|
-
if sed -i '' "s|$pattern|$replacement|g" "$file" 2>/dev/null; then
|
|
288
|
-
log_hook_debug "$hook_name" "Replaced pattern (macOS sed)"
|
|
289
|
-
return 0
|
|
290
|
-
elif sed -i "s|$pattern|$replacement|g" "$file" 2>/dev/null; then
|
|
291
|
-
log_hook_debug "$hook_name" "Replaced pattern (Linux sed)"
|
|
292
|
-
return 0
|
|
293
|
-
else
|
|
294
|
-
log_hook_warning "$hook_name" "sed replacement failed for pattern: $pattern"
|
|
295
|
-
return 0 # Still don't fail
|
|
296
|
-
fi
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
# ============================================================================
|
|
300
|
-
# Public API: JSON Helpers
|
|
301
|
-
# ============================================================================
|
|
302
|
-
|
|
303
|
-
# Safe JSON read with fallback
|
|
304
|
-
# Usage: value=$(safe_json_get "hook-name" "file.json" ".key")
|
|
305
|
-
safe_json_get() {
|
|
306
|
-
local hook_name="${1:-unknown}"
|
|
307
|
-
local file="$2"
|
|
308
|
-
local key="$3"
|
|
309
|
-
local default="${4:-}"
|
|
310
|
-
|
|
311
|
-
if [[ ! -f "$file" ]]; then
|
|
312
|
-
echo "$default"
|
|
313
|
-
return 0
|
|
314
|
-
fi
|
|
315
|
-
|
|
316
|
-
if ! command -v jq >/dev/null 2>&1; then
|
|
317
|
-
log_hook_debug "$hook_name" "jq not available, returning default"
|
|
318
|
-
echo "$default"
|
|
319
|
-
return 0
|
|
320
|
-
fi
|
|
321
|
-
|
|
322
|
-
local value
|
|
323
|
-
value=$(jq -r "$key // empty" "$file" 2>/dev/null)
|
|
324
|
-
|
|
325
|
-
if [[ -z "$value" ]] || [[ "$value" == "null" ]]; then
|
|
326
|
-
echo "$default"
|
|
327
|
-
else
|
|
328
|
-
echo "$value"
|
|
329
|
-
fi
|
|
330
|
-
|
|
331
|
-
return 0
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
# Safe JSON update
|
|
335
|
-
# Usage: safe_json_set "hook-name" "file.json" ".key" "value"
|
|
336
|
-
safe_json_set() {
|
|
337
|
-
local hook_name="${1:-unknown}"
|
|
338
|
-
local file="$2"
|
|
339
|
-
local key="$3"
|
|
340
|
-
local value="$4"
|
|
341
|
-
|
|
342
|
-
if [[ ! -f "$file" ]]; then
|
|
343
|
-
log_hook_warning "$hook_name" "Cannot update JSON: file not found: $file"
|
|
344
|
-
return 0
|
|
345
|
-
fi
|
|
346
|
-
|
|
347
|
-
if ! command -v jq >/dev/null 2>&1; then
|
|
348
|
-
log_hook_warning "$hook_name" "jq not available, skipping JSON update"
|
|
349
|
-
return 0
|
|
350
|
-
fi
|
|
351
|
-
|
|
352
|
-
local tmp_file
|
|
353
|
-
tmp_file=$(mktemp)
|
|
354
|
-
|
|
355
|
-
if jq "$key = $value" "$file" > "$tmp_file" 2>/dev/null && [[ -s "$tmp_file" ]]; then
|
|
356
|
-
mv "$tmp_file" "$file" 2>/dev/null || {
|
|
357
|
-
log_hook_warning "$hook_name" "Failed to write JSON update"
|
|
358
|
-
rm -f "$tmp_file"
|
|
359
|
-
}
|
|
360
|
-
else
|
|
361
|
-
log_hook_warning "$hook_name" "jq update failed for $key"
|
|
362
|
-
rm -f "$tmp_file"
|
|
363
|
-
fi
|
|
364
|
-
|
|
365
|
-
return 0
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
# ============================================================================
|
|
369
|
-
# Public API: Output Helpers
|
|
370
|
-
# ============================================================================
|
|
371
|
-
|
|
372
|
-
# Output a visible warning banner to user
|
|
373
|
-
# Usage: show_hook_warning_banner "title" "message line 1" "message line 2"
|
|
374
|
-
show_hook_warning_banner() {
|
|
375
|
-
local title="$1"
|
|
376
|
-
shift
|
|
377
|
-
|
|
378
|
-
echo ""
|
|
379
|
-
echo " WARNING: $title"
|
|
380
|
-
echo " ----------------------------------------"
|
|
381
|
-
for line in "$@"; do
|
|
382
|
-
echo " $line"
|
|
383
|
-
done
|
|
384
|
-
echo " ----------------------------------------"
|
|
385
|
-
echo ""
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
# Output a non-blocking info message
|
|
389
|
-
# Usage: show_hook_info "Operation completed with warnings"
|
|
390
|
-
show_hook_info() {
|
|
391
|
-
local message="$1"
|
|
392
|
-
echo ""
|
|
393
|
-
echo " [Info] $message"
|
|
394
|
-
echo ""
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
# ============================================================================
|
|
398
|
-
# Initialization
|
|
399
|
-
# ============================================================================
|
|
400
|
-
|
|
401
|
-
# Export all functions for use in subshells
|
|
402
|
-
export -f log_hook_warning 2>/dev/null || true
|
|
403
|
-
export -f log_hook_error 2>/dev/null || true
|
|
404
|
-
export -f log_hook_debug 2>/dev/null || true
|
|
405
|
-
export -f log_hook_start 2>/dev/null || true
|
|
406
|
-
export -f log_hook_end 2>/dev/null || true
|
|
407
|
-
export -f safe_run 2>/dev/null || true
|
|
408
|
-
export -f safe_file_modify 2>/dev/null || true
|
|
409
|
-
export -f pattern_exists 2>/dev/null || true
|
|
410
|
-
export -f safe_sed_replace 2>/dev/null || true
|
|
411
|
-
export -f safe_json_get 2>/dev/null || true
|
|
412
|
-
export -f safe_json_set 2>/dev/null || true
|
|
413
|
-
export -f show_hook_warning_banner 2>/dev/null || true
|
|
414
|
-
export -f show_hook_info 2>/dev/null || true
|