@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,174 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # ShellCheck Wrapper with Temporary Virtual Environment
4
+ #
5
+ # This script creates a temporary Python virtual environment, installs shellcheck-py,
6
+ # runs shellcheck, and cleans up afterwards.
7
+ #
8
+ # Usage: ./shellcheck_wrapper.sh [shellcheck-options] <script-file>
9
+ #
10
+
11
+ set -euo pipefail
12
+
13
+ # Colors
14
+ RED='\033[0;31m'
15
+ GREEN='\033[0;32m'
16
+ YELLOW='\033[1;33m'
17
+ BLUE='\033[0;34m'
18
+ NC='\033[0m' # No Color
19
+
20
+ # Configuration
21
+ VENV_DIR=""
22
+ CLEANUP_ON_EXIT=true
23
+ CACHE_VENV=false
24
+ VENV_CACHE_DIR="${HOME}/.cache/bash-script-validator/shellcheck-venv"
25
+
26
+ # Cleanup function
27
+ cleanup() {
28
+ if [[ "$CLEANUP_ON_EXIT" == "true" ]] && [[ -n "$VENV_DIR" ]] && [[ -d "$VENV_DIR" ]]; then
29
+ echo -e "${BLUE}[INFO]${NC} Cleaning up temporary virtual environment..." >&2
30
+ rm -rf "$VENV_DIR"
31
+ fi
32
+ }
33
+
34
+ trap cleanup EXIT INT TERM
35
+
36
+ # Check if Python 3 is available
37
+ check_python() {
38
+ if ! command -v python3 &>/dev/null; then
39
+ echo -e "${RED}[ERROR]${NC} python3 not found. Please install Python 3." >&2
40
+ exit 1
41
+ fi
42
+ }
43
+
44
+ # Check if shellcheck is already available system-wide
45
+ check_system_shellcheck() {
46
+ if command -v shellcheck &>/dev/null; then
47
+ # ShellCheck is available, use it directly
48
+ shellcheck "$@"
49
+ exit $?
50
+ fi
51
+ }
52
+
53
+ # Create and activate virtual environment
54
+ setup_venv() {
55
+ if [[ "$CACHE_VENV" == "true" ]] && [[ -d "$VENV_CACHE_DIR" ]]; then
56
+ echo -e "${GREEN}[INFO]${NC} Using cached virtual environment..." >&2
57
+ VENV_DIR="$VENV_CACHE_DIR"
58
+ CLEANUP_ON_EXIT=false
59
+ return 0
60
+ fi
61
+
62
+ echo -e "${BLUE}[INFO]${NC} Creating temporary virtual environment..." >&2
63
+
64
+ if [[ "$CACHE_VENV" == "true" ]]; then
65
+ mkdir -p "$(dirname "$VENV_CACHE_DIR")"
66
+ VENV_DIR="$VENV_CACHE_DIR"
67
+ CLEANUP_ON_EXIT=false
68
+ else
69
+ VENV_DIR=$(mktemp -d -t shellcheck-venv.XXXXXX)
70
+ fi
71
+
72
+ python3 -m venv "$VENV_DIR"
73
+
74
+ # Activate virtual environment
75
+ # shellcheck source=/dev/null
76
+ source "$VENV_DIR/bin/activate"
77
+ }
78
+
79
+ # Install shellcheck-py
80
+ install_shellcheck() {
81
+ local marker_file="$VENV_DIR/.shellcheck_installed"
82
+
83
+ if [[ -f "$marker_file" ]]; then
84
+ echo -e "${GREEN}[INFO]${NC} ShellCheck already installed in cached venv" >&2
85
+ return 0
86
+ fi
87
+
88
+ echo -e "${BLUE}[INFO]${NC} Installing shellcheck-py..." >&2
89
+
90
+ # Upgrade pip first (suppress output)
91
+ pip3 install --upgrade pip &>/dev/null
92
+
93
+ # Install shellcheck-py
94
+ if pip3 install shellcheck-py &>/dev/null; then
95
+ echo -e "${GREEN}[INFO]${NC} ShellCheck installed successfully" >&2
96
+ touch "$marker_file"
97
+ return 0
98
+ else
99
+ echo -e "${RED}[ERROR]${NC} Failed to install shellcheck-py" >&2
100
+ return 1
101
+ fi
102
+ }
103
+
104
+ # Run shellcheck
105
+ run_shellcheck() {
106
+ if [[ ! -f "$VENV_DIR/bin/shellcheck" ]]; then
107
+ echo -e "${RED}[ERROR]${NC} ShellCheck binary not found in virtual environment" >&2
108
+ exit 1
109
+ fi
110
+
111
+ # Run shellcheck with all provided arguments
112
+ "$VENV_DIR/bin/shellcheck" "$@"
113
+ }
114
+
115
+ # Main function
116
+ main() {
117
+ # If no arguments, show usage
118
+ if [[ $# -eq 0 ]]; then
119
+ echo "Usage: $0 [--cache] [--no-cache] [shellcheck-options] <script-file>"
120
+ echo ""
121
+ echo "Options:"
122
+ echo " --cache Cache the virtual environment for faster subsequent runs"
123
+ echo " --no-cache Don't use cached venv (default)"
124
+ echo " --clear-cache Clear the cached virtual environment"
125
+ echo ""
126
+ echo "Examples:"
127
+ echo " $0 script.sh"
128
+ echo " $0 --cache -s bash script.sh"
129
+ echo " $0 -f gcc script.sh"
130
+ exit 0
131
+ fi
132
+
133
+ # Parse wrapper-specific options
134
+ local args=()
135
+ while [[ $# -gt 0 ]]; do
136
+ case "$1" in
137
+ --cache)
138
+ CACHE_VENV=true
139
+ shift
140
+ ;;
141
+ --no-cache)
142
+ CACHE_VENV=false
143
+ shift
144
+ ;;
145
+ --clear-cache)
146
+ if [[ -d "$VENV_CACHE_DIR" ]]; then
147
+ echo -e "${BLUE}[INFO]${NC} Clearing cached virtual environment..."
148
+ rm -rf "$VENV_CACHE_DIR"
149
+ echo -e "${GREEN}[INFO]${NC} Cache cleared"
150
+ else
151
+ echo -e "${YELLOW}[INFO]${NC} No cache to clear"
152
+ fi
153
+ exit 0
154
+ ;;
155
+ *)
156
+ args+=("$1")
157
+ shift
158
+ ;;
159
+ esac
160
+ done
161
+
162
+ # Check if system shellcheck is available
163
+ check_system_shellcheck "${args[@]}"
164
+
165
+ # System shellcheck not found, use venv approach
166
+ check_python
167
+ setup_venv
168
+ install_shellcheck
169
+
170
+ # Run shellcheck with remaining arguments
171
+ run_shellcheck "${args[@]}"
172
+ }
173
+
174
+ main "$@"
@@ -0,0 +1,446 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Regression test suite for validate.sh
4
+ #
5
+ # Runs the validator against each example file and asserts the expected exit code.
6
+ # Exit 0 when all assertions pass; non-zero otherwise.
7
+ #
8
+
9
+ set -euo pipefail
10
+
11
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
+ readonly SCRIPT_DIR
13
+ VALIDATOR="$SCRIPT_DIR/validate.sh"
14
+ EXAMPLES_DIR="$SCRIPT_DIR/../examples"
15
+ TMP_DIR="$(mktemp -d)"
16
+ FAKE_SHELLCHECK_BIN="$TMP_DIR/fake-shellcheck-bin"
17
+
18
+ cleanup() {
19
+ rm -rf "$TMP_DIR"
20
+ }
21
+ trap cleanup EXIT
22
+
23
+ # Counters
24
+ PASS=0
25
+ FAIL=0
26
+
27
+ # ─── helpers ────────────────────────────────────────────────────────────────
28
+
29
+ pass() { echo " PASS: $1"; PASS=$((PASS + 1)); }
30
+ fail() { echo " FAIL: $1"; FAIL=$((FAIL + 1)); }
31
+
32
+ # Create a deterministic shellcheck stub so regression tests do not depend on host tooling.
33
+ mkdir -p "$FAKE_SHELLCHECK_BIN"
34
+ cat > "$FAKE_SHELLCHECK_BIN/shellcheck" <<'EOF'
35
+ #!/usr/bin/env bash
36
+ set -euo pipefail
37
+
38
+ mode="${FAKE_SHELLCHECK_MODE:-ok}"
39
+ target="${@: -1}"
40
+
41
+ case "$mode" in
42
+ ok)
43
+ exit 0
44
+ ;;
45
+ warning)
46
+ echo "${target}:1:1: warning: fake shellcheck warning [SC9999]"
47
+ exit 1
48
+ ;;
49
+ infra)
50
+ echo "fake shellcheck infrastructure failure" >&2
51
+ exit 3
52
+ ;;
53
+ *)
54
+ echo "unknown FAKE_SHELLCHECK_MODE: $mode" >&2
55
+ exit 2
56
+ ;;
57
+ esac
58
+ EOF
59
+ chmod +x "$FAKE_SHELLCHECK_BIN/shellcheck"
60
+
61
+ # Run the validator and return its exit code without aborting this script.
62
+ # Uses || to prevent set -e from treating a non-zero validator exit as fatal.
63
+ run_validator() {
64
+ local file="$1"
65
+ shift
66
+
67
+ local exit_code=0
68
+ if [[ $# -gt 0 ]]; then
69
+ env "$@" bash "$VALIDATOR" "$file" >/dev/null 2>&1 || exit_code=$?
70
+ else
71
+ bash "$VALIDATOR" "$file" >/dev/null 2>&1 || exit_code=$?
72
+ fi
73
+ echo "$exit_code"
74
+ }
75
+
76
+ validator_output() {
77
+ local file="$1"
78
+ shift
79
+
80
+ if [[ $# -gt 0 ]]; then
81
+ env "$@" bash "$VALIDATOR" "$file" 2>&1 || true
82
+ else
83
+ bash "$VALIDATOR" "$file" 2>&1 || true
84
+ fi
85
+ }
86
+
87
+ # Assert that the validator exits with a specific code for the given file.
88
+ assert_exit_code() {
89
+ local label="$1"
90
+ local file="$2"
91
+ local expected="$3"
92
+ shift 3
93
+
94
+ local actual
95
+ actual=$(run_validator "$file" "$@")
96
+
97
+ if [[ "$actual" -eq "$expected" ]]; then
98
+ pass "$label (exit $actual)"
99
+ else
100
+ fail "$label — expected exit $expected, got $actual"
101
+ # Re-run with output visible so the failure is diagnosable.
102
+ echo " --- validator output ---"
103
+ validator_output "$file" "$@" | sed 's/^/ /'
104
+ echo " --- end output ---"
105
+ fi
106
+ }
107
+
108
+ assert_exit_code_in() {
109
+ local label="$1"
110
+ local file="$2"
111
+ local expected_csv="$3"
112
+ shift 3
113
+
114
+ local actual
115
+ actual=$(run_validator "$file" "$@")
116
+
117
+ if [[ ",$expected_csv," == *",$actual,"* ]]; then
118
+ pass "$label (exit $actual)"
119
+ else
120
+ fail "$label — expected one of [$expected_csv], got $actual"
121
+ echo " --- validator output ---"
122
+ validator_output "$file" "$@" | sed 's/^/ /'
123
+ echo " --- end output ---"
124
+ fi
125
+ }
126
+
127
+ # Assert that a pattern IS found in the validator output for a given file.
128
+ assert_output_contains() {
129
+ local label="$1"
130
+ local file="$2"
131
+ local pattern="$3"
132
+ shift 3
133
+
134
+ local output
135
+ output=$(validator_output "$file" "$@")
136
+
137
+ if echo "$output" | grep -qE "$pattern"; then
138
+ pass "$label"
139
+ else
140
+ fail "$label — pattern not found: $pattern"
141
+ echo " --- validator output ---"
142
+ echo "$output" | sed 's/^/ /'
143
+ echo " --- end output ---"
144
+ fi
145
+ }
146
+
147
+ # Assert that a pattern is NOT found in the validator output for a given file.
148
+ assert_output_not_contains() {
149
+ local label="$1"
150
+ local file="$2"
151
+ local pattern="$3"
152
+ shift 3
153
+
154
+ local output
155
+ output=$(validator_output "$file" "$@")
156
+
157
+ if echo "$output" | grep -qE "$pattern"; then
158
+ fail "$label — unexpected pattern found: $pattern"
159
+ echo " --- validator output ---"
160
+ echo "$output" | sed 's/^/ /'
161
+ echo " --- end output ---"
162
+ else
163
+ pass "$label"
164
+ fi
165
+ }
166
+
167
+ assert_shellcheck_stage_verifiable() {
168
+ local label="$1"
169
+ local file="$2"
170
+ shift 2
171
+
172
+ local output
173
+ output=$(validator_output "$file" "$@")
174
+
175
+ if ! echo "$output" | grep -q '\[SHELLCHECK\]'; then
176
+ fail "$label — missing [SHELLCHECK] stage"
177
+ echo " --- validator output ---"
178
+ echo "$output" | sed 's/^/ /'
179
+ echo " --- end output ---"
180
+ return
181
+ fi
182
+
183
+ if echo "$output" | grep -qE 'ShellCheck unavailable|ShellCheck not installed'; then
184
+ fail "$label — ShellCheck stage unavailable"
185
+ echo " --- validator output ---"
186
+ echo "$output" | sed 's/^/ /'
187
+ echo " --- end output ---"
188
+ return
189
+ fi
190
+
191
+ if echo "$output" | grep -qE 'No ShellCheck issues found|SC[0-9]{4}|: (error|warning|note|style):'; then
192
+ pass "$label"
193
+ return
194
+ fi
195
+
196
+ fail "$label — ShellCheck produced no verifiable signal"
197
+ echo " --- validator output ---"
198
+ echo "$output" | sed 's/^/ /'
199
+ echo " --- end output ---"
200
+ }
201
+
202
+ # ─── test cases ─────────────────────────────────────────────────────────────
203
+
204
+ echo "Running bash-script-validator tests..."
205
+ echo ""
206
+
207
+ # --- good-bash.sh: well-written bash, must exit 0 ---
208
+ echo "[good-bash.sh]"
209
+ assert_exit_code \
210
+ "exits cleanly (code 0)" \
211
+ "$EXAMPLES_DIR/good-bash.sh" \
212
+ 0
213
+
214
+ assert_output_not_contains \
215
+ "no false-positive errors" \
216
+ "$EXAMPLES_DIR/good-bash.sh" \
217
+ "✗"
218
+
219
+ assert_shellcheck_stage_verifiable \
220
+ "shellcheck stage is verifiable" \
221
+ "$EXAMPLES_DIR/good-bash.sh"
222
+
223
+ # --- good-shell.sh: well-written POSIX sh, must exit 0 ---
224
+ echo ""
225
+ echo "[good-shell.sh]"
226
+ assert_exit_code \
227
+ "exits cleanly (code 0)" \
228
+ "$EXAMPLES_DIR/good-shell.sh" \
229
+ 0
230
+
231
+ assert_output_not_contains \
232
+ "no false-positive [[ ]] error from comment on line 31" \
233
+ "$EXAMPLES_DIR/good-shell.sh" \
234
+ "\[\["
235
+
236
+ assert_output_not_contains \
237
+ "no false-positive errors" \
238
+ "$EXAMPLES_DIR/good-shell.sh" \
239
+ "✗"
240
+
241
+ assert_shellcheck_stage_verifiable \
242
+ "shellcheck stage is verifiable" \
243
+ "$EXAMPLES_DIR/good-shell.sh"
244
+
245
+ # --- bad-bash.sh: intentionally bad bash, must be non-clean ---
246
+ echo ""
247
+ echo "[bad-bash.sh]"
248
+ assert_exit_code_in \
249
+ "exits non-clean (code 1 or 2)" \
250
+ "$EXAMPLES_DIR/bad-bash.sh" \
251
+ "1,2"
252
+
253
+ assert_output_contains \
254
+ "detects eval with variable" \
255
+ "$EXAMPLES_DIR/bad-bash.sh" \
256
+ "eval with variable"
257
+
258
+ assert_output_contains \
259
+ "detects useless cat" \
260
+ "$EXAMPLES_DIR/bad-bash.sh" \
261
+ "Useless use of cat"
262
+
263
+ # --- bad-shell.sh: intentionally bad POSIX sh, must exit 2 ---
264
+ echo ""
265
+ echo "[bad-shell.sh]"
266
+ assert_exit_code \
267
+ "exits with errors (code 2)" \
268
+ "$EXAMPLES_DIR/bad-shell.sh" \
269
+ 2
270
+
271
+ assert_output_contains \
272
+ "detects [[ ]] in sh script (line 7, actual code)" \
273
+ "$EXAMPLES_DIR/bad-shell.sh" \
274
+ "Bash-specific \[\[ \]\]"
275
+
276
+ assert_output_contains \
277
+ "detects bash arrays in sh script" \
278
+ "$EXAMPLES_DIR/bad-shell.sh" \
279
+ "Bash-specific arrays"
280
+
281
+ assert_output_contains \
282
+ "detects function keyword in sh script" \
283
+ "$EXAMPLES_DIR/bad-shell.sh" \
284
+ "function.*keyword"
285
+
286
+ assert_output_contains \
287
+ "detects source command in sh script" \
288
+ "$EXAMPLES_DIR/bad-shell.sh" \
289
+ "source.*command"
290
+
291
+ assert_output_contains \
292
+ "detects eval with variable" \
293
+ "$EXAMPLES_DIR/bad-shell.sh" \
294
+ "eval with variable"
295
+
296
+ assert_output_contains \
297
+ "detects useless cat" \
298
+ "$EXAMPLES_DIR/bad-shell.sh" \
299
+ "Useless use of cat"
300
+
301
+ # Only real code lines flagged — not comment lines for [[ check
302
+ assert_output_not_contains \
303
+ "[[ check does not flag comment lines in bad-shell.sh" \
304
+ "$EXAMPLES_DIR/bad-shell.sh" \
305
+ "Line [0-9]*:# Bad: using bash-specific"
306
+
307
+ # --- p1 regression fixtures ---
308
+ INDENTED_SH="$TMP_DIR/indented-bashisms.sh"
309
+ cat > "$INDENTED_SH" <<'EOF'
310
+ #!/bin/sh
311
+ set -e
312
+ function helper {
313
+ echo "bad"
314
+ }
315
+ source /etc/profile
316
+ helper
317
+ EOF
318
+
319
+ COMMENT_ONLY_ERR_HANDLING="$TMP_DIR/comment-only-error-handling.sh"
320
+ cat > "$COMMENT_ONLY_ERR_HANDLING" <<'EOF'
321
+ #!/bin/sh
322
+ # set -e
323
+ # set -o errexit
324
+ # trap 'echo failed' ERR
325
+ echo "hello"
326
+ EOF
327
+
328
+ echo ""
329
+ echo "[p1 regressions]"
330
+ assert_output_contains \
331
+ "indented function in sh is detected" \
332
+ "$INDENTED_SH" \
333
+ "function.*keyword"
334
+
335
+ assert_output_contains \
336
+ "indented source in sh is detected" \
337
+ "$INDENTED_SH" \
338
+ "source.*command"
339
+
340
+ assert_output_contains \
341
+ "comment-only set -e does not suppress warning" \
342
+ "$COMMENT_ONLY_ERR_HANDLING" \
343
+ "Consider adding error handling"
344
+
345
+ # --- p2 deterministic shellcheck stage regressions ---
346
+ echo ""
347
+ echo "[p2 shellcheck-mode regressions]"
348
+
349
+ assert_exit_code \
350
+ "system mode accepts deterministic clean shellcheck run" \
351
+ "$EXAMPLES_DIR/good-bash.sh" \
352
+ 0 \
353
+ VALIDATOR_REQUIRE_SHELLCHECK=1 \
354
+ VALIDATOR_SHELLCHECK_MODE=system \
355
+ PATH="$FAKE_SHELLCHECK_BIN:/usr/bin:/bin"
356
+
357
+ assert_output_contains \
358
+ "system mode reports clean shellcheck output" \
359
+ "$EXAMPLES_DIR/good-bash.sh" \
360
+ "No ShellCheck issues found" \
361
+ VALIDATOR_REQUIRE_SHELLCHECK=1 \
362
+ VALIDATOR_SHELLCHECK_MODE=system \
363
+ PATH="$FAKE_SHELLCHECK_BIN:/usr/bin:/bin"
364
+
365
+ assert_exit_code \
366
+ "system mode returns warning on shellcheck findings" \
367
+ "$EXAMPLES_DIR/good-bash.sh" \
368
+ 1 \
369
+ VALIDATOR_REQUIRE_SHELLCHECK=1 \
370
+ VALIDATOR_SHELLCHECK_MODE=system \
371
+ FAKE_SHELLCHECK_MODE=warning \
372
+ PATH="$FAKE_SHELLCHECK_BIN:/usr/bin:/bin"
373
+
374
+ assert_output_contains \
375
+ "system mode surfaces shellcheck issue codes" \
376
+ "$EXAMPLES_DIR/good-bash.sh" \
377
+ "SC9999" \
378
+ VALIDATOR_REQUIRE_SHELLCHECK=1 \
379
+ VALIDATOR_SHELLCHECK_MODE=system \
380
+ FAKE_SHELLCHECK_MODE=warning \
381
+ PATH="$FAKE_SHELLCHECK_BIN:/usr/bin:/bin"
382
+
383
+ assert_exit_code \
384
+ "system mode fails on shellcheck infrastructure error" \
385
+ "$EXAMPLES_DIR/good-bash.sh" \
386
+ 2 \
387
+ VALIDATOR_REQUIRE_SHELLCHECK=1 \
388
+ VALIDATOR_SHELLCHECK_MODE=system \
389
+ FAKE_SHELLCHECK_MODE=infra \
390
+ PATH="$FAKE_SHELLCHECK_BIN:/usr/bin:/bin"
391
+
392
+ assert_output_contains \
393
+ "system mode reports shellcheck infrastructure error" \
394
+ "$EXAMPLES_DIR/good-bash.sh" \
395
+ "ShellCheck execution failed \(exit 3\)" \
396
+ VALIDATOR_REQUIRE_SHELLCHECK=1 \
397
+ VALIDATOR_SHELLCHECK_MODE=system \
398
+ FAKE_SHELLCHECK_MODE=infra \
399
+ PATH="$FAKE_SHELLCHECK_BIN:/usr/bin:/bin"
400
+
401
+ assert_exit_code \
402
+ "invalid shellcheck mode exits with error" \
403
+ "$EXAMPLES_DIR/good-bash.sh" \
404
+ 2 \
405
+ VALIDATOR_SHELLCHECK_MODE=invalid-mode
406
+
407
+ assert_output_contains \
408
+ "invalid shellcheck mode prints actionable error" \
409
+ "$EXAMPLES_DIR/good-bash.sh" \
410
+ "Invalid VALIDATOR_SHELLCHECK_MODE" \
411
+ VALIDATOR_SHELLCHECK_MODE=invalid-mode
412
+
413
+ # --- edge cases ---
414
+ echo ""
415
+ echo "[edge cases]"
416
+
417
+ # P0: strict mode must fail when ShellCheck is unavailable
418
+ assert_exit_code \
419
+ "strict mode fails when shellcheck is unavailable" \
420
+ "$EXAMPLES_DIR/good-bash.sh" \
421
+ 2 \
422
+ VALIDATOR_REQUIRE_SHELLCHECK=1 \
423
+ VALIDATOR_DISABLE_SHELLCHECK=1
424
+
425
+ assert_output_contains \
426
+ "strict mode reports shellcheck unavailable" \
427
+ "$EXAMPLES_DIR/good-bash.sh" \
428
+ "ShellCheck (unavailable|disabled by configuration)" \
429
+ VALIDATOR_REQUIRE_SHELLCHECK=1 \
430
+ VALIDATOR_DISABLE_SHELLCHECK=1
431
+
432
+ # Missing file → exit 1 from the validator's own error path
433
+ assert_exit_code \
434
+ "missing file exits non-zero" \
435
+ "/nonexistent/path/script.sh" \
436
+ 1
437
+
438
+ # ─── summary ────────────────────────────────────────────────────────────────
439
+
440
+ echo ""
441
+ echo "Results: $PASS passed, $FAIL failed"
442
+ echo ""
443
+
444
+ if [[ $FAIL -gt 0 ]]; then
445
+ exit 1
446
+ fi