@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,127 @@
1
+ #!/usr/bin/env python3
2
+ """PreToolUse hook: verify GPG/SSH commit signing before git commit.
3
+
4
+ Reads a PreToolUse JSON event from stdin. If the Bash command is a
5
+ git commit, verifies that commit signing is configured. Blocks with
6
+ remediation instructions if not.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import re
13
+ import subprocess
14
+ import sys
15
+
16
+ _GIT_COMMIT_RE = re.compile(r"\bgit\s+commit\b")
17
+
18
+
19
+ def _is_git_commit(command: str) -> bool:
20
+ """Return True if command contains a git commit invocation."""
21
+ return bool(_GIT_COMMIT_RE.search(command))
22
+
23
+
24
+ def _git_config(key: str) -> str:
25
+ """Read a git config value, returning empty string on failure."""
26
+ try:
27
+ result = subprocess.run(
28
+ ["git", "config", "--get", key],
29
+ capture_output=True, text=True, timeout=5,
30
+ )
31
+ return result.stdout.strip()
32
+ except (subprocess.SubprocessError, OSError):
33
+ return ""
34
+
35
+
36
+ def _check_gpg_signing() -> str | None:
37
+ """Verify GPG signing config. Return error message or None."""
38
+ gpg_format = _git_config("gpg.format")
39
+ if gpg_format == "ssh":
40
+ return _check_ssh_signing()
41
+ program = _git_config("gpg.program")
42
+ if not program:
43
+ return "gpg.program is not configured"
44
+ return None
45
+
46
+
47
+ def _check_ssh_signing() -> str | None:
48
+ """Verify SSH signing config. Return error message or None."""
49
+ allowed = _git_config("gpg.ssh.allowedSignersFile")
50
+ if not allowed:
51
+ return "gpg.ssh.allowedSignersFile is not configured"
52
+ return None
53
+
54
+
55
+ _REMEDIATION_TEMPLATE = """\
56
+ Commit signing is not properly configured: {error}
57
+
58
+ IMPORTANT: Ask the user which option they prefer before proceeding.
59
+
60
+ **Option 1 -- GPG signing** (traditional, widely supported):
61
+ ```
62
+ git config --global commit.gpgsign true
63
+ git config --global gpg.program gpg
64
+ ```
65
+ Requires: GPG installed, a GPG key generated (`gpg --gen-key`)
66
+
67
+ **Option 2 -- SSH signing** (simpler, uses existing SSH keys):
68
+
69
+ Step 1 -- Generate an SSH key (skip if ~/.ssh/id_ed25519.pub exists):
70
+ ```
71
+ ssh-keygen -t ed25519 -C "your_email@example.com"
72
+ ```
73
+
74
+ Step 2 -- Configure git to use SSH signing:
75
+ ```
76
+ git config --global commit.gpgsign true
77
+ git config --global gpg.format ssh
78
+ git config --global user.signingkey ~/.ssh/id_ed25519.pub
79
+ ```
80
+
81
+ Step 3 -- Create the allowed signers file:
82
+ ```
83
+ echo "$(git config --get user.email) namespaces=\\"git\\" $(cat ~/.ssh/id_ed25519.pub)" > ~/.ssh/allowed_signers
84
+ git config --global gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers
85
+ ```
86
+
87
+ Step 4 -- Add the key to GitHub as a **signing key**:
88
+ ```
89
+ gh ssh-key add ~/.ssh/id_ed25519.pub --type signing
90
+ ```
91
+ (Or manually: GitHub -> Settings -> SSH and GPG keys -> New SSH key -> \
92
+ Key type: **Signing Key**)
93
+
94
+ Ask the user: "Commit signing isn't configured. Would you like me to \
95
+ set it up for you? I can configure GPG or SSH signing -- which do you \
96
+ prefer?" Then run the commands for their chosen option.\
97
+ """
98
+
99
+
100
+ def _build_remediation(error: str) -> str:
101
+ """Build a user-friendly remediation message."""
102
+ return _REMEDIATION_TEMPLATE.format(error=error)
103
+
104
+
105
+ def main() -> None:
106
+ """Entry point: check commit signing config before git commit."""
107
+ try:
108
+ event = json.load(sys.stdin)
109
+ except (json.JSONDecodeError, EOFError):
110
+ sys.exit(0)
111
+ command = event.get("tool_input", {}).get("command", "")
112
+ if not _is_git_commit(command):
113
+ sys.exit(0)
114
+ gpgsign = _git_config("commit.gpgsign")
115
+ if gpgsign != "true":
116
+ reason = _build_remediation("commit.gpgsign is not enabled")
117
+ print(json.dumps({"decision": "block", "reason": reason}))
118
+ sys.exit(0)
119
+ error = _check_gpg_signing()
120
+ if error:
121
+ reason = _build_remediation(error)
122
+ print(json.dumps({"decision": "block", "reason": reason}))
123
+ sys.exit(0)
124
+
125
+
126
+ if __name__ == "__main__":
127
+ main()
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env python3
2
+ """PostToolUse hook: detect common code smells in modified files.
3
+
4
+ Delegates all analysis to smell_checks module. Reads the PostToolUse
5
+ JSON event from stdin, checks the written file, and emits a blocking
6
+ result when violations are found.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import os
13
+ import sys
14
+
15
+ # Allow importing sibling module from the same directory.
16
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
17
+
18
+ from smell_checks import check_file, format_violations # noqa: E402
19
+
20
+
21
+ def main() -> None:
22
+ """Entry point: read PostToolUse event, check file, emit result."""
23
+ try:
24
+ event = json.load(sys.stdin)
25
+ except (json.JSONDecodeError, EOFError):
26
+ sys.exit(0)
27
+ file_path = event.get("tool_input", {}).get("file_path", "")
28
+ if not file_path or not os.path.isfile(file_path):
29
+ sys.exit(0)
30
+ smells = check_file(file_path)
31
+ if smells:
32
+ reason = format_violations(file_path, smells)
33
+ print(json.dumps({"decision": "block", "reason": reason}))
34
+ sys.exit(0)
35
+
36
+
37
+ if __name__ == "__main__":
38
+ main()
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env python3
2
+ """PostToolUse hook: detect security violations in modified files.
3
+
4
+ Delegates analysis to security_checks module. Reads the PostToolUse
5
+ JSON event from stdin, checks the written file, and emits a blocking
6
+ result when violations are found.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import os
13
+ import sys
14
+
15
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
16
+
17
+ from security_checks import check_security, format_security_violations # noqa: E402
18
+
19
+
20
+ def main() -> None:
21
+ """Entry point: read PostToolUse event, check file, emit result."""
22
+ try:
23
+ event = json.load(sys.stdin)
24
+ except (json.JSONDecodeError, EOFError):
25
+ sys.exit(0)
26
+ file_path = event.get("tool_input", {}).get("file_path", "")
27
+ if not file_path or not os.path.isfile(file_path):
28
+ sys.exit(0)
29
+ smells = check_security(file_path)
30
+ if smells:
31
+ reason = format_security_violations(file_path, smells)
32
+ print(json.dumps({"decision": "block", "reason": reason}))
33
+ sys.exit(0)
34
+
35
+
36
+ if __name__ == "__main__":
37
+ main()
@@ -0,0 +1,29 @@
1
+ #!/bin/bash
2
+ # claude-wrapper.sh - Shell wrapper for claude that manages iTerm2 tab colors
3
+ #
4
+ # Source this file in ~/.zshrc:
5
+ # source ~/Code/claude-code/hooks/claude-wrapper.sh
6
+ #
7
+ # Sets gray tab color on launch, red on non-zero exit, then resets.
8
+ # Mid-session colors (blue=working, green=done) are handled by Claude Code hooks.
9
+ # See ~/.claude/settings.json and tab-color.sh
10
+
11
+ CCDK_HOOKS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
12
+
13
+ claude() {
14
+ # Set initial tab color to gray (idle, no prompt running yet)
15
+ "$CCDK_HOOKS_DIR/tab-color.sh" gray < /dev/null
16
+
17
+ # Pass all args through to the real claude binary
18
+ command claude "$@"
19
+ local exit_code=$?
20
+
21
+ if [ $exit_code -ne 0 ]; then
22
+ "$CCDK_HOOKS_DIR/tab-color.sh" red < /dev/null
23
+ fi
24
+
25
+ # Reset tab color to default
26
+ "$CCDK_HOOKS_DIR/tab-color.sh" reset < /dev/null
27
+
28
+ return $exit_code
29
+ }
@@ -0,0 +1,110 @@
1
+ """Per-project configuration loader for code smell and security hooks.
2
+
3
+ Searches for .smellrc.json walking up from the target file's directory.
4
+ Falls back to defaults matching the hardcoded thresholds in smell_types.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import fnmatch
10
+ import json
11
+ import os
12
+ from dataclasses import dataclass, field
13
+ from functools import lru_cache
14
+
15
+
16
+ @dataclass(frozen=True)
17
+ class Config:
18
+ """Immutable configuration for all hooks."""
19
+
20
+ max_complexity: int = 10
21
+ max_function_lines: int = 20
22
+ max_nesting_depth: int = 3
23
+ max_parameters: int = 4
24
+ max_file_lines: int = 300
25
+ duplicate_min_lines: int = 4
26
+ security_enabled: bool = True
27
+ trojan_enabled: bool = True
28
+ suppress_files: tuple[str, ...] = ()
29
+
30
+
31
+ DEFAULT_CONFIG = Config()
32
+
33
+
34
+ def _find_config_file(start_dir: str) -> str | None:
35
+ """Walk up from start_dir looking for .smellrc.json."""
36
+ current = os.path.abspath(start_dir)
37
+ root = os.path.dirname(current)
38
+ while current != root:
39
+ candidate = os.path.join(current, ".smellrc.json")
40
+ if os.path.isfile(candidate):
41
+ return candidate
42
+ root = current
43
+ current = os.path.dirname(current)
44
+ return None
45
+
46
+
47
+ def _parse_config(path: str) -> Config:
48
+ """Parse a .smellrc.json file into a Config object."""
49
+ with open(path, "r", encoding="utf-8") as fh:
50
+ raw = json.load(fh)
51
+ thresholds = raw.get("thresholds", {})
52
+ security = raw.get("security", {})
53
+ suppress = raw.get("suppress_files", [])
54
+ return Config(
55
+ max_complexity=thresholds.get("max_complexity", DEFAULT_CONFIG.max_complexity),
56
+ max_function_lines=thresholds.get("max_function_lines", DEFAULT_CONFIG.max_function_lines),
57
+ max_nesting_depth=thresholds.get("max_nesting_depth", DEFAULT_CONFIG.max_nesting_depth),
58
+ max_parameters=thresholds.get("max_parameters", DEFAULT_CONFIG.max_parameters),
59
+ max_file_lines=thresholds.get("max_file_lines", DEFAULT_CONFIG.max_file_lines),
60
+ duplicate_min_lines=thresholds.get("duplicate_min_lines", DEFAULT_CONFIG.duplicate_min_lines),
61
+ security_enabled=security.get("enabled", DEFAULT_CONFIG.security_enabled),
62
+ trojan_enabled=security.get("trojan_enabled", DEFAULT_CONFIG.trojan_enabled),
63
+ suppress_files=tuple(suppress),
64
+ )
65
+
66
+
67
+ @lru_cache(maxsize=32)
68
+ def _cached_load(config_path: str) -> Config:
69
+ """Load and cache a config file by its resolved path."""
70
+ return _parse_config(config_path)
71
+
72
+
73
+ def load_config(file_path: str) -> Config:
74
+ """Load config for a given source file.
75
+
76
+ Args:
77
+ file_path: Path to the source file being checked.
78
+
79
+ Returns:
80
+ Config from nearest .smellrc.json, or DEFAULT_CONFIG.
81
+ """
82
+ start = os.path.dirname(os.path.abspath(file_path))
83
+ config_path = _find_config_file(start)
84
+ if config_path is None:
85
+ return DEFAULT_CONFIG
86
+ try:
87
+ return _cached_load(config_path)
88
+ except (json.JSONDecodeError, OSError, KeyError, TypeError):
89
+ return DEFAULT_CONFIG
90
+
91
+
92
+ def is_file_suppressed(file_path: str, config: Config) -> bool:
93
+ """Check if a file matches any suppress_files glob patterns.
94
+
95
+ Args:
96
+ file_path: Path to check against suppression globs.
97
+ config: Config with suppress_files patterns.
98
+
99
+ Returns:
100
+ True if the file should be skipped.
101
+ """
102
+ if not config.suppress_files:
103
+ return False
104
+ basename = os.path.basename(file_path)
105
+ for pattern in config.suppress_files:
106
+ if fnmatch.fnmatch(basename, pattern):
107
+ return True
108
+ if fnmatch.fnmatch(file_path, pattern):
109
+ return True
110
+ return False
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Claude Code Hook: File Logger
5
+ #
6
+ # Purpose: Simple demonstration of hook functionality
7
+ # Trigger: PreToolUse for Edit, Write, MultiEdit tools
8
+ # Blocking: No - just logs activity
9
+ #
10
+ # This hook demonstrates basic hook functionality by logging file operations
11
+
12
+
13
+ ##################################
14
+ # Configuration
15
+ ##################################
16
+ HOOK_NAME="file-logger"
17
+ LOG_FILE="$HOME/.claude/logs/file-logger.log"
18
+
19
+ # Ensure log directory exists with secure permissions
20
+ mkdir -p "$(dirname "$LOG_FILE")"
21
+ chmod 700 "$(dirname "$LOG_FILE")"
22
+
23
+ # Create log file with restrictive permissions if it doesn't exist
24
+ touch "$LOG_FILE"
25
+ chmod 600 "$LOG_FILE"
26
+
27
+ ##################################
28
+ # Logging Functions
29
+ ##################################
30
+ log() {
31
+ echo "[$(date +'%Y-%m-%d %H:%M:%S')] [$HOOK_NAME] $*" | tee -a "$LOG_FILE"
32
+ }
33
+
34
+ ##################################
35
+ # Dependency Validation
36
+ ##################################
37
+ validate_hook_dependencies() {
38
+ local deps=("wc" "file")
39
+ local missing=()
40
+
41
+ for dep in "${deps[@]}"; do
42
+ if ! command -v "$dep" &> /dev/null; then
43
+ missing+=("$dep")
44
+ fi
45
+ done
46
+
47
+ if [[ ${#missing[@]} -gt 0 ]]; then
48
+ log "ERROR: Missing required dependencies: ${missing[*]}"
49
+ echo "Install missing tools and retry"
50
+ exit 1
51
+ fi
52
+ }
53
+
54
+ ##################################
55
+ # Main Hook Logic
56
+ ##################################
57
+ main() {
58
+ # Validate dependencies first
59
+ validate_hook_dependencies
60
+ local tool_name="${CLAUDE_TOOL:-unknown}"
61
+ local file_path="${CLAUDE_FILE:-unknown}"
62
+
63
+ log "Hook triggered!"
64
+ log "Tool: $tool_name"
65
+ log "File: $file_path"
66
+
67
+ # Only process file modification tools
68
+ case "$tool_name" in
69
+ "Edit"|"Write"|"MultiEdit")
70
+ log "Processing file modification tool: $tool_name"
71
+ ;;
72
+ *)
73
+ log "Skipping non-file tool: $tool_name"
74
+ exit 0
75
+ ;;
76
+ esac
77
+
78
+ # Get basic file info if file exists
79
+ if [[ -n "$file_path" ]] && [[ "$file_path" != "unknown" ]] && [[ -f "$file_path" ]]; then
80
+ local file_size
81
+ file_size=$(wc -c < "$file_path" 2>/dev/null || echo "0")
82
+ local file_lines
83
+ file_lines=$(wc -l < "$file_path" 2>/dev/null || echo "0")
84
+
85
+ log "File size: $file_size bytes"
86
+ log "File lines: $file_lines"
87
+ log "File type: $(file -b "$file_path" 2>/dev/null || echo "unknown")"
88
+ else
89
+ log "File does not exist yet or path unknown"
90
+ fi
91
+
92
+ # Always allow the operation to proceed
93
+ log "Operation allowed - no blocking behavior"
94
+ exit 0
95
+ }
96
+
97
+ ##################################
98
+ # Execute Main Function
99
+ ##################################
100
+ main "$@"