@paulduvall/claude-dev-toolkit 0.0.1-alpha.2 → 0.0.1-alpha.21

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 (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +88 -37
  3. package/bin/claude-commands +307 -65
  4. package/commands/active/xarchitecture.md +393 -0
  5. package/commands/active/xconfig.md +127 -0
  6. package/commands/active/xcontinue.md +92 -0
  7. package/commands/active/xdebug.md +130 -0
  8. package/commands/active/xdocs.md +178 -0
  9. package/commands/active/xexplore.md +94 -0
  10. package/commands/active/xgit.md +149 -0
  11. package/commands/active/xpipeline.md +152 -0
  12. package/commands/active/xquality.md +96 -0
  13. package/commands/active/xrefactor.md +198 -0
  14. package/commands/active/xrelease.md +142 -0
  15. package/commands/active/xsecurity.md +92 -0
  16. package/commands/active/xspec.md +174 -0
  17. package/commands/active/xtdd.md +151 -0
  18. package/commands/active/xtest.md +89 -0
  19. package/commands/active/xverify.md +80 -0
  20. package/commands/experiments/xact.md +742 -0
  21. package/commands/experiments/xanalytics.md +113 -0
  22. package/commands/experiments/xanalyze.md +70 -0
  23. package/commands/experiments/xapi.md +161 -0
  24. package/commands/experiments/xatomic.md +112 -0
  25. package/commands/experiments/xaws.md +85 -0
  26. package/commands/experiments/xcicd.md +337 -0
  27. package/commands/experiments/xcommit.md +122 -0
  28. package/commands/experiments/xcompliance.md +182 -0
  29. package/commands/experiments/xconstraints.md +89 -0
  30. package/commands/experiments/xcoverage.md +90 -0
  31. package/commands/experiments/xdb.md +102 -0
  32. package/commands/experiments/xdesign.md +121 -0
  33. package/commands/experiments/xdevcontainer.md +238 -0
  34. package/commands/experiments/xevaluate.md +111 -0
  35. package/commands/experiments/xfootnote.md +12 -0
  36. package/commands/experiments/xgenerate.md +117 -0
  37. package/commands/experiments/xgovernance.md +149 -0
  38. package/commands/experiments/xgreen.md +66 -0
  39. package/commands/experiments/xiac.md +118 -0
  40. package/commands/experiments/xincident.md +137 -0
  41. package/commands/experiments/xinfra.md +115 -0
  42. package/commands/experiments/xknowledge.md +115 -0
  43. package/commands/experiments/xmaturity.md +120 -0
  44. package/commands/experiments/xmetrics.md +118 -0
  45. package/commands/experiments/xmonitoring.md +128 -0
  46. package/commands/experiments/xnew.md +903 -0
  47. package/commands/experiments/xobservable.md +114 -0
  48. package/commands/experiments/xoidc.md +165 -0
  49. package/commands/experiments/xoptimize.md +115 -0
  50. package/commands/experiments/xperformance.md +112 -0
  51. package/commands/experiments/xplanning.md +131 -0
  52. package/commands/experiments/xpolicy.md +115 -0
  53. package/commands/experiments/xproduct.md +98 -0
  54. package/commands/experiments/xreadiness.md +75 -0
  55. package/commands/experiments/xred.md +55 -0
  56. package/commands/experiments/xrisk.md +128 -0
  57. package/commands/experiments/xrules.md +124 -0
  58. package/commands/experiments/xsandbox.md +120 -0
  59. package/commands/experiments/xscan.md +102 -0
  60. package/commands/experiments/xsetup.md +123 -0
  61. package/commands/experiments/xtemplate.md +116 -0
  62. package/commands/experiments/xtrace.md +212 -0
  63. package/commands/experiments/xux.md +171 -0
  64. package/commands/experiments/xvalidate.md +104 -0
  65. package/commands/experiments/xworkflow.md +113 -0
  66. package/hooks/.smellrc.example.json +19 -0
  67. package/hooks/README.md +263 -0
  68. package/hooks/check-commit-signing.py +127 -0
  69. package/hooks/check-complexity.py +38 -0
  70. package/hooks/check-security.py +37 -0
  71. package/hooks/claude-wrapper.sh +29 -0
  72. package/hooks/config.py +110 -0
  73. package/hooks/file-logger.sh +100 -0
  74. package/hooks/lib/argument-parser.sh +427 -0
  75. package/hooks/lib/config-constants.sh +230 -0
  76. package/hooks/lib/context-manager.sh +560 -0
  77. package/hooks/lib/error-handler.sh +423 -0
  78. package/hooks/lib/execution-engine.sh +444 -0
  79. package/hooks/lib/execution-results.sh +113 -0
  80. package/hooks/lib/execution-simulation.sh +114 -0
  81. package/hooks/lib/field-validators.sh +104 -0
  82. package/hooks/lib/file-utils.sh +398 -0
  83. package/hooks/lib/subagent-discovery.sh +468 -0
  84. package/hooks/lib/subagent-validator.sh +407 -0
  85. package/hooks/lib/validation-reporter.sh +134 -0
  86. package/hooks/on-error-debug.sh +226 -0
  87. package/hooks/pre-commit-quality.sh +204 -0
  88. package/hooks/pre-commit-test-runner.sh +132 -0
  89. package/hooks/pre-write-security.sh +115 -0
  90. package/hooks/prevent-credential-exposure.sh +279 -0
  91. package/hooks/security_bandit.py +177 -0
  92. package/hooks/security_checks.py +97 -0
  93. package/hooks/security_secrets.py +81 -0
  94. package/hooks/security_trojan.py +61 -0
  95. package/hooks/settings.example.json +52 -0
  96. package/hooks/smell_checks.py +238 -0
  97. package/hooks/smell_javascript.py +231 -0
  98. package/hooks/smell_python.py +110 -0
  99. package/hooks/smell_ruff.py +70 -0
  100. package/hooks/smell_types.py +72 -0
  101. package/hooks/subagent-trigger-simple.sh +202 -0
  102. package/hooks/subagent-trigger.sh +253 -0
  103. package/hooks/suppression.py +82 -0
  104. package/hooks/tab-color.sh +70 -0
  105. package/hooks/verify-before-edit.sh +135 -0
  106. package/lib/backup-restore-command.js +140 -0
  107. package/lib/base/base-command.js +252 -0
  108. package/lib/base/command-result.js +184 -0
  109. package/lib/config/constants.js +255 -0
  110. package/lib/config.js +48 -6
  111. package/lib/configure-command.js +428 -0
  112. package/lib/dependency-validator.js +64 -5
  113. package/lib/hook-installer-core.js +2 -2
  114. package/lib/installation-instruction-generator.js +213 -495
  115. package/lib/installer.js +134 -56
  116. package/lib/oidc-command.js +740 -0
  117. package/lib/services/backup-list-service.js +226 -0
  118. package/lib/services/backup-service.js +230 -0
  119. package/lib/services/command-installer-service.js +217 -0
  120. package/lib/services/logger-service.js +201 -0
  121. package/lib/services/package-manager-service.js +319 -0
  122. package/lib/services/platform-instruction-service.js +294 -0
  123. package/lib/services/recovery-instruction-service.js +348 -0
  124. package/lib/services/restore-service.js +221 -0
  125. package/lib/setup-command.js +359 -0
  126. package/lib/setup-wizard.js +155 -262
  127. package/lib/uninstall-command.js +100 -0
  128. package/lib/utils/claude-path-config.js +184 -0
  129. package/lib/utils/file-system-utils.js +152 -0
  130. package/lib/utils.js +8 -4
  131. package/lib/verify-command.js +430 -0
  132. package/package.json +7 -3
  133. package/scripts/postinstall.js +172 -157
  134. package/subagents/debug-specialist.md +7 -0
  135. package/templates/README.md +115 -0
  136. package/templates/basic-settings.json +30 -0
  137. package/templates/comprehensive-settings.json +57 -0
  138. package/templates/global-claude.md +344 -0
  139. package/templates/hybrid-hook-config.yaml +132 -0
  140. package/templates/security-focused-settings.json +62 -0
  141. package/templates/subagent-hooks.yaml +188 -0
  142. package/lib/package-manager-service.js +0 -270
  143. package/subagents/debug-context.md +0 -197
@@ -0,0 +1,72 @@
1
+ """Shared types, thresholds, and helpers for code smell detection."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ # ---------------------------------------------------------------------------
8
+ # Thresholds
9
+ # ---------------------------------------------------------------------------
10
+ MAX_COMPLEXITY = 10
11
+ MAX_FUNCTION_LINES = 20
12
+ MAX_NESTING_DEPTH = 3
13
+ MAX_PARAMETERS = 4
14
+ MAX_FILE_LINES = 300
15
+ DUPLICATE_MIN_LINES = 4
16
+ DUPLICATE_MIN_OCCURRENCES = 2
17
+
18
+ FIXES = {
19
+ "complexity": "Use extract-method, early returns, guard clauses, or lookup tables.",
20
+ "long_function": "Extract helper functions for distinct logical steps.",
21
+ "deep_nesting": "Use guard clauses and early returns to flatten control flow.",
22
+ "too_many_params": "Group related parameters into a dataclass or options object.",
23
+ "duplicate_block": "Extract repeated code into a shared helper function.",
24
+ "long_file": "Split into smaller modules with clear single responsibilities.",
25
+ "secrets": "Move secrets to environment variables or a secrets manager.",
26
+ "B101": "Remove assert from non-test code; use proper validation instead.",
27
+ "B102": "Replace exec/eval with safer alternatives.",
28
+ "B105": "Move hardcoded passwords to environment variables.",
29
+ "B106": "Move hardcoded passwords to environment variables.",
30
+ "B110": "Handle exceptions explicitly instead of using bare except-pass.",
31
+ "B301": "Avoid pickle for untrusted data; use json or safer serialization.",
32
+ "B602": "Avoid shell=True; pass command as a list to subprocess.",
33
+ "trojan_bidi": "Remove Unicode bidi override characters that can disguise code.",
34
+ "trojan_zero_width": "Remove zero-width Unicode characters that hide content.",
35
+ "ruff_lint": "Fix the ruff lint violation manually.",
36
+ }
37
+
38
+
39
+ @dataclass(frozen=True)
40
+ class Smell:
41
+ """A single code-smell violation."""
42
+
43
+ kind: str
44
+ name: str
45
+ line: int
46
+ detail: str
47
+ fix: str
48
+
49
+
50
+ # ---------------------------------------------------------------------------
51
+ # Shared helpers
52
+ # ---------------------------------------------------------------------------
53
+
54
+ _DETAIL_TEMPLATES = {
55
+ "complexity": ("complexity={value} (max {max})", MAX_COMPLEXITY),
56
+ "long_function": ("{value} lines (max {max})", MAX_FUNCTION_LINES),
57
+ "deep_nesting": ("nesting depth {value} (max {max})", MAX_NESTING_DEPTH),
58
+ "too_many_params": ("{value} params (max {max})", MAX_PARAMETERS),
59
+ }
60
+
61
+
62
+ def make_smell(kind: str, name: str, line: int, value: int) -> Smell:
63
+ """Create a Smell with a formatted detail string."""
64
+ tpl, mx = _DETAIL_TEMPLATES[kind]
65
+ return Smell(kind, name, line, tpl.format(value=value, max=mx), FIXES[kind])
66
+
67
+
68
+ def apply_threshold_checks(
69
+ name: str, line: int, checks: list[tuple[str, int, int]],
70
+ ) -> list[Smell]:
71
+ """Return Smell for each (kind, value, threshold) where value > threshold."""
72
+ return [make_smell(k, name, line, v) for k, v, t in checks if v > t]
@@ -0,0 +1,202 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Claude Code Hook: Simple Subagent Trigger
5
+ #
6
+ # Purpose: Lightweight trigger for delegating to any subagent
7
+ # Usage: subagent-trigger-simple.sh <subagent-name> [event-type] [context]
8
+ # Approach: Minimal orchestration - gather context and delegate to subagents
9
+ #
10
+ # This is the simplified version of the original 253-line subagent-trigger.sh,
11
+ # focusing on clean delegation rather than complex orchestration.
12
+
13
+ ##################################
14
+ # Load Shared Libraries
15
+ ##################################
16
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
17
+ LIB_DIR="$SCRIPT_DIR/lib"
18
+
19
+ # Load only essential modules for lightweight operation
20
+ source "$LIB_DIR/config-constants.sh"
21
+ source "$LIB_DIR/file-utils.sh"
22
+ source "$LIB_DIR/context-manager.sh"
23
+ source "$LIB_DIR/error-handler.sh"
24
+
25
+ ##################################
26
+ # Usage Information
27
+ ##################################
28
+ show_usage() {
29
+ cat <<EOF
30
+ Usage: subagent-trigger-simple.sh <subagent-name> [event-type] [additional-context]
31
+
32
+ Lightweight subagent trigger for event-driven AI assistance.
33
+
34
+ Arguments:
35
+ subagent-name Name of the subagent to invoke (required)
36
+ event-type Type of event (default: manual)
37
+ additional-context Additional context information (optional)
38
+
39
+ Examples:
40
+ subagent-trigger-simple.sh security-auditor pre_write
41
+ subagent-trigger-simple.sh style-enforcer pre_commit "Check Python files"
42
+ subagent-trigger-simple.sh debug-specialist on_error "ImportError in main.py"
43
+
44
+ Available Events: ${SUPPORTED_EVENTS[*]}
45
+ EOF
46
+ }
47
+
48
+ ##################################
49
+ # Simple Context Gathering
50
+ ##################################
51
+ gather_simple_context() {
52
+ local subagent_name="$1"
53
+ local event_type="$2"
54
+ local additional_context="$3"
55
+
56
+ # Create lightweight context - much simpler than the original
57
+ local safe_name safe_event safe_ctx safe_user safe_wd safe_branch
58
+ safe_name=$(json_escape "$subagent_name")
59
+ safe_event=$(json_escape "$event_type")
60
+ safe_ctx=$(json_escape "$additional_context")
61
+ safe_user=$(json_escape "$USER")
62
+ safe_wd=$(json_escape "$(pwd)")
63
+ safe_branch=$(json_escape "$(git branch --show-current 2>/dev/null || echo 'not-in-git')")
64
+
65
+ local context_data
66
+ context_data=$(cat <<EOF
67
+ {
68
+ "trigger": "simple_subagent_trigger",
69
+ "subagent": "$safe_name",
70
+ "event": "$safe_event",
71
+ "additional_context": "$safe_ctx",
72
+ "environment": {
73
+ "tool": "${CLAUDE_TOOL:-unknown}",
74
+ "file": "${CLAUDE_FILE:-none}",
75
+ "user": "$safe_user",
76
+ "working_directory": "$safe_wd",
77
+ "timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
78
+ "session_id": "${CLAUDE_SESSION_ID:-$$}"
79
+ },
80
+ "project": {
81
+ "git_branch": "$safe_branch"
82
+ }
83
+ }
84
+ EOF
85
+ )
86
+
87
+ echo "$context_data"
88
+ }
89
+
90
+ ##################################
91
+ # Subagent Validation
92
+ ##################################
93
+ validate_subagent_exists() {
94
+ local subagent_name="$1"
95
+
96
+ # Simple existence check - leverage the existing validation if needed
97
+ local common_subagents=("security-auditor" "style-enforcer" "debug-specialist" "test-writer")
98
+
99
+ for known_subagent in "${common_subagents[@]}"; do
100
+ if [[ "$subagent_name" == "$known_subagent" ]]; then
101
+ return 0
102
+ fi
103
+ done
104
+
105
+ # If not a known subagent, still allow but warn
106
+ log_warning "Subagent '$subagent_name' not in known list, proceeding anyway"
107
+ return 0
108
+ }
109
+
110
+ ##################################
111
+ # Simple Delegation
112
+ ##################################
113
+ delegate_to_subagent() {
114
+ local subagent_name="$1"
115
+ local event_type="$2"
116
+ local context="$3"
117
+
118
+ log_info "Delegating to subagent: $subagent_name for event: $event_type"
119
+
120
+ echo "🤖 SUBAGENT TRIGGER: Invoking $subagent_name"
121
+ echo ""
122
+ echo "Event: $event_type"
123
+ echo "Triggered by: ${CLAUDE_TOOL:-manual}"
124
+ echo "Target: ${CLAUDE_FILE:-general}"
125
+ echo "Time: $(date)"
126
+ echo ""
127
+ echo "Context for $subagent_name:"
128
+ echo "$context" | jq . 2>/dev/null || echo "$context"
129
+ echo ""
130
+ echo "🎯 REQUEST:"
131
+ echo "Please handle this $event_type event with your specialized expertise."
132
+ echo "Analyze the context above and provide appropriate assistance."
133
+ }
134
+
135
+ ##################################
136
+ # Input Validation
137
+ ##################################
138
+ validate_arguments() {
139
+ local subagent_name="$1"
140
+ local event_type="$2"
141
+
142
+ # Basic argument validation
143
+ if [[ -z "$subagent_name" ]]; then
144
+ echo "ERROR: Subagent name is required" >&2
145
+ show_usage >&2
146
+ return 1
147
+ fi
148
+
149
+ # Validate event type if provided
150
+ if [[ -n "$event_type" ]] && [[ "$event_type" != "manual" ]]; then
151
+ if ! is_supported_event "$event_type"; then
152
+ log_warning "Event type '$event_type' not in supported list"
153
+ log_info "Supported events: ${SUPPORTED_EVENTS[*]}"
154
+ fi
155
+ fi
156
+
157
+ return 0
158
+ }
159
+
160
+ ##################################
161
+ # Main Logic (Simplified)
162
+ ##################################
163
+ main() {
164
+ local subagent_name="${1:-}"
165
+ local event_type="${2:-manual}"
166
+ local additional_context="${3:-}"
167
+
168
+ # Handle help request
169
+ if [[ "$subagent_name" == "--help" ]] || [[ "$subagent_name" == "-h" ]] || [[ -z "$subagent_name" ]]; then
170
+ show_usage
171
+ exit 0
172
+ fi
173
+
174
+ # Initialize minimal error handling
175
+ initialize_error_handling || {
176
+ echo "ERROR: Failed to initialize error handling" >&2
177
+ exit 1
178
+ }
179
+
180
+ # Validate input arguments
181
+ if ! validate_arguments "$subagent_name" "$event_type"; then
182
+ exit 1
183
+ fi
184
+
185
+ # Validate subagent exists (lenient check)
186
+ validate_subagent_exists "$subagent_name"
187
+
188
+ # Gather context (much simpler than original)
189
+ local context
190
+ context=$(gather_simple_context "$subagent_name" "$event_type" "$additional_context")
191
+
192
+ # Delegate to subagent (no complex execution engine needed)
193
+ delegate_to_subagent "$subagent_name" "$event_type" "$context"
194
+
195
+ log_info "Simple trigger completed for: $subagent_name"
196
+ exit 0
197
+ }
198
+
199
+ ##################################
200
+ # Execute (Clean and Simple)
201
+ ##################################
202
+ main "$@"
@@ -0,0 +1,253 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Claude Code Hook: Subagent Event Trigger
5
+ #
6
+ # Purpose: Bridge between Claude Code hooks and subagents, enabling event-driven subagent execution
7
+ # Usage: subagent-trigger.sh [OPTIONS] <subagent-name> [event-type] [additional-context]
8
+ # subagent-trigger.sh [OPTIONS] --event <event-type>
9
+ # Trigger: Can be used in PreToolUse, PostToolUse, or custom hook configurations
10
+ #
11
+ # This hook enables automatic invocation of specialized subagents based on
12
+ # specific events, ensuring the right expertise is applied at the right time.
13
+
14
+ ##################################
15
+ # Module Loading
16
+ ##################################
17
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
18
+ LIB_DIR="$SCRIPT_DIR/lib"
19
+
20
+ # Source all required modules in dependency order
21
+ source "$LIB_DIR/config-constants.sh"
22
+ source "$LIB_DIR/file-utils.sh"
23
+ source "$LIB_DIR/error-handler.sh"
24
+ source "$LIB_DIR/argument-parser.sh"
25
+ source "$LIB_DIR/subagent-discovery.sh"
26
+ source "$LIB_DIR/subagent-validator.sh"
27
+ source "$LIB_DIR/context-manager.sh"
28
+ source "$LIB_DIR/execution-engine.sh"
29
+
30
+ ##################################
31
+ # Initialization
32
+ ##################################
33
+ initialize_all_modules() {
34
+ log_debug "Initializing all modules"
35
+
36
+ initialize_error_handling || {
37
+ echo "FATAL: Error handling initialization failed" >&2
38
+ exit $EXIT_GENERAL_ERROR
39
+ }
40
+
41
+ initialize_argument_parser || {
42
+ log_error "Argument parser initialization failed"
43
+ return $EXIT_GENERAL_ERROR
44
+ }
45
+
46
+ initialize_subagent_discovery || {
47
+ log_error "Subagent discovery initialization failed"
48
+ return $EXIT_GENERAL_ERROR
49
+ }
50
+
51
+ initialize_subagent_validator || {
52
+ log_error "Subagent validator initialization failed"
53
+ return $EXIT_GENERAL_ERROR
54
+ }
55
+
56
+ initialize_context_manager || {
57
+ log_error "Context manager initialization failed"
58
+ return $EXIT_GENERAL_ERROR
59
+ }
60
+
61
+ initialize_execution_engine || {
62
+ log_error "Execution engine initialization failed"
63
+ return $EXIT_GENERAL_ERROR
64
+ }
65
+
66
+ log_debug "All modules initialized successfully"
67
+ return $EXIT_SUCCESS
68
+ }
69
+
70
+ ##################################
71
+ # Core Workflow Functions
72
+ ##################################
73
+ execute_single_subagent() {
74
+ local subagent_name="$1"
75
+ local event_type="$2"
76
+ local additional_context="$3"
77
+
78
+ log_info "Executing single subagent: $subagent_name for event: $event_type"
79
+
80
+ # Find the subagent file
81
+ local subagent_file
82
+ if ! subagent_file=$(find_subagent "$subagent_name"); then
83
+ handle_missing_subagent "$subagent_name"
84
+ return $EXIT_SUBAGENT_NOT_FOUND
85
+ fi
86
+
87
+ log_debug "Found subagent file: $subagent_file"
88
+
89
+ # Validate the subagent
90
+ if ! validate_subagent_file "$subagent_file" "strict"; then
91
+ handle_validation_failure "$subagent_name" "file validation failed"
92
+ return $EXIT_VALIDATION_FAILED
93
+ fi
94
+
95
+ # Create context file
96
+ if ! create_context_file "$subagent_name" "$event_type"; then
97
+ log_error "Failed to create context file"
98
+ return $EXIT_GENERAL_ERROR
99
+ fi
100
+
101
+ local context_file
102
+ context_file=$(get_context_file)
103
+
104
+ # Gather context information
105
+ if ! gather_complete_context "$event_type" "$subagent_name" "$additional_context" "false"; then
106
+ log_error "Failed to gather context"
107
+ cleanup_context_file
108
+ return $EXIT_GENERAL_ERROR
109
+ fi
110
+
111
+ # Write context to file
112
+ if ! write_context_to_file "$context_file"; then
113
+ log_error "Failed to write context to file"
114
+ cleanup_context_file
115
+ return $EXIT_GENERAL_ERROR
116
+ fi
117
+
118
+ # Determine execution mode and timeout
119
+ local execution_mode timeout
120
+ execution_mode=$(determine_execution_mode "$event_type" "$(get_parsed_execution_mode)")
121
+ timeout=$(get_timeout_for_execution "$event_type" "$subagent_name")
122
+
123
+ # Check for dry run mode
124
+ if is_dry_run; then
125
+ execution_mode="dry-run"
126
+ fi
127
+
128
+ # Execute the subagent
129
+ local execution_result
130
+ if ! execute_subagent "$subagent_file" "$context_file" "$execution_mode" "$timeout"; then
131
+ log_error "Subagent execution failed: $subagent_name"
132
+ cleanup_context_file
133
+ return $EXIT_EXECUTION_FAILED
134
+ fi
135
+
136
+ # Clean up context file
137
+ cleanup_context_file
138
+
139
+ log_info "Single subagent execution completed successfully: $subagent_name"
140
+ return $EXIT_SUCCESS
141
+ }
142
+
143
+ execute_event_based_subagents() {
144
+ local event_type="$1"
145
+ local additional_context="$2"
146
+
147
+ log_info "Executing event-based subagents for event: $event_type"
148
+
149
+ # Create shared context file for all subagents
150
+ if ! create_context_file "event-$event_type" "$event_type"; then
151
+ log_error "Failed to create context file for event execution"
152
+ return $EXIT_GENERAL_ERROR
153
+ fi
154
+
155
+ local context_file
156
+ context_file=$(get_context_file)
157
+
158
+ # Gather context information
159
+ if ! gather_complete_context "$event_type" "event-based" "$additional_context" "false"; then
160
+ log_error "Failed to gather context for event execution"
161
+ cleanup_context_file
162
+ return $EXIT_GENERAL_ERROR
163
+ fi
164
+
165
+ # Write context to file
166
+ if ! write_context_to_file "$context_file"; then
167
+ log_error "Failed to write context to file for event execution"
168
+ cleanup_context_file
169
+ return $EXIT_GENERAL_ERROR
170
+ fi
171
+
172
+ # Determine execution mode
173
+ local execution_mode
174
+ execution_mode=$(determine_execution_mode "$event_type" "$(get_parsed_execution_mode)")
175
+
176
+ # Execute all subagents for this event
177
+ local execution_result
178
+ if ! execute_multiple_subagents "$event_type" "$context_file" "$execution_mode"; then
179
+ log_error "Event-based subagent execution failed for event: $event_type"
180
+ cleanup_context_file
181
+ return $EXIT_EXECUTION_FAILED
182
+ fi
183
+
184
+ # Clean up context file
185
+ cleanup_context_file
186
+
187
+ log_info "Event-based subagent execution completed successfully: $event_type"
188
+ return $EXIT_SUCCESS
189
+ }
190
+
191
+ ##################################
192
+ # Main Hook Logic
193
+ ##################################
194
+ main() {
195
+ # Initialize all modules
196
+ if ! initialize_all_modules; then
197
+ echo "FATAL: Module initialization failed" >&2
198
+ exit $EXIT_GENERAL_ERROR
199
+ fi
200
+
201
+ # Parse arguments
202
+ if ! parse_arguments "$@"; then
203
+ # Error messages already logged by parser
204
+ safe_exit $EXIT_VALIDATION_FAILED
205
+ fi
206
+
207
+ # Handle help request
208
+ if is_help_requested; then
209
+ show_usage
210
+ safe_exit $EXIT_SUCCESS
211
+ fi
212
+
213
+ # Log parsed arguments in debug mode
214
+ if is_debug_mode; then
215
+ log_parsed_arguments
216
+ fi
217
+
218
+ # Get execution parameters
219
+ local subagent_name event_type additional_context execution_mode
220
+ subagent_name=$(get_parsed_subagent_name)
221
+ event_type=$(get_parsed_event_type)
222
+ additional_context=$(get_parsed_additional_context)
223
+ execution_mode=$(get_parsed_execution_mode)
224
+
225
+ log_info "Starting subagent hook execution"
226
+ log_info "Mode: $execution_mode, Event: $event_type"
227
+
228
+ # Execute based on mode
229
+ local exit_code
230
+ case "$execution_mode" in
231
+ "event-based")
232
+ execute_event_based_subagents "$event_type" "$additional_context"
233
+ exit_code=$?
234
+ ;;
235
+ *)
236
+ execute_single_subagent "$subagent_name" "$event_type" "$additional_context"
237
+ exit_code=$?
238
+ ;;
239
+ esac
240
+
241
+ if [[ $exit_code -eq $EXIT_SUCCESS ]]; then
242
+ log_info "Subagent hook completed successfully"
243
+ else
244
+ log_error "Subagent hook failed with exit code: $exit_code"
245
+ fi
246
+
247
+ safe_exit $exit_code
248
+ }
249
+
250
+ ##################################
251
+ # Execute Main Function
252
+ ##################################
253
+ main "$@"
@@ -0,0 +1,82 @@
1
+ """Inline suppression parser for code smell and security hooks.
2
+
3
+ Supports per-line suppression comments:
4
+ # smell: ignore[complexity,long_function]
5
+ # security: ignore[secrets,B101]
6
+
7
+ Applies to the same line or the line immediately following the comment.
8
+ No wildcards supported -- explicit check names required.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import re
14
+ import sys
15
+
16
+ from smell_types import Smell
17
+
18
+ _SUPPRESS_RE = re.compile(
19
+ r"#\s*(?P<ns>smell|security):\s*ignore\[(?P<names>[^\]]+)\]"
20
+ )
21
+ _MAX_SUPPRESSIONS_PER_FILE = 5
22
+
23
+
24
+ def _parse_suppression(line: str) -> dict[str, set[str]]:
25
+ """Parse suppression directives from a single line.
26
+
27
+ Returns:
28
+ Dict mapping namespace to set of suppressed check names.
29
+ """
30
+ result: dict[str, set[str]] = {}
31
+ for match in _SUPPRESS_RE.finditer(line):
32
+ ns = match.group("ns")
33
+ names = {n.strip() for n in match.group("names").split(",")}
34
+ result.setdefault(ns, set()).update(names)
35
+ return result
36
+
37
+
38
+ def _build_suppression_map(lines: list[str]) -> dict[int, dict[str, set[str]]]:
39
+ """Build a map of line numbers to their active suppressions.
40
+
41
+ A suppression on line N applies to line N and line N+1.
42
+ """
43
+ suppression_map: dict[int, dict[str, set[str]]] = {}
44
+ for lineno, line in enumerate(lines, start=1):
45
+ parsed = _parse_suppression(line)
46
+ if not parsed:
47
+ continue
48
+ for target_line in (lineno, lineno + 1):
49
+ existing = suppression_map.setdefault(target_line, {})
50
+ for ns, names in parsed.items():
51
+ existing.setdefault(ns, set()).update(names)
52
+ return suppression_map
53
+
54
+
55
+ def _is_suppressed(smell: Smell, namespace: str, active: dict[str, set[str]]) -> bool:
56
+ """Check if a smell is suppressed by the active suppressions."""
57
+ names = active.get(namespace, set())
58
+ return smell.kind in names or smell.name in names
59
+
60
+
61
+ def _warn_excessive(count: int, total_lines: int) -> None:
62
+ """Warn to stderr if too many suppressions are used."""
63
+ if count > _MAX_SUPPRESSIONS_PER_FILE:
64
+ print(
65
+ f"Warning: {count} suppression comments in file "
66
+ f"({_MAX_SUPPRESSIONS_PER_FILE} max recommended)",
67
+ file=sys.stderr,
68
+ )
69
+
70
+
71
+ def filter_suppressed(
72
+ smells: list[Smell], lines: list[str], namespace: str,
73
+ ) -> list[Smell]:
74
+ """Remove smells covered by inline suppression comments."""
75
+ suppression_map = _build_suppression_map(lines)
76
+ _warn_excessive(len(suppression_map), len(lines))
77
+ result: list[Smell] = []
78
+ for smell in smells:
79
+ active = suppression_map.get(smell.line, {})
80
+ if not _is_suppressed(smell, namespace, active):
81
+ result.append(smell)
82
+ return result
@@ -0,0 +1,70 @@
1
+ #!/bin/bash
2
+ # tab-color.sh - Sets iTerm2 tab color for Claude Code hook events
3
+ # Usage: tab-color.sh [gray|blue|green|red|reset]
4
+ #
5
+ # gray = session idle (no prompt running)
6
+ # blue = prompt in progress (working)
7
+ # green = prompt complete (done) -- decays to gray after 3 minutes
8
+ # red = error
9
+ # reset = restore default tab color
10
+ #
11
+ # Tab TITLE (directory name) is set by claude-wrapper.sh at launch.
12
+ # This script only manages COLOR -- Claude Code overrides mid-session title changes.
13
+
14
+ DECAY_SECONDS=180
15
+
16
+ # Consume stdin (hook protocol sends JSON on stdin)
17
+ cat > /dev/null
18
+
19
+ # Per-session state file to prevent decay timer from overwriting blue
20
+ get_state_file() {
21
+ local tty_id
22
+ tty_id=$(tty < /dev/tty 2>/dev/null | tr '/' '_' || echo "unknown")
23
+ echo "/tmp/claude-tab-state${tty_id}"
24
+ }
25
+
26
+ set_tab_color() {
27
+ printf "\033]6;1;bg;red;brightness;%s\a" "$1" > /dev/tty 2>/dev/null
28
+ printf "\033]6;1;bg;green;brightness;%s\a" "$2" > /dev/tty 2>/dev/null
29
+ printf "\033]6;1;bg;blue;brightness;%s\a" "$3" > /dev/tty 2>/dev/null
30
+ }
31
+
32
+ STATE_FILE=$(get_state_file)
33
+
34
+ case "${1:-blue}" in
35
+ gray)
36
+ echo "gray" > "$STATE_FILE" 2>/dev/null
37
+ set_tab_color 130 130 130
38
+ ;;
39
+ blue)
40
+ echo "blue" > "$STATE_FILE" 2>/dev/null
41
+ set_tab_color 20 80 255
42
+ ;;
43
+ green)
44
+ echo "green" > "$STATE_FILE" 2>/dev/null
45
+ set_tab_color 0 255 0
46
+ # Ring terminal bell -- triggers iTerm2 notification for background tabs
47
+ printf "\a" > /dev/tty 2>/dev/null
48
+ # Auto-decay to gray after 3 minutes (only if still green)
49
+ # Fully detach: redirect all fds so Claude Code doesn't wait for child
50
+ nohup bash -c "
51
+ sleep $DECAY_SECONDS
52
+ state_file='$STATE_FILE'
53
+ if [[ -f \"\$state_file\" && \"\$(cat \"\$state_file\" 2>/dev/null)\" == 'green' ]]; then
54
+ echo 'gray' > \"\$state_file\"
55
+ printf '\033]6;1;bg;red;brightness;130\a' > /dev/tty 2>/dev/null
56
+ printf '\033]6;1;bg;green;brightness;130\a' > /dev/tty 2>/dev/null
57
+ printf '\033]6;1;bg;blue;brightness;130\a' > /dev/tty 2>/dev/null
58
+ fi
59
+ " </dev/null >/dev/null 2>&1 &
60
+ ;;
61
+ red)
62
+ echo "red" > "$STATE_FILE" 2>/dev/null
63
+ set_tab_color 220 0 0
64
+ printf "\a" > /dev/tty 2>/dev/null
65
+ ;;
66
+ reset)
67
+ rm -f "$STATE_FILE" 2>/dev/null
68
+ printf "\033]6;1;bg;*;default\a" > /dev/tty 2>/dev/null
69
+ ;;
70
+ esac