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