@hybridlabor-api/aos 4.8.0 → 4.9.0

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/.agents/agents.md +1 -1
  2. package/.agents/nodes.json +1 -1
  3. package/.agents/vendor-manifest.json +23 -1
  4. package/.claude/agents/godmode-media-eventtech.md +1 -1
  5. package/.claude/hooks/conventional-commits.mjs +125 -0
  6. package/.claude/hooks/env-file-protection.mjs +105 -0
  7. package/.claude/hooks/go-gate.mjs +101 -81
  8. package/.claude/settings.json +13 -0
  9. package/.opencode/agents/godmode-media-eventtech.md +1 -1
  10. package/.opencode/plugins/bdb-aos.js +31 -4
  11. package/README.de.md +1 -1
  12. package/README.md +1 -1
  13. package/README.pt.md +1 -1
  14. package/THIRD_PARTY_NOTICES.md +88 -0
  15. package/docs/skills_table.md +1 -1
  16. package/installer.js +174 -46
  17. package/package.json +6 -2
  18. package/packages/aos-cli/README.md +80 -0
  19. package/packages/aos-cli/bin/aos-cli.mjs +134 -0
  20. package/packages/aos-cli/core-skills.json +12 -0
  21. package/packages/aos-cli/extensions/aos.ts +321 -0
  22. package/packages/aos-cli/package-lock.json +1923 -0
  23. package/packages/aos-cli/package.json +29 -0
  24. package/packages/aos-cli/scripts/check-theme.mjs +63 -0
  25. package/packages/aos-cli/themes/aos.json +97 -0
  26. package/scripts/build-plugin-manifest.mjs +131 -0
  27. package/scripts/validate-skills.mjs +81 -6
  28. package/skills/basic/ao-orchestrator/SKILL.md +116 -0
  29. package/skills/global_config/agenttrail/SKILL.md +6 -1
  30. package/skills/global_config/ask-tim/SKILL.md +4 -4
  31. package/skills/global_config/bash-script-generator/SKILL.md +201 -0
  32. package/skills/global_config/bash-script-generator/assets/templates/standard-template.sh +96 -0
  33. package/skills/global_config/bash-script-generator/docs/bash-scripting-guide.md +729 -0
  34. package/skills/global_config/bash-script-generator/docs/generation-best-practices.md +193 -0
  35. package/skills/global_config/bash-script-generator/docs/script-patterns.md +566 -0
  36. package/skills/global_config/bash-script-generator/docs/text-processing-guide.md +437 -0
  37. package/skills/global_config/bash-script-generator/examples/log-analyzer.sh +92 -0
  38. package/skills/global_config/bash-script-generator/scripts/generate_script_template.sh +123 -0
  39. package/skills/global_config/bash-script-generator/scripts/run_ci_checks.sh +172 -0
  40. package/skills/global_config/bash-script-generator/scripts/test_generator.sh +413 -0
  41. package/skills/global_config/bash-script-validator/SKILL.md +249 -0
  42. package/skills/global_config/bash-script-validator/docs/awk-reference.md +449 -0
  43. package/skills/global_config/bash-script-validator/docs/bash-reference.md +468 -0
  44. package/skills/global_config/bash-script-validator/docs/common-mistakes.md +623 -0
  45. package/skills/global_config/bash-script-validator/docs/grep-reference.md +395 -0
  46. package/skills/global_config/bash-script-validator/docs/regex-reference.md +391 -0
  47. package/skills/global_config/bash-script-validator/docs/sed-reference.md +454 -0
  48. package/skills/global_config/bash-script-validator/docs/shell-reference.md +463 -0
  49. package/skills/global_config/bash-script-validator/docs/shellcheck-reference.md +399 -0
  50. package/skills/global_config/bash-script-validator/examples/bad-bash.sh +55 -0
  51. package/skills/global_config/bash-script-validator/examples/bad-shell.sh +54 -0
  52. package/skills/global_config/bash-script-validator/examples/good-bash.sh +71 -0
  53. package/skills/global_config/bash-script-validator/examples/good-shell.sh +69 -0
  54. package/skills/global_config/bash-script-validator/scripts/run_ci_checks.sh +23 -0
  55. package/skills/global_config/bash-script-validator/scripts/shellcheck_wrapper.sh +174 -0
  56. package/skills/global_config/bash-script-validator/scripts/test_validate.sh +446 -0
  57. package/skills/global_config/bash-script-validator/scripts/validate.sh +512 -0
  58. package/skills/global_config/ci-pipeline/SKILL.md +135 -0
  59. package/skills/global_config/dispatching-parallel-agents/SKILL.md +170 -0
  60. package/skills/global_config/dockerfile-generator/SKILL.md +1038 -0
  61. package/skills/global_config/dockerfile-generator/examples/example.dockerignore +95 -0
  62. package/skills/global_config/dockerfile-generator/examples/golang-distroless.Dockerfile +34 -0
  63. package/skills/global_config/dockerfile-generator/examples/java-springboot.Dockerfile +45 -0
  64. package/skills/global_config/dockerfile-generator/examples/nextjs-production.Dockerfile +49 -0
  65. package/skills/global_config/dockerfile-generator/examples/nodejs-multistage.Dockerfile +55 -0
  66. package/skills/global_config/dockerfile-generator/examples/python-fastapi.Dockerfile +48 -0
  67. package/skills/global_config/dockerfile-generator/references/language_specific_guides.md +510 -0
  68. package/skills/global_config/dockerfile-generator/references/multistage_builds.md +570 -0
  69. package/skills/global_config/dockerfile-generator/references/optimization_patterns.md +492 -0
  70. package/skills/global_config/dockerfile-generator/references/security_best_practices.md +375 -0
  71. package/skills/global_config/dockerfile-generator/scripts/generate_dockerignore.sh +199 -0
  72. package/skills/global_config/dockerfile-generator/scripts/generate_golang.sh +172 -0
  73. package/skills/global_config/dockerfile-generator/scripts/generate_java.sh +185 -0
  74. package/skills/global_config/dockerfile-generator/scripts/generate_nodejs.sh +279 -0
  75. package/skills/global_config/dockerfile-generator/scripts/generate_python.sh +218 -0
  76. package/skills/global_config/dockerfile-generator/scripts/test_generator.sh +210 -0
  77. package/skills/global_config/dockerfile-validator/SKILL.md +300 -0
  78. package/skills/global_config/dockerfile-validator/examples/.dockerignore.example +34 -0
  79. package/skills/global_config/dockerfile-validator/examples/bad-example.Dockerfile +37 -0
  80. package/skills/global_config/dockerfile-validator/examples/golang-distroless.Dockerfile +50 -0
  81. package/skills/global_config/dockerfile-validator/examples/good-example.Dockerfile +52 -0
  82. package/skills/global_config/dockerfile-validator/examples/python-optimized.Dockerfile +53 -0
  83. package/skills/global_config/dockerfile-validator/examples/security-issues.Dockerfile +42 -0
  84. package/skills/global_config/dockerfile-validator/references/docker_best_practices.md +348 -0
  85. package/skills/global_config/dockerfile-validator/references/optimization_guide.md +473 -0
  86. package/skills/global_config/dockerfile-validator/references/security_checklist.md +208 -0
  87. package/skills/global_config/dockerfile-validator/scripts/dockerfile-validate.sh +699 -0
  88. package/skills/global_config/dockerfile-validator/scripts/test_validate.sh +35 -0
  89. package/skills/global_config/dockerfile-validator/tests/fixtures/copy-before-yarn-lock-read.Dockerfile +6 -0
  90. package/skills/global_config/dockerfile-validator/tests/fixtures/copy-before-yarn.Dockerfile +6 -0
  91. package/skills/global_config/dockerfile-validator/tests/fixtures/from-platform-nonroot.Dockerfile +7 -0
  92. package/skills/global_config/dockerfile-validator/tests/test_regression.sh +164 -0
  93. package/skills/global_config/finishing-a-development-branch/SKILL.md +228 -0
  94. package/skills/global_config/github-actions-generator/SKILL.md +353 -0
  95. package/skills/global_config/github-actions-generator/assets/templates/action/composite/action.yml +82 -0
  96. package/skills/global_config/github-actions-generator/assets/templates/action/docker/Dockerfile +25 -0
  97. package/skills/global_config/github-actions-generator/assets/templates/action/docker/action.yml +42 -0
  98. package/skills/global_config/github-actions-generator/assets/templates/action/docker/entrypoint.sh +27 -0
  99. package/skills/global_config/github-actions-generator/assets/templates/action/javascript/action.yml +33 -0
  100. package/skills/global_config/github-actions-generator/assets/templates/action/javascript/index.js +50 -0
  101. package/skills/global_config/github-actions-generator/assets/templates/action/javascript/package.json +27 -0
  102. package/skills/global_config/github-actions-generator/assets/templates/workflow/basic_workflow.yml +242 -0
  103. package/skills/global_config/github-actions-generator/assets/templates/workflow/reusable_workflow.yml +106 -0
  104. package/skills/global_config/github-actions-generator/examples/README.md +147 -0
  105. package/skills/global_config/github-actions-generator/examples/actions/setup-node-cached/action.yml +93 -0
  106. package/skills/global_config/github-actions-generator/examples/caching/docker-buildkit.yml +256 -0
  107. package/skills/global_config/github-actions-generator/examples/security/dependency-review.yml +62 -0
  108. package/skills/global_config/github-actions-generator/examples/security/sbom-attestation.yml +119 -0
  109. package/skills/global_config/github-actions-generator/examples/triggers/chatops-commands.yml +475 -0
  110. package/skills/global_config/github-actions-generator/examples/triggers/repository-dispatch.yml +418 -0
  111. package/skills/global_config/github-actions-generator/examples/triggers/workflow-orchestration.yml +404 -0
  112. package/skills/global_config/github-actions-generator/examples/workflows/docker-build-push.yml +68 -0
  113. package/skills/global_config/github-actions-generator/examples/workflows/go-ci.yml +161 -0
  114. package/skills/global_config/github-actions-generator/examples/workflows/monorepo-ci.yml +340 -0
  115. package/skills/global_config/github-actions-generator/examples/workflows/multi-environment-deploy.yml +406 -0
  116. package/skills/global_config/github-actions-generator/examples/workflows/nodejs-ci.yml +122 -0
  117. package/skills/global_config/github-actions-generator/examples/workflows/python-ci.yml +157 -0
  118. package/skills/global_config/github-actions-generator/examples/workflows/scheduled-tasks.yml +376 -0
  119. package/skills/global_config/github-actions-generator/references/advanced-triggers.md +917 -0
  120. package/skills/global_config/github-actions-generator/references/best-practices.md +755 -0
  121. package/skills/global_config/github-actions-generator/references/common-actions.md +715 -0
  122. package/skills/global_config/github-actions-generator/references/custom-actions.md +320 -0
  123. package/skills/global_config/github-actions-generator/references/expressions-and-contexts.md +688 -0
  124. package/skills/global_config/github-actions-generator/references/modern-features.md +421 -0
  125. package/skills/global_config/github-actions-generator/scripts/test_generator.sh +344 -0
  126. package/skills/global_config/github-actions-templates/SKILL.md +7 -0
  127. package/skills/global_config/github-actions-validator/SKILL.md +576 -0
  128. package/skills/global_config/github-actions-validator/examples/README.md +88 -0
  129. package/skills/global_config/github-actions-validator/examples/outdated-versions.yml +76 -0
  130. package/skills/global_config/github-actions-validator/examples/valid-ci.yml +79 -0
  131. package/skills/global_config/github-actions-validator/examples/with-errors.yml +47 -0
  132. package/skills/global_config/github-actions-validator/references/act_usage.md +233 -0
  133. package/skills/global_config/github-actions-validator/references/action_versions.md +122 -0
  134. package/skills/global_config/github-actions-validator/references/actionlint_usage.md +343 -0
  135. package/skills/global_config/github-actions-validator/references/common_errors.md +512 -0
  136. package/skills/global_config/github-actions-validator/references/modern_features.md +384 -0
  137. package/skills/global_config/github-actions-validator/references/runners.md +317 -0
  138. package/skills/global_config/github-actions-validator/scripts/install_tools.sh +113 -0
  139. package/skills/global_config/github-actions-validator/scripts/validate_workflow.sh +910 -0
  140. package/skills/global_config/github-actions-validator/tests/test_validate_workflow.sh +237 -0
  141. package/skills/global_config/makefile-generator/SKILL.md +614 -0
  142. package/skills/global_config/makefile-generator/assets/templates/.gitkeep +1 -0
  143. package/skills/global_config/makefile-generator/docs/makefile-structure.md +530 -0
  144. package/skills/global_config/makefile-generator/docs/optimization-guide.md +784 -0
  145. package/skills/global_config/makefile-generator/docs/patterns-guide.md +642 -0
  146. package/skills/global_config/makefile-generator/docs/security-guide.md +361 -0
  147. package/skills/global_config/makefile-generator/docs/targets-guide.md +642 -0
  148. package/skills/global_config/makefile-generator/docs/variables-guide.md +596 -0
  149. package/skills/global_config/makefile-generator/scripts/add_standard_targets.sh +539 -0
  150. package/skills/global_config/makefile-generator/scripts/generate_makefile_template.sh +690 -0
  151. package/skills/global_config/makefile-generator/test/test_helper_scripts.sh +190 -0
  152. package/skills/global_config/makefile-validator/SKILL.md +244 -0
  153. package/skills/global_config/makefile-validator/docs/bake-tool.md +1000 -0
  154. package/skills/global_config/makefile-validator/docs/best-practices.md +858 -0
  155. package/skills/global_config/makefile-validator/docs/common-mistakes.md +944 -0
  156. package/skills/global_config/makefile-validator/examples/bad-makefile.mk +77 -0
  157. package/skills/global_config/makefile-validator/examples/good-makefile.mk +103 -0
  158. package/skills/global_config/makefile-validator/scripts/test_validate.sh +382 -0
  159. package/skills/global_config/makefile-validator/scripts/validate_makefile.sh +712 -0
  160. package/skills/global_config/{MCP_Manage → mcp-manage}/SKILL.md +3 -3
  161. package/skills/global_config/requesting-code-review/SKILL.md +98 -0
  162. package/skills/global_config/requesting-code-review/code-reviewer.md +198 -0
  163. package/skills/global_config/using-git-worktrees/SKILL.md +170 -0
  164. package/skills/global_config/verification-before-completion/SKILL.md +123 -0
  165. package/skills/global_config/writing-plans/SKILL.md +126 -46
  166. package/skills/global_config/writing-plans-legacy/SKILL.md +139 -0
@@ -0,0 +1,712 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Makefile Validator Script
4
+ # Validates Makefile syntax, best practices, security, and optimization using mbake tool
5
+ # Features: venv isolation, automatic cleanup via trap, comprehensive validation
6
+
7
+ set -euo pipefail
8
+
9
+ # Colors for output (supports NO_COLOR standard: https://no-color.org/)
10
+ if [ -n "${NO_COLOR:-}" ]; then
11
+ RED=''
12
+ YELLOW=''
13
+ GREEN=''
14
+ BLUE=''
15
+ NC=''
16
+ else
17
+ RED='\033[0;31m'
18
+ YELLOW='\033[1;33m'
19
+ GREEN='\033[0;32m'
20
+ BLUE='\033[0;34m'
21
+ NC='\033[0m' # No Color
22
+ fi
23
+
24
+ # Counters
25
+ ERRORS=0
26
+ WARNINGS=0
27
+ INFO=0
28
+
29
+ # mbake availability flag — set to 1 by setup_venv when the venv+mbake are ready.
30
+ # Stays 0 when python3/pip3 are absent or the venv/install step fails, allowing
31
+ # the validator to fall back to GNU make + custom checks only.
32
+ MBAKE_AVAILABLE=0
33
+ MBAKE_SKIP_INSTALL_MODE=0
34
+
35
+ case "${MBAKE_SKIP_INSTALL:-0}" in
36
+ 1|true|TRUE|yes|YES|on|ON)
37
+ MBAKE_SKIP_INSTALL_MODE=1
38
+ ;;
39
+ 0|false|FALSE|no|NO|off|OFF|"")
40
+ MBAKE_SKIP_INSTALL_MODE=0
41
+ ;;
42
+ *)
43
+ echo -e "${YELLOW}[WARNING]${NC} Unsupported MBAKE_SKIP_INSTALL='${MBAKE_SKIP_INSTALL}', treating as disabled"
44
+ ((WARNINGS+=1))
45
+ ;;
46
+ esac
47
+
48
+ # Temporary venv directory (unique per invocation, respects TMPDIR)
49
+ VENV_DIR="${TMPDIR:-/tmp}/makefile-validator-venv-$$"
50
+ CLEANUP_DONE=0
51
+
52
+ # Cleanup function - always runs on exit
53
+ cleanup() {
54
+ if [ "$CLEANUP_DONE" -eq 0 ]; then
55
+ CLEANUP_DONE=1
56
+ # Safety check: only remove if it's our temp venv (works with custom TMPDIR)
57
+ if [ -d "$VENV_DIR" ] && [[ "$VENV_DIR" == */makefile-validator-venv-* ]]; then
58
+ echo -e "${BLUE}[CLEANUP]${NC} Removing temporary venv..."
59
+ rm -rf "$VENV_DIR"
60
+ fi
61
+ fi
62
+ }
63
+
64
+ # Register cleanup trap for all exit scenarios
65
+ trap cleanup EXIT INT TERM
66
+
67
+ # Print error and exit
68
+ error_exit() {
69
+ echo -e "${RED}[ERROR]${NC} $1" >&2
70
+ exit 1
71
+ }
72
+
73
+ # Print section header
74
+ print_header() {
75
+ echo ""
76
+ echo "========================================"
77
+ echo "$1"
78
+ echo "========================================"
79
+ }
80
+
81
+ # Print sub-header
82
+ print_subheader() {
83
+ echo -e "\n${BLUE}[$1]${NC}"
84
+ }
85
+
86
+ # Check dependencies.
87
+ # python3 and pip3 are needed only for the mbake stages; their absence degrades
88
+ # coverage but does not abort — GNU make syntax and all custom checks still run.
89
+ check_dependencies() {
90
+ local mbake_prereqs_ok=1
91
+
92
+ if [ "$MBAKE_SKIP_INSTALL_MODE" -eq 1 ]; then
93
+ echo -e "${BLUE}ℹ${NC} MBAKE_SKIP_INSTALL=1 enabled — deterministic mode will skip mbake stages"
94
+ ((INFO+=1))
95
+ else
96
+ if ! command -v python3 &> /dev/null; then
97
+ echo -e "${YELLOW}[WARNING]${NC} python3 not found — mbake stages will be skipped"
98
+ echo " Install python3 to enable mbake validation and format-check coverage"
99
+ ((WARNINGS+=1))
100
+ mbake_prereqs_ok=0
101
+ fi
102
+
103
+ if ! command -v pip3 &> /dev/null; then
104
+ echo -e "${YELLOW}[WARNING]${NC} pip3 not found — mbake stages will be skipped"
105
+ echo " Install pip3 to enable mbake validation and format-check coverage"
106
+ ((WARNINGS+=1))
107
+ mbake_prereqs_ok=0
108
+ fi
109
+ fi
110
+
111
+ if ! command -v make &> /dev/null; then
112
+ echo -e "${YELLOW}[WARNING]${NC} GNU make not found — syntax validation will be limited"
113
+ ((WARNINGS+=1))
114
+ fi
115
+
116
+ # Signal to setup_venv (and main) that it is worth attempting venv setup
117
+ if [ "$MBAKE_SKIP_INSTALL_MODE" -eq 0 ] && [ "$mbake_prereqs_ok" -eq 1 ]; then
118
+ MBAKE_AVAILABLE=1
119
+ fi
120
+ }
121
+
122
+ # Setup virtual environment and install mbake.
123
+ # On failure (offline, proxy, bad python env) the function warns and clears
124
+ # MBAKE_AVAILABLE so the caller skips all mbake-dependent stages.
125
+ setup_venv() {
126
+ print_subheader "ENVIRONMENT SETUP"
127
+ echo "Creating temporary venv at: $VENV_DIR"
128
+
129
+ if ! python3 -m venv "$VENV_DIR" 2>&1; then
130
+ echo -e "${YELLOW}⚠${NC} Failed to create virtual environment — mbake stages will be skipped"
131
+ ((WARNINGS+=1))
132
+ MBAKE_AVAILABLE=0
133
+ return 0
134
+ fi
135
+
136
+ # Activate venv
137
+ # shellcheck source=/dev/null
138
+ source "$VENV_DIR/bin/activate"
139
+
140
+ echo "Installing mbake..."
141
+ if ! pip3 install --quiet mbake 2>&1; then
142
+ echo -e "${YELLOW}⚠${NC} Failed to install mbake — mbake stages will be skipped"
143
+ echo " Ensure network access or an internal PyPI mirror is available, then rerun"
144
+ ((WARNINGS+=1))
145
+ MBAKE_AVAILABLE=0
146
+ return 0
147
+ fi
148
+
149
+ MBAKE_AVAILABLE=1
150
+ echo -e "${GREEN}✓${NC} Environment ready"
151
+ }
152
+
153
+ # Validate Makefile exists and is readable
154
+ validate_file() {
155
+ local file=$1
156
+
157
+ if [ ! -f "$file" ]; then
158
+ error_exit "File not found: $file"
159
+ fi
160
+
161
+ if [ ! -r "$file" ]; then
162
+ error_exit "File not readable: $file"
163
+ fi
164
+ }
165
+
166
+ # Basic syntax check using GNU make
167
+ syntax_check() {
168
+ local file=$1
169
+ print_subheader "SYNTAX CHECK (GNU make)"
170
+
171
+ if ! command -v make &> /dev/null; then
172
+ echo -e "${YELLOW}⚠${NC} Skipped - GNU make not installed"
173
+ return 0
174
+ fi
175
+
176
+ # Get absolute path to Makefile
177
+ local abs_file
178
+ abs_file=$(cd "$(dirname "$file")" && pwd)/$(basename "$file")
179
+ local makefile_dir
180
+ makefile_dir=$(dirname "$abs_file")
181
+ local makefile_name
182
+ makefile_name=$(basename "$abs_file")
183
+
184
+ # Run make from the Makefile's directory to resolve relative paths correctly
185
+ # Capture output and exit code in a single invocation (avoids running make twice)
186
+ local make_output make_exit=0
187
+ make_output=$(cd "$makefile_dir" && make -f "$makefile_name" -n 2>&1) || make_exit=$?
188
+
189
+ if [ "$make_exit" -eq 0 ]; then
190
+ echo -e "${GREEN}✓${NC} No syntax errors found"
191
+ else
192
+ echo -e "${RED}✗${NC} Syntax errors detected:"
193
+ echo "$make_output"
194
+ ((ERRORS+=1))
195
+ return 1
196
+ fi
197
+ }
198
+
199
+ # Run mbake validation
200
+ mbake_validation() {
201
+ local file=$1
202
+ print_subheader "MBAKE VALIDATION"
203
+
204
+ echo "Running mbake validate..."
205
+ if mbake validate "$file" 2>&1; then
206
+ echo -e "${GREEN}✓${NC} mbake validation passed"
207
+ else
208
+ echo -e "${RED}✗${NC} mbake validation failed"
209
+ ((ERRORS+=1))
210
+ fi
211
+ }
212
+
213
+ # Run mbake format check
214
+ mbake_format_check() {
215
+ local file=$1
216
+ print_subheader "MBAKE FORMAT CHECK"
217
+
218
+ echo "Checking formatting consistency..."
219
+ # Capture output - mbake may wrap long lines
220
+ local format_output
221
+ format_output=$(mbake format --check "$file" 2>&1)
222
+ local format_exit=$?
223
+
224
+ # Join multi-line output for easier pattern matching (mbake wraps at ~80 chars)
225
+ # Also normalize whitespace (collapse multiple spaces to single space)
226
+ local format_oneline
227
+ format_oneline=$(echo "$format_output" | tr '\n' ' ' | tr -s ' ')
228
+
229
+ # Check for known false positives (mbake limitation with GNU Make special targets).
230
+ # mbake does not recognise many valid GNU Make special targets (.DELETE_ON_ERROR,
231
+ # .SUFFIXES, .ONESHELL, .POSIX, .PRECIOUS, .NOTPARALLEL, .INTERMEDIATE, etc.).
232
+ # The sed cleaning below already strips all of them; this flag just controls
233
+ # whether the "known mbake limitation" info note is printed.
234
+ local has_unknown_special_target=0
235
+ if echo "$format_oneline" | grep -qE "Unknown special target '\.[A-Z_]+'"; then
236
+ has_unknown_special_target=1
237
+ fi
238
+
239
+ # Check if there are real formatting issues ("Would reformat:" indicates changes needed)
240
+ local has_reformat=0
241
+ if echo "$format_oneline" | grep -q "Would reformat:"; then
242
+ has_reformat=1
243
+ fi
244
+
245
+ # Check for other errors (not related to unknown special targets)
246
+ local has_other_errors=0
247
+ # Remove known false positive patterns completely (including "Error:" before them)
248
+ # Pattern matches: "some_file.mk:0: Error: Unknown special target '.DELETE_ON_ERROR'"
249
+ local cleaned_output
250
+ cleaned_output=$(echo "$format_oneline" | sed -E "s/[^ ]*:[0-9]+: Error: Unknown special target '\.[A-Z_]+'//g")
251
+ if echo "$cleaned_output" | grep -qE "Error:|Fatal error"; then
252
+ has_other_errors=1
253
+ fi
254
+
255
+ # Decision logic
256
+ if [ $format_exit -eq 0 ]; then
257
+ echo -e "${GREEN}✓${NC} Formatting is consistent"
258
+ elif [ $has_unknown_special_target -eq 1 ] && [ $has_reformat -eq 0 ] && [ $has_other_errors -eq 0 ]; then
259
+ # Only false positive about unknown special targets - treat as success
260
+ echo -e "${GREEN}✓${NC} Formatting is consistent"
261
+ echo -e "${BLUE}ℹ${NC} mbake reported unknown special targets (this is a known mbake limitation)"
262
+ elif [ $has_reformat -eq 1 ] || [ $has_other_errors -eq 1 ]; then
263
+ # Real formatting issues or other errors exist
264
+ echo "$format_output" | grep -v "Unknown special target" | grep -v "^$" || true
265
+ echo -e "${YELLOW}⚠${NC} Formatting issues found"
266
+ echo ""
267
+ echo "Run 'mbake format $file' to fix formatting issues"
268
+ echo "Or run 'mbake format --diff $file' to preview changes"
269
+ ((WARNINGS+=1))
270
+ else
271
+ # Unknown case - show as info, don't warn
272
+ echo -e "${GREEN}✓${NC} Formatting is consistent"
273
+ if [ $has_unknown_special_target -eq 1 ]; then
274
+ echo -e "${BLUE}ℹ${NC} mbake reported unknown special targets (this is a known mbake limitation)"
275
+ fi
276
+ fi
277
+ }
278
+
279
+ # Custom security and best practice checks
280
+ custom_checks() {
281
+ local file=$1
282
+ print_subheader "CUSTOM CHECKS"
283
+
284
+ local found_issues=0
285
+
286
+ # ============================================================
287
+ # CRITICAL: Check for .DELETE_ON_ERROR (GNU Make best practice)
288
+ # ============================================================
289
+ if ! grep -q "^\.DELETE_ON_ERROR:" "$file"; then
290
+ echo -e "${YELLOW}⚠${NC} Missing .DELETE_ON_ERROR declaration"
291
+ echo " GNU Make recommends this to delete targets on recipe failure"
292
+ echo " Add '.DELETE_ON_ERROR:' at the top of your Makefile"
293
+ echo " See: https://www.gnu.org/software/make/manual/html_node/Special-Targets.html"
294
+ ((WARNINGS+=1))
295
+ found_issues=1
296
+ fi
297
+
298
+ # ============================================================
299
+ # Check for explicit SHELL setting (modern best practice)
300
+ # ============================================================
301
+ if ! grep -qE "^SHELL\s*:?=\s*(bash|/bin/bash|/usr/bin/bash)" "$file"; then
302
+ echo -e "${BLUE}ℹ${NC} No explicit SHELL setting"
303
+ echo " Consider 'SHELL := bash' for predictable behavior"
304
+ echo " See: https://tech.davis-hansson.com/p/make/"
305
+ ((INFO+=1))
306
+ found_issues=1
307
+ fi
308
+
309
+ # ============================================================
310
+ # Check for recommended MAKEFLAGS settings
311
+ # ============================================================
312
+ if ! grep -q "MAKEFLAGS.*--warn-undefined-variables" "$file"; then
313
+ echo -e "${BLUE}ℹ${NC} Consider 'MAKEFLAGS += --warn-undefined-variables'"
314
+ echo " This alerts you to undefined Make variable references"
315
+ ((INFO+=1))
316
+ found_issues=1
317
+ fi
318
+ if ! grep -q "MAKEFLAGS.*--no-builtin-rules" "$file"; then
319
+ echo -e "${BLUE}ℹ${NC} Consider 'MAKEFLAGS += --no-builtin-rules'"
320
+ echo " This disables built-in implicit rules for faster builds"
321
+ ((INFO+=1))
322
+ found_issues=1
323
+ fi
324
+
325
+ # Check for .PHONY declarations
326
+ if ! grep -q "^\.PHONY:" "$file"; then
327
+ echo -e "${YELLOW}⚠${NC} No .PHONY declarations found"
328
+ echo " Consider adding .PHONY for targets that don't create files"
329
+ echo " Example: .PHONY: clean test install"
330
+ ((WARNINGS+=1))
331
+ found_issues=1
332
+ fi
333
+
334
+ # Check for tabs vs spaces in recipes (improved regex)
335
+ # Catches 2, 4, or 8 space indentation that should be tabs
336
+ local space_lines
337
+ space_lines=$(grep -nE "^( | | )[a-zA-Z@\$\(]" "$file" 2>/dev/null | head -5) || true
338
+ if [ -n "$space_lines" ]; then
339
+ echo -e "${RED}✗${NC} Potential spaces instead of tabs in recipes detected:"
340
+ echo "$space_lines"
341
+ echo " Makefiles require TAB characters for recipe indentation"
342
+ ((ERRORS+=1))
343
+ found_issues=1
344
+ fi
345
+
346
+ # Check for hardcoded credentials (expanded pattern for common credential names)
347
+ # Note: grep -n adds "N:" prefix, so filter must match "N:# comment" format, not "# comment"
348
+ local cred_lines
349
+ cred_lines=$(grep -niE '(password|secret|api[_-]?key|apikey|token|private[_-]?key|aws_access_key|aws_secret_access_key|github_token|auth_token|credentials|azure_client_secret|database_url|db_password|ssh_key|ssl_key|encryption_key)\s*[:?]?=' "$file" 2>/dev/null | grep -vE "^[0-9]+:[[:space:]]*#" | head -3) || true
350
+ if [ -n "$cred_lines" ]; then
351
+ echo -e "${RED}✗${NC} Potential hardcoded credentials detected:"
352
+ echo "$cred_lines"
353
+ echo " Use environment variables or secret management instead"
354
+ ((ERRORS+=1))
355
+ found_issues=1
356
+ fi
357
+
358
+ # Check for TRULY unsafe variable expansion patterns
359
+ # Only flag variables that are NOT defined with defaults in the same file
360
+ # Look for rm/sudo/curl/wget with variables that could be empty or user-controlled
361
+ # Handles both UPPERCASE and lowercase variable names
362
+ local unsafe_vars=""
363
+ while IFS= read -r line; do
364
+ # Extract variable name from $(VAR) or $(var) pattern (any case)
365
+ var_name=$(echo "$line" | grep -oE '\$\([a-zA-Z_][a-zA-Z0-9_]*\)' | head -1 | tr -d '$()')
366
+ if [ -n "$var_name" ]; then
367
+ # Check if variable has a default value with := or ?=
368
+ if ! grep -qE "^${var_name}\s*[:?]=" "$file"; then
369
+ # Variable has no default, this is potentially unsafe
370
+ unsafe_vars="${unsafe_vars}${line}\n"
371
+ fi
372
+ fi
373
+ done < <(grep -E '\$\([a-zA-Z_][a-zA-Z0-9_]*\)' "$file" | grep -E '(rm -rf|sudo|curl|wget)')
374
+
375
+ if [ -n "$unsafe_vars" ]; then
376
+ echo -e "${YELLOW}⚠${NC} Variables without defaults used in dangerous commands:"
377
+ echo -e "$unsafe_vars" | head -3
378
+ echo " Consider adding default values (VAR := value) or validation"
379
+ ((WARNINGS+=1))
380
+ found_issues=1
381
+ fi
382
+
383
+ # Check for missing error handling in recipes
384
+ if grep -E "^\t[^@#-]" "$file" | grep -vE "set -e|pipefail|\|\||&&" | grep -q .; then
385
+ echo -e "${BLUE}ℹ${NC} Some recipe commands may lack error handling"
386
+ echo " Consider using 'set -e', '||', '&&' or '-' prefix for error control"
387
+ ((INFO+=1))
388
+ found_issues=1
389
+ fi
390
+
391
+ # Check for missing .INTERMEDIATE or .SECONDARY for temporary files
392
+ if grep -E "\.o|\.tmp|\.temp" "$file" | grep -q ":"; then
393
+ if ! grep -qE "^\.(INTERMEDIATE|SECONDARY):" "$file"; then
394
+ echo -e "${BLUE}ℹ${NC} Intermediate files detected (.o, .tmp, .temp)"
395
+ echo " Consider using .INTERMEDIATE or .SECONDARY for automatic cleanup"
396
+ ((INFO+=1))
397
+ found_issues=1
398
+ fi
399
+ fi
400
+
401
+ # Check for missing default target documentation
402
+ # Look for various patterns: "Default target", "(default)", "Main target", "default:" before all:
403
+ if ! grep -qE "^##?.*(([Dd]efault|[Mm]ain|[Ff]irst).*target|target.*(default|main)|^\s*all:.*#.*default|\(default\))" "$file"; then
404
+ # Also check if there's a ## comment right before "all:" target
405
+ if ! grep -B1 "^all:" "$file" | grep -qE "^##"; then
406
+ echo -e "${BLUE}ℹ${NC} No documentation for default target"
407
+ echo " Consider adding a comment explaining the default target"
408
+ ((INFO+=1))
409
+ found_issues=1
410
+ fi
411
+ fi
412
+
413
+ # Check for recursive variable expansion with shell commands (performance issue)
414
+ # Pattern uses single quotes so \$ reaches grep as a literal-dollar matcher,
415
+ # not as an end-of-line anchor (which double-quote expansion would produce).
416
+ local shell_lines
417
+ shell_lines=$(grep -nE '^\s*[A-Z_]+\s*=\s*\$\(shell' "$file" 2>/dev/null | head -3) || true
418
+ if [ -n "$shell_lines" ]; then
419
+ echo -e "${YELLOW}⚠${NC} Shell commands with recursive expansion '=' found:"
420
+ echo "$shell_lines"
421
+ echo " Use ':=' for immediate expansion to avoid repeated shell calls"
422
+ ((WARNINGS+=1))
423
+ found_issues=1
424
+ fi
425
+
426
+ # Check for .SUFFIXES (optimization for disabling built-in rules)
427
+ # This is informational - not all Makefiles need this
428
+ if grep -qE "^%\." "$file" || grep -qE "^\.[a-z]+\.[a-z]+:" "$file"; then
429
+ if ! grep -q "^\.SUFFIXES:" "$file"; then
430
+ echo -e "${BLUE}ℹ${NC} Pattern/suffix rules found but no .SUFFIXES declaration"
431
+ echo " Consider adding '.SUFFIXES:' to disable built-in rules for faster builds"
432
+ ((INFO+=1))
433
+ found_issues=1
434
+ fi
435
+ fi
436
+
437
+ # Check for using 'make' instead of '$(MAKE)' in recursive calls
438
+ # Exclude: echo statements, comments, and string literals
439
+ # Match 'make' followed by whitespace OR at end of line (bare 'make' with no args)
440
+ local make_lines
441
+ make_lines=$(grep -nE "^\t[^#@]*\bmake(\s|$)" "$file" 2>/dev/null | grep -vE '(echo|printf|MAKE\)|".*make.*"|'"'"'.*make.*'"'"')' | head -3) || true
442
+ if [ -n "$make_lines" ]; then
443
+ echo -e "${YELLOW}⚠${NC} Direct 'make' call in recipe (should use \$(MAKE)):"
444
+ echo "$make_lines"
445
+ echo " Use '\$(MAKE)' for recursive make calls to preserve flags and options"
446
+ ((WARNINGS+=1))
447
+ found_issues=1
448
+ fi
449
+
450
+ # Check for .SUFFIXES recommendation for large Makefiles
451
+ local target_count
452
+ target_count=$(grep -cE "^[a-zA-Z_][a-zA-Z0-9_-]*:" "$file" 2>/dev/null) || target_count=0
453
+ if [ "$target_count" -gt 10 ] && ! grep -q "^\.SUFFIXES:" "$file"; then
454
+ echo -e "${BLUE}ℹ${NC} Large Makefile ($target_count targets) without .SUFFIXES"
455
+ echo " Consider adding '.SUFFIXES:' to disable built-in rules for faster builds"
456
+ ((INFO+=1))
457
+ found_issues=1
458
+ fi
459
+
460
+ # ============================================================
461
+ # Check for .ONESHELL without proper error handling
462
+ # ============================================================
463
+ if grep -q "^\.ONESHELL:" "$file"; then
464
+ # .ONESHELL is used - check if .SHELLFLAGS includes recommended flags
465
+ local has_shellflags=0
466
+ local has_e_flag=0
467
+ local has_u_flag=0
468
+ local has_pipefail=0
469
+
470
+ if grep -qE "^\.?SHELLFLAGS\s*:?=" "$file"; then
471
+ has_shellflags=1
472
+ local shellflags_line
473
+ shellflags_line=$(grep -E "^\.?SHELLFLAGS\s*:?=" "$file" | head -1)
474
+ # Check for -e flag (can be standalone -e or combined like -eu, -euo, etc.)
475
+ # Match: -e, -eu, -euo, -eux, etc. (e after dash, possibly with other letters)
476
+ if [[ "$shellflags_line" =~ -[a-zA-Z]*e[a-zA-Z]* ]] || [[ "$shellflags_line" == *"-e "* ]] || [[ "$shellflags_line" == *"-e\""* ]]; then
477
+ has_e_flag=1
478
+ fi
479
+ # Check for -u flag (can be standalone -u or combined like -eu, -euo, etc.)
480
+ if [[ "$shellflags_line" =~ -[a-zA-Z]*u[a-zA-Z]* ]] || [[ "$shellflags_line" == *"-u "* ]] || [[ "$shellflags_line" == *"-u\""* ]]; then
481
+ has_u_flag=1
482
+ fi
483
+ # Check for pipefail (always as -o pipefail)
484
+ [[ "$shellflags_line" == *"pipefail"* ]] && has_pipefail=1
485
+ fi
486
+
487
+ if [ "$has_shellflags" -eq 0 ]; then
488
+ # No SHELLFLAGS at all, check if recipes commonly use set -e
489
+ local oneshell_recipes
490
+ oneshell_recipes=$(grep -cE "^\t" "$file" 2>/dev/null) || oneshell_recipes=0
491
+ local set_e_count
492
+ set_e_count=$(grep -cE "^\t.*set -e" "$file" 2>/dev/null) || set_e_count=0
493
+
494
+ # If less than 33% of recipe blocks have set -e, warn
495
+ if [ "$oneshell_recipes" -gt 0 ] && [ "$set_e_count" -lt $((oneshell_recipes / 3)) ]; then
496
+ echo -e "${YELLOW}⚠${NC} .ONESHELL used without .SHELLFLAGS"
497
+ echo " With .ONESHELL, recipe errors (except the last line) are silently ignored"
498
+ echo " Fix: Add '.SHELLFLAGS := -eu -o pipefail -c'"
499
+ echo " See: https://www.gnu.org/software/make/manual/html_node/One-Shell.html"
500
+ ((WARNINGS+=1))
501
+ found_issues=1
502
+ fi
503
+ elif [ "$has_e_flag" -eq 0 ]; then
504
+ echo -e "${YELLOW}⚠${NC} .ONESHELL with SHELLFLAGS missing -e flag"
505
+ echo " Without -e, errors in recipe commands are ignored"
506
+ echo " Consider: .SHELLFLAGS := -eu -o pipefail -c"
507
+ ((WARNINGS+=1))
508
+ found_issues=1
509
+ else
510
+ # Has -e, but recommend full flags as info
511
+ if [ "$has_u_flag" -eq 0 ] || [ "$has_pipefail" -eq 0 ]; then
512
+ echo -e "${BLUE}ℹ${NC} .SHELLFLAGS could include additional safety flags"
513
+ echo " Recommended: .SHELLFLAGS := -eu -o pipefail -c"
514
+ echo " -u: error on undefined variables, -o pipefail: catch pipe failures"
515
+ ((INFO+=1))
516
+ found_issues=1
517
+ fi
518
+ fi
519
+ fi
520
+
521
+ # ============================================================
522
+ # Check for .EXPORT_ALL_VARIABLES (security concern)
523
+ # ============================================================
524
+ if grep -q "^\.EXPORT_ALL_VARIABLES:" "$file"; then
525
+ echo -e "${YELLOW}⚠${NC} .EXPORT_ALL_VARIABLES used - security consideration"
526
+ echo " This exports ALL Make variables to subprocesses, potentially leaking sensitive data"
527
+ echo " Consider using 'export VAR' for specific variables instead"
528
+ ((WARNINGS+=1))
529
+ found_issues=1
530
+ fi
531
+
532
+ # ============================================================
533
+ # Check for order-only prerequisites (directory best practice)
534
+ # ============================================================
535
+ # If mkdir -p is used in recipes and order-only | syntax is not used
536
+ if grep -qE "^\t.*mkdir.*-p" "$file"; then
537
+ if ! grep -qE '\| \$\(' "$file"; then
538
+ echo -e "${BLUE}ℹ${NC} mkdir in recipes without order-only prerequisites"
539
+ echo " Consider using order-only prerequisites for directories:"
540
+ echo " Example: \$(BUILD_DIR)/app: \$(SOURCES) | \$(BUILD_DIR)"
541
+ echo " This prevents unnecessary rebuilds when only timestamps change"
542
+ ((INFO+=1))
543
+ found_issues=1
544
+ fi
545
+ fi
546
+
547
+ # ============================================================
548
+ # Check for parallel-unsafe patterns without .NOTPARALLEL
549
+ # ============================================================
550
+ if grep -qE "^\t.*(docker build|npm install|pip install|yarn install|bundle install)" "$file"; then
551
+ if ! grep -q "^\.NOTPARALLEL:" "$file"; then
552
+ echo -e "${BLUE}ℹ${NC} Parallel-sensitive commands detected (npm/docker/pip install)"
553
+ echo " Consider using .NOTPARALLEL for targets with these commands"
554
+ echo " Or add proper dependencies to prevent race conditions"
555
+ ((INFO+=1))
556
+ found_issues=1
557
+ fi
558
+ fi
559
+
560
+ if [ "$found_issues" -eq 0 ]; then
561
+ echo -e "${GREEN}✓${NC} No additional issues found"
562
+ fi
563
+ }
564
+
565
+ # Run checkmake if available
566
+ checkmake_validation() {
567
+ local file=$1
568
+ print_subheader "CHECKMAKE VALIDATION (optional)"
569
+
570
+ if ! command -v checkmake &> /dev/null; then
571
+ echo -e "${BLUE}ℹ${NC} checkmake not installed - skipping additional linting"
572
+ echo " Install with: go install github.com/checkmake/checkmake/cmd/checkmake@latest"
573
+ return 0
574
+ fi
575
+
576
+ echo "Running checkmake..."
577
+ local checkmake_output
578
+ checkmake_output=$(checkmake "$file" 2>&1) || true
579
+ if [ -n "$checkmake_output" ]; then
580
+ echo "$checkmake_output"
581
+ # Count warnings from checkmake
582
+ local cm_warnings
583
+ cm_warnings=$(echo "$checkmake_output" | grep -c "WARN" 2>/dev/null) || cm_warnings=0
584
+ if [ "$cm_warnings" -gt 0 ]; then
585
+ ((WARNINGS+=cm_warnings))
586
+ fi
587
+ else
588
+ echo -e "${GREEN}✓${NC} checkmake validation passed"
589
+ fi
590
+ }
591
+
592
+ # Run unmake if available (for POSIX portability checks)
593
+ unmake_validation() {
594
+ local file=$1
595
+ print_subheader "UNMAKE VALIDATION (optional)"
596
+
597
+ if ! command -v unmake &> /dev/null; then
598
+ echo -e "${BLUE}ℹ${NC} unmake not installed - skipping POSIX portability checks"
599
+ echo " See: https://github.com/mcandre/unmake"
600
+ return 0
601
+ fi
602
+
603
+ echo "Running unmake for POSIX portability..."
604
+ local unmake_output
605
+ unmake_output=$(unmake "$file" 2>&1) || true
606
+ if [ -n "$unmake_output" ]; then
607
+ echo "$unmake_output"
608
+ # Count warnings from unmake
609
+ local um_warnings
610
+ um_warnings=$(echo "$unmake_output" | grep -cE "(warning|Warning)" 2>/dev/null) || um_warnings=0
611
+ if [ "$um_warnings" -gt 0 ]; then
612
+ ((WARNINGS+=um_warnings))
613
+ fi
614
+ else
615
+ echo -e "${GREEN}✓${NC} unmake validation passed (POSIX compatible)"
616
+ fi
617
+ }
618
+
619
+ # Print summary
620
+ print_summary() {
621
+ local file=$1
622
+ print_header "VALIDATION SUMMARY"
623
+ echo "File: $file"
624
+ echo ""
625
+ echo -e "${RED}Errors: ${NC} $ERRORS"
626
+ echo -e "${YELLOW}Warnings:${NC} $WARNINGS"
627
+ echo -e "${BLUE}Info: ${NC} $INFO"
628
+ echo ""
629
+
630
+ if [ "$ERRORS" -gt 0 ]; then
631
+ echo -e "${RED}⚠ Validation FAILED - errors must be fixed${NC}"
632
+ return 2
633
+ elif [ "$WARNINGS" -gt 0 ]; then
634
+ echo -e "${YELLOW}⚠ Validation PASSED with warnings${NC}"
635
+ return 1
636
+ else
637
+ echo -e "${GREEN}✓ Validation PASSED${NC}"
638
+ return 0
639
+ fi
640
+ }
641
+
642
+ # Main execution
643
+ main() {
644
+ if [ $# -eq 0 ]; then
645
+ echo "Usage: $0 <Makefile>"
646
+ echo ""
647
+ echo "Validates Makefile for syntax errors, best practices, security issues,"
648
+ echo "and optimization opportunities using mbake and custom checks."
649
+ echo ""
650
+ echo "Examples:"
651
+ echo " $0 Makefile"
652
+ echo " $0 path/to/Makefile"
653
+ echo " $0 project.mk"
654
+ exit 1
655
+ fi
656
+
657
+ local makefile=$1
658
+
659
+ print_header "MAKEFILE VALIDATOR"
660
+ echo "File: $makefile"
661
+
662
+ # Validation pipeline
663
+ check_dependencies
664
+ validate_file "$makefile"
665
+
666
+ # Set up mbake venv only when python3+pip3 are present and skip mode is disabled.
667
+ if [ "$MBAKE_AVAILABLE" -eq 1 ]; then
668
+ setup_venv || true
669
+ fi
670
+
671
+ # Run syntax check — always runs (uses GNU make, not mbake)
672
+ syntax_check "$makefile" || true
673
+
674
+ # Run mbake stages only when the venv is ready
675
+ if [ "$MBAKE_AVAILABLE" -eq 1 ]; then
676
+ mbake_validation "$makefile" || true
677
+ mbake_format_check "$makefile" || true
678
+ else
679
+ print_subheader "MBAKE VALIDATION"
680
+ if [ "$MBAKE_SKIP_INSTALL_MODE" -eq 1 ]; then
681
+ echo -e "${BLUE}ℹ${NC} Skipped — MBAKE_SKIP_INSTALL=1 (deterministic mode)"
682
+ else
683
+ echo -e "${BLUE}ℹ${NC} Skipped — mbake not available (python3, pip3, and network required)"
684
+ fi
685
+ print_subheader "MBAKE FORMAT CHECK"
686
+ if [ "$MBAKE_SKIP_INSTALL_MODE" -eq 1 ]; then
687
+ echo -e "${BLUE}ℹ${NC} Skipped — MBAKE_SKIP_INSTALL=1 (deterministic mode)"
688
+ else
689
+ echo -e "${BLUE}ℹ${NC} Skipped — mbake not available (python3, pip3, and network required)"
690
+ fi
691
+ ((INFO+=2))
692
+ fi
693
+
694
+ # Run custom checks — always runs (pure bash + grep, no external tools)
695
+ custom_checks "$makefile" || true
696
+
697
+ # Run checkmake if available (optional, continue even if it fails)
698
+ checkmake_validation "$makefile" || true
699
+
700
+ # Run unmake if available (optional, continue even if it fails)
701
+ unmake_validation "$makefile" || true
702
+
703
+ # Print summary and return appropriate exit code
704
+ print_summary "$makefile"
705
+ local exit_code=$?
706
+
707
+ return $exit_code
708
+ }
709
+
710
+ # Run main with all arguments and exit with its return code
711
+ main "$@"
712
+ exit $?