shipwright-cli 1.10.0 → 2.0.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 (108) hide show
  1. package/README.md +114 -36
  2. package/completions/_shipwright +212 -32
  3. package/completions/shipwright.bash +97 -25
  4. package/docs/strategy/01-market-research.md +619 -0
  5. package/docs/strategy/02-mission-and-brand.md +587 -0
  6. package/docs/strategy/03-gtm-and-roadmap.md +759 -0
  7. package/docs/strategy/QUICK-START.txt +289 -0
  8. package/docs/strategy/README.md +172 -0
  9. package/package.json +4 -2
  10. package/scripts/sw +208 -1
  11. package/scripts/sw-activity.sh +500 -0
  12. package/scripts/sw-adaptive.sh +925 -0
  13. package/scripts/sw-adversarial.sh +1 -1
  14. package/scripts/sw-architecture-enforcer.sh +1 -1
  15. package/scripts/sw-auth.sh +613 -0
  16. package/scripts/sw-autonomous.sh +664 -0
  17. package/scripts/sw-changelog.sh +704 -0
  18. package/scripts/sw-checkpoint.sh +1 -1
  19. package/scripts/sw-ci.sh +602 -0
  20. package/scripts/sw-cleanup.sh +1 -1
  21. package/scripts/sw-code-review.sh +637 -0
  22. package/scripts/sw-connect.sh +1 -1
  23. package/scripts/sw-context.sh +605 -0
  24. package/scripts/sw-cost.sh +1 -1
  25. package/scripts/sw-daemon.sh +432 -130
  26. package/scripts/sw-dashboard.sh +1 -1
  27. package/scripts/sw-db.sh +540 -0
  28. package/scripts/sw-decompose.sh +539 -0
  29. package/scripts/sw-deps.sh +551 -0
  30. package/scripts/sw-developer-simulation.sh +1 -1
  31. package/scripts/sw-discovery.sh +412 -0
  32. package/scripts/sw-docs-agent.sh +539 -0
  33. package/scripts/sw-docs.sh +1 -1
  34. package/scripts/sw-doctor.sh +59 -1
  35. package/scripts/sw-dora.sh +615 -0
  36. package/scripts/sw-durable.sh +710 -0
  37. package/scripts/sw-e2e-orchestrator.sh +535 -0
  38. package/scripts/sw-eventbus.sh +393 -0
  39. package/scripts/sw-feedback.sh +471 -0
  40. package/scripts/sw-fix.sh +1 -1
  41. package/scripts/sw-fleet-discover.sh +567 -0
  42. package/scripts/sw-fleet-viz.sh +404 -0
  43. package/scripts/sw-fleet.sh +8 -1
  44. package/scripts/sw-github-app.sh +596 -0
  45. package/scripts/sw-github-checks.sh +1 -1
  46. package/scripts/sw-github-deploy.sh +1 -1
  47. package/scripts/sw-github-graphql.sh +1 -1
  48. package/scripts/sw-guild.sh +569 -0
  49. package/scripts/sw-heartbeat.sh +1 -1
  50. package/scripts/sw-hygiene.sh +559 -0
  51. package/scripts/sw-incident.sh +617 -0
  52. package/scripts/sw-init.sh +88 -1
  53. package/scripts/sw-instrument.sh +699 -0
  54. package/scripts/sw-intelligence.sh +1 -1
  55. package/scripts/sw-jira.sh +1 -1
  56. package/scripts/sw-launchd.sh +363 -28
  57. package/scripts/sw-linear.sh +1 -1
  58. package/scripts/sw-logs.sh +1 -1
  59. package/scripts/sw-loop.sh +64 -3
  60. package/scripts/sw-memory.sh +1 -1
  61. package/scripts/sw-mission-control.sh +487 -0
  62. package/scripts/sw-model-router.sh +545 -0
  63. package/scripts/sw-otel.sh +596 -0
  64. package/scripts/sw-oversight.sh +689 -0
  65. package/scripts/sw-pipeline-composer.sh +1 -1
  66. package/scripts/sw-pipeline-vitals.sh +1 -1
  67. package/scripts/sw-pipeline.sh +687 -24
  68. package/scripts/sw-pm.sh +693 -0
  69. package/scripts/sw-pr-lifecycle.sh +522 -0
  70. package/scripts/sw-predictive.sh +1 -1
  71. package/scripts/sw-prep.sh +1 -1
  72. package/scripts/sw-ps.sh +1 -1
  73. package/scripts/sw-public-dashboard.sh +798 -0
  74. package/scripts/sw-quality.sh +595 -0
  75. package/scripts/sw-reaper.sh +1 -1
  76. package/scripts/sw-recruit.sh +573 -0
  77. package/scripts/sw-regression.sh +642 -0
  78. package/scripts/sw-release-manager.sh +736 -0
  79. package/scripts/sw-release.sh +706 -0
  80. package/scripts/sw-remote.sh +1 -1
  81. package/scripts/sw-replay.sh +520 -0
  82. package/scripts/sw-retro.sh +691 -0
  83. package/scripts/sw-scale.sh +444 -0
  84. package/scripts/sw-security-audit.sh +505 -0
  85. package/scripts/sw-self-optimize.sh +1 -1
  86. package/scripts/sw-session.sh +1 -1
  87. package/scripts/sw-setup.sh +1 -1
  88. package/scripts/sw-standup.sh +712 -0
  89. package/scripts/sw-status.sh +1 -1
  90. package/scripts/sw-strategic.sh +658 -0
  91. package/scripts/sw-stream.sh +450 -0
  92. package/scripts/sw-swarm.sh +583 -0
  93. package/scripts/sw-team-stages.sh +511 -0
  94. package/scripts/sw-templates.sh +1 -1
  95. package/scripts/sw-testgen.sh +515 -0
  96. package/scripts/sw-tmux-pipeline.sh +554 -0
  97. package/scripts/sw-tmux.sh +1 -1
  98. package/scripts/sw-trace.sh +485 -0
  99. package/scripts/sw-tracker-github.sh +188 -0
  100. package/scripts/sw-tracker-jira.sh +172 -0
  101. package/scripts/sw-tracker-linear.sh +251 -0
  102. package/scripts/sw-tracker.sh +117 -2
  103. package/scripts/sw-triage.sh +603 -0
  104. package/scripts/sw-upgrade.sh +1 -1
  105. package/scripts/sw-ux.sh +677 -0
  106. package/scripts/sw-webhook.sh +627 -0
  107. package/scripts/sw-widgets.sh +530 -0
  108. package/scripts/sw-worktree.sh +1 -1
@@ -0,0 +1,515 @@
1
+ #!/usr/bin/env bash
2
+ # ╔═══════════════════════════════════════════════════════════════════════════╗
3
+ # ║ shipwright testgen — Autonomous test generation and coverage maintenance ║
4
+ # ║ Analyze coverage · Generate tests · Maintain thresholds · Score quality ║
5
+ # ╚═══════════════════════════════════════════════════════════════════════════╝
6
+ set -euo pipefail
7
+ trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
+
9
+ VERSION="1.13.0"
10
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
+
12
+ # ─── Handle subcommands ───────────────────────────────────────────────────────
13
+ if [[ "${1:-}" == "test" ]]; then
14
+ shift
15
+ exec "$SCRIPT_DIR/sw-testgen-test.sh" "$@"
16
+ fi
17
+
18
+ # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
19
+ CYAN='\033[38;2;0;212;255m' # #00d4ff — primary accent
20
+ PURPLE='\033[38;2;124;58;237m' # #7c3aed — secondary
21
+ BLUE='\033[38;2;0;102;255m' # #0066ff — tertiary
22
+ GREEN='\033[38;2;74;222;128m' # success
23
+ YELLOW='\033[38;2;250;204;21m' # warning
24
+ RED='\033[38;2;248;113;113m' # error
25
+ DIM='\033[2m'
26
+ BOLD='\033[1m'
27
+ RESET='\033[0m'
28
+
29
+ # ─── Cross-platform compatibility ──────────────────────────────────────────
30
+ # shellcheck source=lib/compat.sh
31
+ [[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
32
+
33
+ # ─── Output Helpers ─────────────────────────────────────────────────────────
34
+ info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
35
+ success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
36
+ warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
37
+ error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
38
+
39
+ emit_event() {
40
+ local type="$1"
41
+ shift
42
+ local json_data="{\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$type\""
43
+ for pair in "$@"; do
44
+ json_data="$json_data,\"$pair\""
45
+ done
46
+ json_data="$json_data}"
47
+ echo "$json_data" >> "${EVENTS_FILE:-.shipwright-events.jsonl}"
48
+ }
49
+
50
+ now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
51
+
52
+ # ─── Configuration ───────────────────────────────────────────────────────────
53
+ PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
54
+ SCRIPTS_DIR="$PROJECT_ROOT/scripts"
55
+ TEST_SUITES_DIR="$PROJECT_ROOT/scripts"
56
+ COVERAGE_THRESHOLD=70
57
+ TESTGEN_DIR="${TESTGEN_DIR:-.claude/testgen}"
58
+ COVERAGE_DB="$TESTGEN_DIR/coverage.json"
59
+ QUALITY_DB="$TESTGEN_DIR/quality.json"
60
+ REGRESSION_DB="$TESTGEN_DIR/regressions.json"
61
+
62
+ # ─── Help ───────────────────────────────────────────────────────────────────
63
+ show_help() {
64
+ echo -e "${CYAN}${BOLD}shipwright testgen${RESET} ${DIM}v${VERSION}${RESET} — Test generation and coverage maintenance"
65
+ echo ""
66
+ echo -e "${BOLD}USAGE${RESET}"
67
+ echo -e " ${CYAN}shipwright testgen${RESET} <command> [options]"
68
+ echo ""
69
+ echo -e "${BOLD}COMMANDS${RESET}"
70
+ echo -e " ${CYAN}coverage${RESET} Show test coverage analysis"
71
+ echo -e " ${CYAN}generate${RESET} Generate tests for uncovered functions"
72
+ echo -e " ${CYAN}gaps${RESET} Show specific untested code paths"
73
+ echo -e " ${CYAN}quality${RESET} Score existing test quality"
74
+ echo -e " ${CYAN}maintain${RESET} Check if tests need updating after code changes"
75
+ echo -e " ${CYAN}threshold${RESET} Set/check coverage threshold"
76
+ echo -e " ${CYAN}regression${RESET} Compare test results across runs"
77
+ echo -e " ${CYAN}help${RESET} Show this help message"
78
+ echo ""
79
+ echo -e "${BOLD}OPTIONS${RESET}"
80
+ echo -e " ${CYAN}--target${RESET} <script> Target script for analysis"
81
+ echo -e " ${CYAN}--threshold${RESET} <num> Set minimum coverage % (default: 70)"
82
+ echo -e " ${CYAN}--json${RESET} Output JSON format"
83
+ echo -e " ${CYAN}--verbose${RESET} Detailed output"
84
+ echo ""
85
+ echo -e "${BOLD}EXAMPLES${RESET}"
86
+ echo -e " ${DIM}shipwright testgen coverage${RESET} # Overall coverage"
87
+ echo -e " ${DIM}shipwright testgen coverage --target sw-daemon.sh${RESET} # Target script"
88
+ echo -e " ${DIM}shipwright testgen generate --threshold 75${RESET} # Generate with threshold"
89
+ echo -e " ${DIM}shipwright testgen quality sw-pipeline-test.sh${RESET} # Score test quality"
90
+ echo ""
91
+ echo -e "${DIM}Docs: https://sethdford.github.io/shipwright${RESET}"
92
+ }
93
+
94
+ # ═══════════════════════════════════════════════════════════════════════════════
95
+ # COVERAGE ANALYSIS
96
+ # ═══════════════════════════════════════════════════════════════════════════════
97
+
98
+ analyze_coverage() {
99
+ local target_script="${1:-.}"
100
+ local output_format="${2:-text}"
101
+
102
+ mkdir -p "$TESTGEN_DIR"
103
+
104
+ # Extract all function definitions from target
105
+ local total_functions=0
106
+ local tested_functions=0
107
+ local function_names=""
108
+
109
+ if [[ -f "$target_script" ]]; then
110
+ # Parse function definitions
111
+ function_names=$(grep -E '^[a-zA-Z_][a-zA-Z0-9_]*\(\)' "$target_script" | sed 's/().*//' || echo "")
112
+ total_functions=$(echo "$function_names" | grep -c . || echo "0")
113
+ fi
114
+
115
+ # Find existing tests that call these functions
116
+ if [[ -n "$function_names" ]]; then
117
+ local test_file
118
+ while IFS= read -r func; do
119
+ [[ -z "$func" ]] && continue
120
+ for test_file in "$TEST_SUITES_DIR"/*-test.sh; do
121
+ [[ -f "$test_file" ]] || continue
122
+ if grep -q "$func" "$test_file" 2>/dev/null; then
123
+ tested_functions=$((tested_functions + 1))
124
+ break
125
+ fi
126
+ done
127
+ done << EOF
128
+ $function_names
129
+ EOF
130
+ fi
131
+
132
+ local coverage_pct=0
133
+ if [[ $total_functions -gt 0 ]]; then
134
+ coverage_pct=$((tested_functions * 100 / total_functions))
135
+ fi
136
+
137
+ if [[ "$output_format" == "json" ]]; then
138
+ jq -n \
139
+ --arg target "$target_script" \
140
+ --argjson total "$total_functions" \
141
+ --argjson tested "$tested_functions" \
142
+ --argjson pct "$coverage_pct" \
143
+ '{target: $target, total_functions: $total, tested_functions: $tested, coverage_percent: $pct}'
144
+ else
145
+ info "Coverage Analysis"
146
+ echo ""
147
+ echo -e " ${CYAN}Target:${RESET} $target_script"
148
+ echo -e " ${CYAN}Functions:${RESET} $total_functions total"
149
+ echo -e " ${CYAN}Tested:${RESET} $tested_functions"
150
+ echo -e " ${CYAN}Coverage:${RESET} ${coverage_pct}%"
151
+ echo ""
152
+
153
+ if [[ $coverage_pct -lt $COVERAGE_THRESHOLD ]]; then
154
+ warn "Coverage below threshold ($COVERAGE_THRESHOLD%)"
155
+ else
156
+ success "Coverage meets threshold"
157
+ fi
158
+ fi
159
+ }
160
+
161
+ # ═══════════════════════════════════════════════════════════════════════════════
162
+ # TEST GENERATION
163
+ # ═══════════════════════════════════════════════════════════════════════════════
164
+
165
+ generate_tests() {
166
+ local target_script="${1:-.}"
167
+ local threshold="${2:-$COVERAGE_THRESHOLD}"
168
+
169
+ mkdir -p "$TESTGEN_DIR"
170
+
171
+ info "Generating tests for $target_script"
172
+
173
+ # Extract untested functions
174
+ local all_functions=""
175
+ all_functions=$(grep -E '^[a-zA-Z_][a-zA-Z0-9_]*\(\)' "$target_script" | sed 's/().*//' || echo "")
176
+
177
+ local untested_functions=""
178
+ if [[ -n "$all_functions" ]]; then
179
+ while IFS= read -r func_name; do
180
+ [[ -z "$func_name" ]] && continue
181
+ local found=false
182
+ for test_file in "$TEST_SUITES_DIR"/*-test.sh; do
183
+ [[ -f "$test_file" ]] || continue
184
+ if grep -q "$func_name" "$test_file" 2>/dev/null; then
185
+ found=true
186
+ break
187
+ fi
188
+ done
189
+ [[ "$found" == "false" ]] && untested_functions="${untested_functions}${func_name}"$'\n'
190
+ done << EOF
191
+ $all_functions
192
+ EOF
193
+ fi
194
+
195
+ local untested_count=0
196
+ [[ -n "$untested_functions" ]] && untested_count=$(echo "$untested_functions" | grep -c . || echo "0")
197
+
198
+ if [[ $untested_count -eq 0 ]]; then
199
+ success "All functions have tests"
200
+ return 0
201
+ fi
202
+
203
+ echo ""
204
+ info "Untested functions: $untested_count"
205
+ echo "$untested_functions" | while IFS= read -r func; do
206
+ [[ -z "$func" ]] && continue
207
+ echo -e " ${YELLOW}•${RESET} $func"
208
+ done
209
+ echo ""
210
+
211
+ # Generate test template
212
+ local test_template_file="$TESTGEN_DIR/generated-tests.sh"
213
+ {
214
+ echo "#!/usr/bin/env bash"
215
+ echo "# Generated tests for $target_script"
216
+ echo "set -euo pipefail"
217
+ echo "trap 'echo \"ERROR: \$BASH_SOURCE:\$LINENO exited with status \$?\" >&2' ERR"
218
+ echo ""
219
+ echo "SCRIPT_DIR=\"\$(cd \"\$(dirname \"\${BASH_SOURCE[0]}\")\" && pwd)\""
220
+ echo ""
221
+ echo "# Test counters"
222
+ echo "PASS=0"
223
+ echo "FAIL=0"
224
+ echo ""
225
+
226
+ echo "$untested_functions" | while IFS= read -r func; do
227
+ [[ -z "$func" ]] && continue
228
+ echo "test_${func}() {"
229
+ echo " # TODO: Implement test for $func"
230
+ echo " # - Test happy path"
231
+ echo " # - Test error cases"
232
+ echo " # - Test edge cases"
233
+ echo " ((PASS++))"
234
+ echo "}"
235
+ echo ""
236
+ done
237
+
238
+ echo "# Run all tests"
239
+ echo "$untested_functions" | while IFS= read -r func; do
240
+ [[ -z "$func" ]] && continue
241
+ echo "test_${func}"
242
+ done
243
+ echo ""
244
+ echo "echo \"Results: \$PASS passed, \$FAIL failed\""
245
+ } > "$test_template_file"
246
+
247
+ chmod +x "$test_template_file"
248
+ success "Generated test template: $test_template_file"
249
+ info "Review and implement test logic, then move to test suite"
250
+ }
251
+
252
+ # ═══════════════════════════════════════════════════════════════════════════════
253
+ # GAP DETECTION
254
+ # ═══════════════════════════════════════════════════════════════════════════════
255
+
256
+ show_gaps() {
257
+ local target_script="${1:-.}"
258
+
259
+ info "Finding test gaps in $target_script"
260
+
261
+ # Extract functions and their line numbers
262
+ local gap_found=0
263
+ while IFS=: read -r line_num func_name; do
264
+ [[ -z "$func_name" ]] && continue
265
+ # Check if tested
266
+ local tested=false
267
+ for test_file in "$TEST_SUITES_DIR"/*-test.sh; do
268
+ [[ -f "$test_file" ]] || continue
269
+ if grep -q "$func_name" "$test_file" 2>/dev/null; then
270
+ tested=true
271
+ break
272
+ fi
273
+ done
274
+
275
+ if [[ "$tested" == "false" ]]; then
276
+ gap_found=1
277
+ echo -e " ${YELLOW}Gap at line $line_num:${RESET} $func_name()"
278
+ fi
279
+ done < <(grep -En '^[a-zA-Z_][a-zA-Z0-9_]*\(\)' "$target_script" | sed 's/:.*\([a-zA-Z_][a-zA-Z0-9_]*\)().*/:\1/' || true)
280
+
281
+ [[ $gap_found -eq 0 ]] && success "No test gaps found"
282
+ }
283
+
284
+ # ═══════════════════════════════════════════════════════════════════════════════
285
+ # TEST QUALITY SCORING
286
+ # ═══════════════════════════════════════════════════════════════════════════════
287
+
288
+ score_quality() {
289
+ local test_file="$1"
290
+
291
+ [[ -f "$test_file" ]] || {
292
+ error "Test file not found: $test_file"
293
+ return 1
294
+ }
295
+
296
+ info "Scoring test quality: $(basename "$test_file")"
297
+
298
+ # Count assertions
299
+ local assertion_count=0
300
+ assertion_count=$(grep -c -E '(assert_|test_|expect_)' "$test_file" || echo "0")
301
+
302
+ # Count edge case patterns
303
+ local edge_case_count=0
304
+ edge_case_count=$(grep -c -E '(empty|null|nil|missing|invalid|error|fail)' "$test_file" || echo "0")
305
+
306
+ # Count error path tests
307
+ local error_path_count=0
308
+ error_path_count=$(grep -c -E '(exit|return 1|error|ERROR)' "$test_file" || echo "0")
309
+
310
+ # Calculate score (0-100)
311
+ local quality_score=0
312
+ quality_score=$((assertion_count * 10 + edge_case_count * 5 + error_path_count * 5))
313
+ [[ $quality_score -gt 100 ]] && quality_score=100
314
+
315
+ echo ""
316
+ echo -e " ${CYAN}Assertions:${RESET} $assertion_count"
317
+ echo -e " ${CYAN}Edge cases:${RESET} $edge_case_count"
318
+ echo -e " ${CYAN}Error paths:${RESET} $error_path_count"
319
+ echo -e " ${CYAN}Quality score:${RESET} $quality_score/100"
320
+ echo ""
321
+
322
+ if [[ $quality_score -ge 80 ]]; then
323
+ success "Excellent test quality"
324
+ elif [[ $quality_score -ge 60 ]]; then
325
+ warn "Good test quality, could improve"
326
+ else
327
+ warn "Low test quality, needs improvement"
328
+ fi
329
+ }
330
+
331
+ # ═══════════════════════════════════════════════════════════════════════════════
332
+ # TEST MAINTENANCE
333
+ # ═══════════════════════════════════════════════════════════════════════════════
334
+
335
+ maintain_tests() {
336
+ local source_file="${1:-.}"
337
+
338
+ info "Checking test maintenance for $source_file"
339
+
340
+ # Check for modified functions
341
+ local modified_count=0
342
+ local test_files=("$TEST_SUITES_DIR"/*-test.sh)
343
+
344
+ for test_file in "${test_files[@]}"; do
345
+ [[ -f "$test_file" ]] || continue
346
+
347
+ # Extract functions tested by this test
348
+ while IFS= read -r func_name; do
349
+ [[ -z "$func_name" ]] && continue
350
+
351
+ # Check if function signature changed
352
+ if git diff --no-index "$source_file" "$source_file" 2>/dev/null | grep -q "$func_name"; then
353
+ modified_count=$((modified_count + 1))
354
+ warn "Function $func_name may need test updates"
355
+ fi
356
+ done < <(grep -E "$func_name\(" "$test_file" | sed 's/.*\([a-zA-Z_][a-zA-Z0-9_]*\)(.*/\1/' | sort -u || true)
357
+ done
358
+
359
+ if [[ $modified_count -eq 0 ]]; then
360
+ success "All tests up to date with source"
361
+ fi
362
+ }
363
+
364
+ # ═══════════════════════════════════════════════════════════════════════════════
365
+ # THRESHOLD MANAGEMENT
366
+ # ═══════════════════════════════════════════════════════════════════════════════
367
+
368
+ manage_threshold() {
369
+ local action="${1:-show}"
370
+ local value="${2:-}"
371
+
372
+ case "$action" in
373
+ set)
374
+ [[ -z "$value" ]] && {
375
+ error "Threshold value required"
376
+ return 1
377
+ }
378
+ COVERAGE_THRESHOLD="$value"
379
+ success "Coverage threshold set to $value%"
380
+ ;;
381
+ show)
382
+ info "Current coverage threshold: $COVERAGE_THRESHOLD%"
383
+ ;;
384
+ check)
385
+ # Compare current coverage against threshold
386
+ local coverage_pct
387
+ coverage_pct=$(analyze_coverage "${value:-.}" json 2>/dev/null | jq -r '.coverage_percent // 0')
388
+
389
+ if [[ $coverage_pct -lt $COVERAGE_THRESHOLD ]]; then
390
+ warn "Coverage ${coverage_pct}% below threshold ($COVERAGE_THRESHOLD%)"
391
+ return 1
392
+ else
393
+ success "Coverage ${coverage_pct}% meets threshold ($COVERAGE_THRESHOLD%)"
394
+ fi
395
+ ;;
396
+ *)
397
+ error "Unknown threshold action: $action"
398
+ return 1
399
+ ;;
400
+ esac
401
+ }
402
+
403
+ # ═══════════════════════════════════════════════════════════════════════════════
404
+ # REGRESSION DETECTION
405
+ # ═══════════════════════════════════════════════════════════════════════════════
406
+
407
+ detect_regressions() {
408
+ mkdir -p "$TESTGEN_DIR"
409
+
410
+ info "Scanning for test regressions"
411
+
412
+ # Run all test suites and capture results
413
+ local current_results=()
414
+ local test_file
415
+ for test_file in "$TEST_SUITES_DIR"/*-test.sh; do
416
+ [[ -f "$test_file" ]] || continue
417
+ [[ "$(basename "$test_file")" == "sw-testgen-test.sh" ]] && continue
418
+
419
+ local result
420
+ if bash "$test_file" &>/dev/null; then
421
+ result="PASS"
422
+ else
423
+ result="FAIL"
424
+ fi
425
+ current_results+=("$(basename "$test_file"):$result")
426
+ done
427
+
428
+ # Compare with previous results
429
+ if [[ -f "$REGRESSION_DB" ]]; then
430
+ info "Comparing with previous run..."
431
+
432
+ local regression_found=0
433
+ for entry in "${current_results[@]}"; do
434
+ local test_name="${entry%:*}"
435
+ local current_status="${entry##*:}"
436
+
437
+ local previous_status
438
+ previous_status=$(jq -r ".\"$test_name\" // \"UNKNOWN\"" "$REGRESSION_DB" 2>/dev/null || echo "UNKNOWN")
439
+
440
+ if [[ "$previous_status" == "PASS" && "$current_status" == "FAIL" ]]; then
441
+ warn "Regression detected: $test_name (was PASS, now FAIL)"
442
+ regression_found=$((regression_found + 1))
443
+ fi
444
+ done
445
+
446
+ if [[ $regression_found -eq 0 ]]; then
447
+ success "No regressions detected"
448
+ fi
449
+ fi
450
+
451
+ # Save current results for future comparison
452
+ {
453
+ jq -n '.' > "$REGRESSION_DB"
454
+ for entry in "${current_results[@]}"; do
455
+ local test_name="${entry%:*}"
456
+ local status="${entry##*:}"
457
+ jq --arg name "$test_name" --arg status "$status" '.[$name] = $status' "$REGRESSION_DB" > "$REGRESSION_DB.tmp"
458
+ mv "$REGRESSION_DB.tmp" "$REGRESSION_DB"
459
+ done
460
+ }
461
+
462
+ success "Regression detection saved to $REGRESSION_DB"
463
+ }
464
+
465
+ # ═══════════════════════════════════════════════════════════════════════════════
466
+ # MAIN COMMAND ROUTER
467
+ # ═══════════════════════════════════════════════════════════════════════════════
468
+
469
+ main() {
470
+ local cmd="${1:-help}"
471
+
472
+ case "$cmd" in
473
+ coverage)
474
+ shift || true
475
+ analyze_coverage "$@"
476
+ ;;
477
+ generate)
478
+ shift || true
479
+ generate_tests "$@"
480
+ ;;
481
+ gaps)
482
+ shift || true
483
+ show_gaps "$@"
484
+ ;;
485
+ quality)
486
+ shift || true
487
+ score_quality "$@"
488
+ ;;
489
+ maintain)
490
+ shift || true
491
+ maintain_tests "$@"
492
+ ;;
493
+ threshold)
494
+ shift || true
495
+ manage_threshold "$@"
496
+ ;;
497
+ regression)
498
+ shift || true
499
+ detect_regressions "$@"
500
+ ;;
501
+ help|--help|-h)
502
+ show_help
503
+ ;;
504
+ *)
505
+ error "Unknown command: $cmd"
506
+ echo ""
507
+ show_help
508
+ exit 1
509
+ ;;
510
+ esac
511
+ }
512
+
513
+ if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
514
+ main "$@"
515
+ fi