@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,104 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+
4
+ # Field Validators Module
5
+ #
6
+ # Provides validation functions for individual subagent metadata fields
7
+ # (name, description, version, tools, tags).
8
+ # Extracted from subagent-validator.sh.
9
+
10
+ # Include guard
11
+ [[ -n "${_FIELD_VALIDATORS_LOADED:-}" ]] && return 0
12
+ _FIELD_VALIDATORS_LOADED=1
13
+
14
+ # Source required modules
15
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
+ source "$SCRIPT_DIR/config-constants.sh"
17
+ source "$SCRIPT_DIR/error-handler.sh"
18
+
19
+ ##################################
20
+ # Field Validation Functions
21
+ ##################################
22
+
23
+ validate_subagent_name_field() {
24
+ local name="$1"
25
+
26
+ if [[ -z "$name" ]]; then
27
+ log_error "Subagent name cannot be empty"
28
+ return $EXIT_VALIDATION_FAILED
29
+ fi
30
+
31
+ if [[ ${#name} -gt $MAX_SUBAGENT_NAME_LENGTH ]]; then
32
+ log_error "Subagent name too long: ${#name} chars (max: $MAX_SUBAGENT_NAME_LENGTH)"
33
+ return $EXIT_VALIDATION_FAILED
34
+ fi
35
+
36
+ if [[ ! "$name" =~ $SUBAGENT_NAME_PATTERN ]]; then
37
+ log_error "Invalid subagent name format: $name"
38
+ return $EXIT_VALIDATION_FAILED
39
+ fi
40
+
41
+ return $EXIT_SUCCESS
42
+ }
43
+
44
+ validate_description_field() {
45
+ local description="$1"
46
+
47
+ if [[ -z "$description" ]]; then
48
+ log_error "Description cannot be empty"
49
+ return $EXIT_VALIDATION_FAILED
50
+ fi
51
+
52
+ if [[ ${#description} -lt $MIN_DESCRIPTION_LENGTH ]]; then
53
+ log_error "Description too short: ${#description} chars (min: $MIN_DESCRIPTION_LENGTH)"
54
+ return $EXIT_VALIDATION_FAILED
55
+ fi
56
+
57
+ if [[ ${#description} -gt $MAX_DESCRIPTION_LENGTH ]]; then
58
+ log_error "Description too long: ${#description} chars (max: $MAX_DESCRIPTION_LENGTH)"
59
+ return $EXIT_VALIDATION_FAILED
60
+ fi
61
+
62
+ return $EXIT_SUCCESS
63
+ }
64
+
65
+ validate_version_field() {
66
+ local version="$1"
67
+
68
+ # Semantic versioning pattern
69
+ if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ ]]; then
70
+ return $EXIT_SUCCESS
71
+ fi
72
+
73
+ # Simple versioning pattern
74
+ if [[ "$version" =~ ^[0-9]+(\.[0-9]+)*$ ]]; then
75
+ return $EXIT_SUCCESS
76
+ fi
77
+
78
+ log_error "Invalid version format: $version"
79
+ return $EXIT_VALIDATION_FAILED
80
+ }
81
+
82
+ validate_tools_field() {
83
+ local tools="$1"
84
+
85
+ # Tools can be comma-separated or a single word
86
+ if [[ "$tools" =~ ^[a-zA-Z][a-zA-Z0-9_]*([[:space:]]*,[[:space:]]*[a-zA-Z][a-zA-Z0-9_]*)*$ ]]; then
87
+ return $EXIT_SUCCESS
88
+ fi
89
+
90
+ log_error "Invalid tools format: $tools"
91
+ return $EXIT_VALIDATION_FAILED
92
+ }
93
+
94
+ validate_tags_field() {
95
+ local tags="$1"
96
+
97
+ # Tags can be array format or comma-separated
98
+ if [[ "$tags" =~ ^\[.*\]$ ]] || [[ "$tags" =~ ^[a-zA-Z][a-zA-Z0-9_,-\s]*$ ]]; then
99
+ return $EXIT_SUCCESS
100
+ fi
101
+
102
+ log_error "Invalid tags format: $tags"
103
+ return $EXIT_VALIDATION_FAILED
104
+ }
@@ -0,0 +1,398 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+
4
+ # File Utilities Module for Subagent-Hook Integration
5
+ #
6
+ # This module provides standardized file operations with proper error handling,
7
+ # security checks, and logging for the subagent-hook integration system.
8
+
9
+ # Include guard
10
+ [[ -n "${_FILE_UTILS_LOADED:-}" ]] && return 0
11
+ _FILE_UTILS_LOADED=1
12
+
13
+ # Source required modules
14
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15
+ source "$SCRIPT_DIR/config-constants.sh"
16
+
17
+ ##################################
18
+ # JSON Utilities
19
+ ##################################
20
+
21
+ json_escape() {
22
+ local input="$1"
23
+ input="${input//\\/\\\\}"
24
+ input="${input//\"/\\\"}"
25
+ input="${input//$'\n'/\\n}"
26
+ input="${input//$'\r'/\\r}"
27
+ input="${input//$'\t'/\\t}"
28
+ printf '%s' "$input"
29
+ }
30
+
31
+ ##################################
32
+ # Directory Operations
33
+ ##################################
34
+
35
+ ensure_directory_exists() {
36
+ local dir_path="$1"
37
+ local permissions="${2:-$SECURE_DIR_PERMISSIONS}"
38
+
39
+ if [[ -z "$dir_path" ]]; then
40
+ return $EXIT_GENERAL_ERROR
41
+ fi
42
+
43
+ if [[ ! -d "$dir_path" ]]; then
44
+ if ! mkdir -p "$dir_path" 2>/dev/null; then
45
+ return $EXIT_GENERAL_ERROR
46
+ fi
47
+ fi
48
+
49
+ if ! chmod "$permissions" "$dir_path" 2>/dev/null; then
50
+ return $EXIT_GENERAL_ERROR
51
+ fi
52
+
53
+ return $EXIT_SUCCESS
54
+ }
55
+
56
+ ensure_required_directories() {
57
+ ensure_directory_exists "$CLAUDE_BASE_DIR" "$SECURE_DIR_PERMISSIONS" || return $?
58
+ ensure_directory_exists "$SUBAGENTS_DIR" "$SECURE_DIR_PERMISSIONS" || return $?
59
+ ensure_directory_exists "$LOGS_DIR" "$SECURE_DIR_PERMISSIONS" || return $?
60
+
61
+ return $EXIT_SUCCESS
62
+ }
63
+
64
+ ##################################
65
+ # File Reading Operations
66
+ ##################################
67
+
68
+ read_file_safely() {
69
+ local file_path="$1"
70
+
71
+ if [[ -z "$file_path" ]]; then
72
+ return $EXIT_GENERAL_ERROR
73
+ fi
74
+
75
+ if [[ ! -f "$file_path" ]]; then
76
+ return $EXIT_GENERAL_ERROR
77
+ fi
78
+
79
+ if [[ ! -r "$file_path" ]]; then
80
+ return $EXIT_GENERAL_ERROR
81
+ fi
82
+
83
+ # Check if file is binary (avoid reading binary files)
84
+ if file "$file_path" 2>/dev/null | grep -q "binary"; then
85
+ return $EXIT_GENERAL_ERROR
86
+ fi
87
+
88
+ cat "$file_path" 2>/dev/null
89
+ return $?
90
+ }
91
+
92
+ read_file_with_size_limit() {
93
+ local file_path="$1"
94
+ local max_size="${2:-$MAX_CONTEXT_FILE_SIZE}"
95
+
96
+ if [[ -z "$file_path" ]]; then
97
+ return $EXIT_GENERAL_ERROR
98
+ fi
99
+
100
+ # Check file size
101
+ if [[ -f "$file_path" ]]; then
102
+ local file_size
103
+ file_size=$(stat -f%z "$file_path" 2>/dev/null || stat -c%s "$file_path" 2>/dev/null)
104
+
105
+ if [[ "$file_size" -gt "$max_size" ]]; then
106
+ return $EXIT_GENERAL_ERROR
107
+ fi
108
+ fi
109
+
110
+ read_file_safely "$file_path"
111
+ return $?
112
+ }
113
+
114
+ ##################################
115
+ # File Writing Operations
116
+ ##################################
117
+
118
+ write_file_safely() {
119
+ local file_path="$1"
120
+ local content="$2"
121
+ local permissions="${3:-$SECURE_FILE_PERMISSIONS}"
122
+
123
+ if [[ -z "$file_path" ]]; then
124
+ return $EXIT_GENERAL_ERROR
125
+ fi
126
+
127
+ # Ensure parent directory exists
128
+ local parent_dir
129
+ parent_dir="$(dirname "$file_path")"
130
+ if ! ensure_directory_exists "$parent_dir"; then
131
+ return $EXIT_GENERAL_ERROR
132
+ fi
133
+
134
+ # Write content
135
+ if ! echo "$content" > "$file_path" 2>/dev/null; then
136
+ return $EXIT_GENERAL_ERROR
137
+ fi
138
+
139
+ # Set permissions
140
+ if ! chmod "$permissions" "$file_path" 2>/dev/null; then
141
+ return $EXIT_GENERAL_ERROR
142
+ fi
143
+
144
+ return $EXIT_SUCCESS
145
+ }
146
+
147
+ create_temp_file() {
148
+ local prefix="${1:-$CONTEXT_FILE_PREFIX}"
149
+ local _suffix="${2:-}" # kept for API compat; mktemp adds randomness
150
+
151
+ # Use mktemp for unpredictable temp file names
152
+ local temp_file
153
+ temp_file=$(mktemp "${prefix}-XXXXXX") || return $EXIT_GENERAL_ERROR
154
+
155
+ if ! chmod "$SECURE_FILE_PERMISSIONS" "$temp_file" 2>/dev/null; then
156
+ rm -f "$temp_file" 2>/dev/null
157
+ return $EXIT_GENERAL_ERROR
158
+ fi
159
+
160
+ echo "$temp_file"
161
+ return $EXIT_SUCCESS
162
+ }
163
+
164
+ write_context_file() {
165
+ local context_data="$1"
166
+ local temp_file="$2"
167
+
168
+ if [[ -z "$context_data" ]] || [[ -z "$temp_file" ]]; then
169
+ return $EXIT_GENERAL_ERROR
170
+ fi
171
+
172
+ write_file_safely "$temp_file" "$context_data" "$SECURE_FILE_PERMISSIONS"
173
+ return $?
174
+ }
175
+
176
+ ##################################
177
+ # File Validation Operations
178
+ ##################################
179
+
180
+ file_exists_and_readable() {
181
+ local file_path="$1"
182
+
183
+ [[ -n "$file_path" ]] && [[ -f "$file_path" ]] && [[ -r "$file_path" ]]
184
+ return $?
185
+ }
186
+
187
+ file_has_extension() {
188
+ local file_path="$1"
189
+ local expected_extension="$2"
190
+
191
+ if [[ -z "$file_path" ]] || [[ -z "$expected_extension" ]]; then
192
+ return $EXIT_GENERAL_ERROR
193
+ fi
194
+
195
+ [[ "$file_path" == *"$expected_extension" ]]
196
+ return $?
197
+ }
198
+
199
+ is_valid_subagent_file() {
200
+ local file_path="$1"
201
+
202
+ # Check file exists and is readable
203
+ if ! file_exists_and_readable "$file_path"; then
204
+ return $EXIT_GENERAL_ERROR
205
+ fi
206
+
207
+ # Check file extension
208
+ if ! file_has_extension "$file_path" "$SUBAGENT_FILE_EXTENSION"; then
209
+ return $EXIT_GENERAL_ERROR
210
+ fi
211
+
212
+ # Check file size
213
+ local file_content
214
+ if ! file_content=$(read_file_with_size_limit "$file_path"); then
215
+ return $EXIT_GENERAL_ERROR
216
+ fi
217
+
218
+ # Basic YAML frontmatter validation
219
+ if ! echo "$file_content" | head -1 | grep -q "$YAML_FRONTMATTER_START"; then
220
+ return $EXIT_GENERAL_ERROR
221
+ fi
222
+
223
+ return $EXIT_SUCCESS
224
+ }
225
+
226
+ ##################################
227
+ # File Cleanup Operations
228
+ ##################################
229
+
230
+ cleanup_temp_files() {
231
+ local pattern="${1:-$TEMP_FILE_PATTERN}"
232
+ local max_age_minutes="${2:-60}"
233
+
234
+ # Find and remove old temp files
235
+ find /tmp -name "$pattern" -type f -mmin +$max_age_minutes -delete 2>/dev/null || true
236
+
237
+ return $EXIT_SUCCESS
238
+ }
239
+
240
+ cleanup_specific_temp_file() {
241
+ local temp_file="$1"
242
+
243
+ if [[ -n "$temp_file" ]] && [[ -f "$temp_file" ]]; then
244
+ rm -f "$temp_file" 2>/dev/null || true
245
+ fi
246
+
247
+ return $EXIT_SUCCESS
248
+ }
249
+
250
+ ##################################
251
+ # File Path Operations
252
+ ##################################
253
+
254
+ resolve_subagent_path() {
255
+ local subagent_name="$1"
256
+
257
+ if [[ -z "$subagent_name" ]]; then
258
+ return $EXIT_GENERAL_ERROR
259
+ fi
260
+
261
+ local subagent_file="${subagent_name}${SUBAGENT_FILE_EXTENSION}"
262
+
263
+ # Check project-level subagents first (higher priority)
264
+ local project_path="${PROJECT_SUBAGENTS_DIR}/${subagent_file}"
265
+ if file_exists_and_readable "$project_path"; then
266
+ echo "$project_path"
267
+ return $EXIT_SUCCESS
268
+ fi
269
+
270
+ # Check user-level subagents
271
+ local user_path="${SUBAGENTS_DIR}/${subagent_file}"
272
+ if file_exists_and_readable "$user_path"; then
273
+ echo "$user_path"
274
+ return $EXIT_SUCCESS
275
+ fi
276
+
277
+ return $EXIT_SUBAGENT_NOT_FOUND
278
+ }
279
+
280
+ get_absolute_path() {
281
+ local path="$1"
282
+
283
+ if [[ -z "$path" ]]; then
284
+ return $EXIT_GENERAL_ERROR
285
+ fi
286
+
287
+ # Convert to absolute path
288
+ if [[ "$path" = /* ]]; then
289
+ echo "$path"
290
+ else
291
+ echo "$(pwd)/$path"
292
+ fi
293
+
294
+ return $EXIT_SUCCESS
295
+ }
296
+
297
+ ##################################
298
+ # File Permission Operations
299
+ ##################################
300
+
301
+ validate_file_permissions() {
302
+ local file_path="$1"
303
+
304
+ if [[ -z "$file_path" ]] || [[ ! -f "$file_path" ]]; then
305
+ return $EXIT_GENERAL_ERROR
306
+ fi
307
+
308
+ # Check if file is world-writable (security risk)
309
+ local permissions
310
+ permissions=$(stat -f%A "$file_path" 2>/dev/null || stat -c%a "$file_path" 2>/dev/null)
311
+
312
+ if [[ -z "$permissions" ]]; then
313
+ return $EXIT_GENERAL_ERROR
314
+ fi
315
+
316
+ # Check if world-writable (last digit should not be writable)
317
+ local world_perms=$((permissions % 10))
318
+ if [[ $((world_perms & 2)) -eq 2 ]]; then
319
+ return $EXIT_SECURITY_VIOLATION
320
+ fi
321
+
322
+ return $EXIT_SUCCESS
323
+ }
324
+
325
+ set_secure_permissions() {
326
+ local file_path="$1"
327
+ local permissions="${2:-$SECURE_FILE_PERMISSIONS}"
328
+
329
+ if [[ -z "$file_path" ]] || [[ ! -f "$file_path" ]]; then
330
+ return $EXIT_GENERAL_ERROR
331
+ fi
332
+
333
+ chmod "$permissions" "$file_path" 2>/dev/null
334
+ return $?
335
+ }
336
+
337
+ ##################################
338
+ # Logging File Operations
339
+ ##################################
340
+
341
+ ensure_log_files() {
342
+ # Create log files with secure permissions if they don't exist
343
+ if [[ ! -f "$LOG_FILE" ]]; then
344
+ write_file_safely "$LOG_FILE" "" "$SECURE_FILE_PERMISSIONS" || return $?
345
+ fi
346
+
347
+ if [[ ! -f "$VIOLATION_LOG" ]]; then
348
+ write_file_safely "$VIOLATION_LOG" "" "$SECURE_FILE_PERMISSIONS" || return $?
349
+ fi
350
+
351
+ return $EXIT_SUCCESS
352
+ }
353
+
354
+ ##################################
355
+ # Path Validation (Security)
356
+ ##################################
357
+
358
+ validate_path_safety() {
359
+ local path="$1"
360
+
361
+ if [[ -z "$path" ]]; then
362
+ return $EXIT_GENERAL_ERROR
363
+ fi
364
+
365
+ # Resolve to canonical path to prevent traversal via symlinks/..
366
+ local resolved
367
+ resolved=$(realpath -m "$path" 2>/dev/null) || resolved="$path"
368
+
369
+ # Check for path traversal in the resolved path
370
+ if [[ "$resolved" == *".."* ]] || [[ "$resolved" == *"/./"* ]]; then
371
+ return $EXIT_SECURITY_VIOLATION
372
+ fi
373
+
374
+ # Check for absolute paths outside allowed directories
375
+ if [[ "$resolved" = /* ]]; then
376
+ # Allow paths within Claude base directory
377
+ if [[ "$resolved" == "$CLAUDE_BASE_DIR"* ]]; then
378
+ return $EXIT_SUCCESS
379
+ fi
380
+
381
+ # Allow paths within project directory
382
+ local cwd
383
+ cwd=$(pwd)
384
+ if [[ "$resolved" == "$cwd"* ]]; then
385
+ return $EXIT_SUCCESS
386
+ fi
387
+
388
+ # Allow temp files (TMPDIR-aware)
389
+ local tmpdir="${TMPDIR:-/tmp}"
390
+ if [[ "$resolved" == "${tmpdir}/claude-subagent"* ]]; then
391
+ return $EXIT_SUCCESS
392
+ fi
393
+
394
+ return $EXIT_SECURITY_VIOLATION
395
+ fi
396
+
397
+ return $EXIT_SUCCESS
398
+ }