specweave 1.0.551 → 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.
Files changed (132) hide show
  1. package/bin/specweave.js +23 -1
  2. package/dist/src/cli/commands/hook.d.ts +15 -0
  3. package/dist/src/cli/commands/hook.d.ts.map +1 -0
  4. package/dist/src/cli/commands/hook.js +61 -0
  5. package/dist/src/cli/commands/hook.js.map +1 -0
  6. package/dist/src/config/types.d.ts +2 -2
  7. package/dist/src/core/hooks/handlers/hook-router.d.ts +19 -0
  8. package/dist/src/core/hooks/handlers/hook-router.d.ts.map +1 -0
  9. package/dist/src/core/hooks/handlers/hook-router.js +75 -0
  10. package/dist/src/core/hooks/handlers/hook-router.js.map +1 -0
  11. package/dist/src/core/hooks/handlers/index.d.ts +10 -0
  12. package/dist/src/core/hooks/handlers/index.d.ts.map +1 -0
  13. package/dist/src/core/hooks/handlers/index.js +9 -0
  14. package/dist/src/core/hooks/handlers/index.js.map +1 -0
  15. package/dist/src/core/hooks/handlers/post-tool-use-analytics.d.ts +11 -0
  16. package/dist/src/core/hooks/handlers/post-tool-use-analytics.d.ts.map +1 -0
  17. package/dist/src/core/hooks/handlers/post-tool-use-analytics.js +73 -0
  18. package/dist/src/core/hooks/handlers/post-tool-use-analytics.js.map +1 -0
  19. package/dist/src/core/hooks/handlers/post-tool-use.d.ts +11 -0
  20. package/dist/src/core/hooks/handlers/post-tool-use.d.ts.map +1 -0
  21. package/dist/src/core/hooks/handlers/post-tool-use.js +76 -0
  22. package/dist/src/core/hooks/handlers/post-tool-use.js.map +1 -0
  23. package/dist/src/core/hooks/handlers/pre-compact.d.ts +11 -0
  24. package/dist/src/core/hooks/handlers/pre-compact.d.ts.map +1 -0
  25. package/dist/src/core/hooks/handlers/pre-compact.js +77 -0
  26. package/dist/src/core/hooks/handlers/pre-compact.js.map +1 -0
  27. package/dist/src/core/hooks/handlers/pre-tool-use.d.ts +11 -0
  28. package/dist/src/core/hooks/handlers/pre-tool-use.d.ts.map +1 -0
  29. package/dist/src/core/hooks/handlers/pre-tool-use.js +318 -0
  30. package/dist/src/core/hooks/handlers/pre-tool-use.js.map +1 -0
  31. package/dist/src/core/hooks/handlers/session-start.d.ts +9 -0
  32. package/dist/src/core/hooks/handlers/session-start.d.ts.map +1 -0
  33. package/dist/src/core/hooks/handlers/session-start.js +111 -0
  34. package/dist/src/core/hooks/handlers/session-start.js.map +1 -0
  35. package/dist/src/core/hooks/handlers/stop-auto.d.ts +16 -0
  36. package/dist/src/core/hooks/handlers/stop-auto.d.ts.map +1 -0
  37. package/dist/src/core/hooks/handlers/stop-auto.js +122 -0
  38. package/dist/src/core/hooks/handlers/stop-auto.js.map +1 -0
  39. package/dist/src/core/hooks/handlers/stop-reflect.d.ts +14 -0
  40. package/dist/src/core/hooks/handlers/stop-reflect.d.ts.map +1 -0
  41. package/dist/src/core/hooks/handlers/stop-reflect.js +43 -0
  42. package/dist/src/core/hooks/handlers/stop-reflect.js.map +1 -0
  43. package/dist/src/core/hooks/handlers/stop-sync.d.ts +15 -0
  44. package/dist/src/core/hooks/handlers/stop-sync.d.ts.map +1 -0
  45. package/dist/src/core/hooks/handlers/stop-sync.js +68 -0
  46. package/dist/src/core/hooks/handlers/stop-sync.js.map +1 -0
  47. package/dist/src/core/hooks/handlers/types.d.ts +63 -0
  48. package/dist/src/core/hooks/handlers/types.d.ts.map +1 -0
  49. package/dist/src/core/hooks/handlers/types.js +27 -0
  50. package/dist/src/core/hooks/handlers/types.js.map +1 -0
  51. package/dist/src/core/hooks/handlers/user-prompt-submit.d.ts +14 -0
  52. package/dist/src/core/hooks/handlers/user-prompt-submit.d.ts.map +1 -0
  53. package/dist/src/core/hooks/handlers/user-prompt-submit.js +173 -0
  54. package/dist/src/core/hooks/handlers/user-prompt-submit.js.map +1 -0
  55. package/dist/src/core/hooks/handlers/utils.d.ts +25 -0
  56. package/dist/src/core/hooks/handlers/utils.d.ts.map +1 -0
  57. package/dist/src/core/hooks/handlers/utils.js +64 -0
  58. package/dist/src/core/hooks/handlers/utils.js.map +1 -0
  59. package/dist/src/init/research/types.d.ts +1 -1
  60. package/dist/src/sync/sync-target-resolver.js.map +1 -1
  61. package/dist/src/utils/lock-manager.d.ts.map +1 -1
  62. package/dist/src/utils/lock-manager.js +5 -0
  63. package/dist/src/utils/lock-manager.js.map +1 -1
  64. package/dist/src/utils/plugin-copier.d.ts.map +1 -1
  65. package/dist/src/utils/plugin-copier.js +3 -30
  66. package/dist/src/utils/plugin-copier.js.map +1 -1
  67. package/package.json +1 -1
  68. package/plugins/specweave/hooks/hooks.json +10 -10
  69. package/plugins/specweave/hooks/README.md +0 -493
  70. package/plugins/specweave/hooks/_archive/stop-auto-v4-legacy.sh +0 -1319
  71. package/plugins/specweave/hooks/lib/common-setup.sh +0 -144
  72. package/plugins/specweave/hooks/lib/hook-errors.sh +0 -414
  73. package/plugins/specweave/hooks/lib/migrate-increment-work.sh +0 -245
  74. package/plugins/specweave/hooks/lib/resolve-package.sh +0 -146
  75. package/plugins/specweave/hooks/lib/scheduler-startup.sh +0 -135
  76. package/plugins/specweave/hooks/lib/score-increment.sh +0 -87
  77. package/plugins/specweave/hooks/lib/sync-spec-content.sh +0 -193
  78. package/plugins/specweave/hooks/lib/update-active-increment.sh +0 -95
  79. package/plugins/specweave/hooks/lib/update-status-line.sh +0 -233
  80. package/plugins/specweave/hooks/lib/validate-spec-status.sh +0 -171
  81. package/plugins/specweave/hooks/llm-judge-validator.sh +0 -219
  82. package/plugins/specweave/hooks/log-decision.sh +0 -168
  83. package/plugins/specweave/hooks/pre-compact.sh +0 -64
  84. package/plugins/specweave/hooks/startup-health-check.sh +0 -64
  85. package/plugins/specweave/hooks/stop-auto-v5.sh +0 -276
  86. package/plugins/specweave/hooks/stop-reflect.sh +0 -336
  87. package/plugins/specweave/hooks/stop-sync.sh +0 -283
  88. package/plugins/specweave/hooks/tests/test-auto-context-integration.sh +0 -126
  89. package/plugins/specweave/hooks/tests/test-stop-auto-enriched.sh +0 -128
  90. package/plugins/specweave/hooks/universal/dispatcher.mjs +0 -336
  91. package/plugins/specweave/hooks/universal/fail-fast-wrapper.sh +0 -325
  92. package/plugins/specweave/hooks/universal/hook-wrapper.cmd +0 -26
  93. package/plugins/specweave/hooks/universal/hook-wrapper.sh +0 -69
  94. package/plugins/specweave/hooks/universal/run-hook.sh +0 -20
  95. package/plugins/specweave/hooks/universal/session-start.cmd +0 -16
  96. package/plugins/specweave/hooks/universal/session-start.ps1 +0 -16
  97. package/plugins/specweave/hooks/user-prompt-submit.sh +0 -2550
  98. package/plugins/specweave/hooks/v2/detectors/lifecycle-detector.sh +0 -87
  99. package/plugins/specweave/hooks/v2/detectors/us-completion-detector.sh +0 -186
  100. package/plugins/specweave/hooks/v2/dispatchers/post-tool-use-analytics.sh +0 -83
  101. package/plugins/specweave/hooks/v2/dispatchers/post-tool-use.sh +0 -447
  102. package/plugins/specweave/hooks/v2/dispatchers/pre-tool-use.sh +0 -104
  103. package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +0 -270
  104. package/plugins/specweave/hooks/v2/guards/completion-guard.sh +0 -14
  105. package/plugins/specweave/hooks/v2/guards/increment-duplicate-guard.sh +0 -14
  106. package/plugins/specweave/hooks/v2/guards/increment-existence-guard.sh +0 -240
  107. package/plugins/specweave/hooks/v2/guards/interview-enforcement-guard.sh +0 -171
  108. package/plugins/specweave/hooks/v2/guards/metadata-json-guard.sh +0 -14
  109. package/plugins/specweave/hooks/v2/guards/skill-chain-enforcement-guard.sh +0 -222
  110. package/plugins/specweave/hooks/v2/guards/spec-template-enforcement-guard.sh +0 -21
  111. package/plugins/specweave/hooks/v2/guards/spec-validation-guard.sh +0 -14
  112. package/plugins/specweave/hooks/v2/guards/status-completion-guard.sh +0 -84
  113. package/plugins/specweave/hooks/v2/guards/task-ac-sync-guard.sh +0 -475
  114. package/plugins/specweave/hooks/v2/guards/tdd-enforcement-guard.sh +0 -268
  115. package/plugins/specweave/hooks/v2/handlers/ac-sync-dispatcher.sh +0 -332
  116. package/plugins/specweave/hooks/v2/handlers/ac-validation-handler.sh +0 -50
  117. package/plugins/specweave/hooks/v2/handlers/github-sync-handler.sh +0 -347
  118. package/plugins/specweave/hooks/v2/handlers/living-docs-handler.sh +0 -83
  119. package/plugins/specweave/hooks/v2/handlers/living-specs-handler.sh +0 -268
  120. package/plugins/specweave/hooks/v2/handlers/project-bridge-handler.sh +0 -104
  121. package/plugins/specweave/hooks/v2/handlers/status-line-handler.sh +0 -165
  122. package/plugins/specweave/hooks/v2/handlers/status-update.sh +0 -61
  123. package/plugins/specweave/hooks/v2/handlers/universal-auto-create-dispatcher.sh +0 -270
  124. package/plugins/specweave/hooks/v2/integrations/ado-post-living-docs-update.sh +0 -367
  125. package/plugins/specweave/hooks/v2/integrations/ado-post-task.sh +0 -179
  126. package/plugins/specweave/hooks/v2/integrations/github-auto-create-handler.sh +0 -553
  127. package/plugins/specweave/hooks/v2/integrations/github-post-task.sh +0 -345
  128. package/plugins/specweave/hooks/v2/integrations/jira-post-task.sh +0 -180
  129. package/plugins/specweave/hooks/v2/lib/check-provider-enabled.sh +0 -52
  130. package/plugins/specweave/hooks/v2/queue/enqueue.sh +0 -81
  131. package/plugins/specweave/hooks/v2/session-end.sh +0 -139
  132. 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