shipwright-cli 3.1.0 → 3.2.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 (118) hide show
  1. package/README.md +21 -7
  2. package/config/defaults.json +25 -2
  3. package/config/policy.json +1 -1
  4. package/dashboard/public/index.html +6 -0
  5. package/dashboard/public/styles.css +76 -0
  6. package/dashboard/server.ts +51 -0
  7. package/dashboard/src/core/api.ts +5 -0
  8. package/dashboard/src/types/api.ts +10 -0
  9. package/dashboard/src/views/metrics.ts +69 -1
  10. package/package.json +1 -1
  11. package/scripts/lib/daemon-adaptive.sh +4 -2
  12. package/scripts/lib/daemon-patrol.sh +2 -2
  13. package/scripts/lib/daemon-state.sh +7 -0
  14. package/scripts/lib/helpers.sh +3 -1
  15. package/scripts/lib/pipeline-detection.sh +1 -1
  16. package/scripts/lib/pipeline-intelligence.sh +5 -3
  17. package/scripts/lib/pipeline-quality-checks.sh +8 -4
  18. package/scripts/lib/pipeline-stages.sh +132 -2
  19. package/scripts/sw +1 -1
  20. package/scripts/sw-activity.sh +1 -7
  21. package/scripts/sw-adaptive.sh +7 -7
  22. package/scripts/sw-adversarial.sh +1 -1
  23. package/scripts/sw-architecture-enforcer.sh +1 -1
  24. package/scripts/sw-auth.sh +1 -1
  25. package/scripts/sw-autonomous.sh +1 -1
  26. package/scripts/sw-changelog.sh +1 -1
  27. package/scripts/sw-checkpoint.sh +1 -1
  28. package/scripts/sw-ci.sh +11 -6
  29. package/scripts/sw-cleanup.sh +1 -1
  30. package/scripts/sw-code-review.sh +36 -17
  31. package/scripts/sw-connect.sh +1 -1
  32. package/scripts/sw-context.sh +1 -1
  33. package/scripts/sw-cost.sh +60 -3
  34. package/scripts/sw-daemon.sh +5 -2
  35. package/scripts/sw-dashboard.sh +1 -1
  36. package/scripts/sw-db.sh +13 -5
  37. package/scripts/sw-decide.sh +1 -1
  38. package/scripts/sw-decompose.sh +1 -1
  39. package/scripts/sw-deps.sh +1 -1
  40. package/scripts/sw-developer-simulation.sh +1 -1
  41. package/scripts/sw-discovery.sh +54 -4
  42. package/scripts/sw-doc-fleet.sh +1 -1
  43. package/scripts/sw-docs-agent.sh +1 -1
  44. package/scripts/sw-docs.sh +1 -1
  45. package/scripts/sw-doctor.sh +1 -1
  46. package/scripts/sw-dora.sh +1 -1
  47. package/scripts/sw-durable.sh +9 -5
  48. package/scripts/sw-e2e-orchestrator.sh +1 -1
  49. package/scripts/sw-eventbus.sh +7 -4
  50. package/scripts/sw-evidence.sh +1 -1
  51. package/scripts/sw-feedback.sh +1 -1
  52. package/scripts/sw-fix.sh +1 -1
  53. package/scripts/sw-fleet-discover.sh +1 -1
  54. package/scripts/sw-fleet-viz.sh +6 -4
  55. package/scripts/sw-fleet.sh +1 -1
  56. package/scripts/sw-github-app.sh +3 -2
  57. package/scripts/sw-github-checks.sh +1 -1
  58. package/scripts/sw-github-deploy.sh +1 -1
  59. package/scripts/sw-github-graphql.sh +1 -1
  60. package/scripts/sw-guild.sh +1 -1
  61. package/scripts/sw-heartbeat.sh +1 -1
  62. package/scripts/sw-hygiene.sh +5 -3
  63. package/scripts/sw-incident.sh +9 -5
  64. package/scripts/sw-init.sh +1 -1
  65. package/scripts/sw-instrument.sh +1 -1
  66. package/scripts/sw-intelligence.sh +3 -2
  67. package/scripts/sw-jira.sh +1 -1
  68. package/scripts/sw-launchd.sh +1 -1
  69. package/scripts/sw-linear.sh +1 -1
  70. package/scripts/sw-logs.sh +1 -1
  71. package/scripts/sw-loop.sh +72 -16
  72. package/scripts/sw-memory.sh +2 -2
  73. package/scripts/sw-mission-control.sh +1 -1
  74. package/scripts/sw-model-router.sh +3 -2
  75. package/scripts/sw-otel.sh +4 -2
  76. package/scripts/sw-oversight.sh +1 -1
  77. package/scripts/sw-pipeline-composer.sh +3 -1
  78. package/scripts/sw-pipeline-vitals.sh +11 -6
  79. package/scripts/sw-pipeline.sh +20 -8
  80. package/scripts/sw-pm.sh +5 -4
  81. package/scripts/sw-pr-lifecycle.sh +1 -1
  82. package/scripts/sw-predictive.sh +11 -5
  83. package/scripts/sw-prep.sh +1 -1
  84. package/scripts/sw-ps.sh +1 -1
  85. package/scripts/sw-public-dashboard.sh +3 -2
  86. package/scripts/sw-quality.sh +13 -6
  87. package/scripts/sw-reaper.sh +1 -1
  88. package/scripts/sw-recruit.sh +1 -1
  89. package/scripts/sw-regression.sh +1 -1
  90. package/scripts/sw-release-manager.sh +1 -1
  91. package/scripts/sw-release.sh +1 -1
  92. package/scripts/sw-remote.sh +1 -1
  93. package/scripts/sw-replay.sh +1 -1
  94. package/scripts/sw-retro.sh +1 -1
  95. package/scripts/sw-review-rerun.sh +1 -1
  96. package/scripts/sw-scale.sh +5 -3
  97. package/scripts/sw-security-audit.sh +1 -1
  98. package/scripts/sw-self-optimize.sh +168 -4
  99. package/scripts/sw-session.sh +1 -1
  100. package/scripts/sw-setup.sh +1 -1
  101. package/scripts/sw-standup.sh +1 -1
  102. package/scripts/sw-status.sh +1 -1
  103. package/scripts/sw-strategic.sh +11 -6
  104. package/scripts/sw-stream.sh +7 -4
  105. package/scripts/sw-swarm.sh +3 -2
  106. package/scripts/sw-team-stages.sh +1 -1
  107. package/scripts/sw-templates.sh +3 -3
  108. package/scripts/sw-testgen.sh +11 -6
  109. package/scripts/sw-tmux-pipeline.sh +1 -1
  110. package/scripts/sw-tmux.sh +35 -1
  111. package/scripts/sw-trace.sh +1 -1
  112. package/scripts/sw-tracker.sh +1 -1
  113. package/scripts/sw-triage.sh +2 -2
  114. package/scripts/sw-upgrade.sh +1 -1
  115. package/scripts/sw-ux.sh +1 -1
  116. package/scripts/sw-webhook.sh +3 -2
  117. package/scripts/sw-widgets.sh +7 -4
  118. package/scripts/sw-worktree.sh +1 -1
@@ -3,6 +3,108 @@
3
3
  [[ -n "${_PIPELINE_STAGES_LOADED:-}" ]] && return 0
4
4
  _PIPELINE_STAGES_LOADED=1
5
5
 
6
+ # ─── Context pruning helpers ────────────────────────────────────────────────
7
+
8
+ # prune_context_section — Intelligently truncate a context section to fit a char budget.
9
+ # $1: section name (for logging/markers)
10
+ # $2: content string
11
+ # $3: max_chars (default 5000)
12
+ # For JSON content (starts with { or [): extracts summary fields via jq.
13
+ # For text content: sandwich approach — keeps first + last N lines.
14
+ # Outputs the (possibly truncated) content to stdout.
15
+ prune_context_section() {
16
+ local section_name="${1:-section}"
17
+ local content="${2:-}"
18
+ local max_chars="${3:-5000}"
19
+
20
+ [[ -z "$content" ]] && return 0
21
+
22
+ local content_len=${#content}
23
+ if [[ "$content_len" -le "$max_chars" ]]; then
24
+ printf '%s' "$content"
25
+ return 0
26
+ fi
27
+
28
+ # JSON content — try jq summary extraction
29
+ local first_char="${content:0:1}"
30
+ if [[ "$first_char" == "{" || "$first_char" == "[" ]]; then
31
+ local summary=""
32
+ # Try extracting summary/results fields
33
+ summary=$(printf '%s' "$content" | jq -r '
34
+ if type == "object" then
35
+ to_entries | map(
36
+ if (.value | type) == "array" then
37
+ "\(.key): \(.value | length) items"
38
+ elif (.value | type) == "object" then
39
+ "\(.key): \(.value | keys | join(", "))"
40
+ else
41
+ "\(.key): \(.value)"
42
+ end
43
+ ) | join("\n")
44
+ elif type == "array" then
45
+ .[:5] | map(tostring) | join("\n")
46
+ else . end
47
+ ' 2>/dev/null) || true
48
+
49
+ if [[ -n "$summary" && ${#summary} -le "$max_chars" ]]; then
50
+ printf '%s' "$summary"
51
+ return 0
52
+ fi
53
+ # jq failed or still too large — fall through to text truncation
54
+ fi
55
+
56
+ # Text content — sandwich approach (first N + last N lines)
57
+ local line_count=0
58
+ line_count=$(printf '%s\n' "$content" | wc -l | xargs)
59
+
60
+ # Calculate how many lines to keep from each end
61
+ # Approximate chars-per-line to figure out line budget
62
+ local avg_chars_per_line=80
63
+ if [[ "$line_count" -gt 0 ]]; then
64
+ avg_chars_per_line=$(( content_len / line_count ))
65
+ [[ "$avg_chars_per_line" -lt 20 ]] && avg_chars_per_line=20
66
+ fi
67
+ local total_lines_budget=$(( max_chars / avg_chars_per_line ))
68
+ [[ "$total_lines_budget" -lt 4 ]] && total_lines_budget=4
69
+ local half=$(( total_lines_budget / 2 ))
70
+
71
+ local head_part=""
72
+ local tail_part=""
73
+ head_part=$(printf '%s\n' "$content" | head -"$half")
74
+ tail_part=$(printf '%s\n' "$content" | tail -"$half")
75
+
76
+ printf '%s\n[... %s truncated: %d→%d chars ...]\n%s' \
77
+ "$head_part" "$section_name" "$content_len" "$max_chars" "$tail_part"
78
+ }
79
+
80
+ # guard_prompt_size — Warn and hard-truncate if prompt exceeds budget.
81
+ # $1: stage name (for logging)
82
+ # $2: prompt content
83
+ # $3: max_chars (default 100000)
84
+ # Outputs the (possibly truncated) prompt to stdout.
85
+ PIPELINE_PROMPT_BUDGET="${PIPELINE_PROMPT_BUDGET:-100000}"
86
+
87
+ guard_prompt_size() {
88
+ local stage_name="${1:-stage}"
89
+ local prompt="${2:-}"
90
+ local max_chars="${3:-$PIPELINE_PROMPT_BUDGET}"
91
+
92
+ local prompt_len=${#prompt}
93
+ if [[ "$prompt_len" -le "$max_chars" ]]; then
94
+ printf '%s' "$prompt"
95
+ return 0
96
+ fi
97
+
98
+ warn "${stage_name} prompt too large (${prompt_len} chars, budget ${max_chars}) — truncating"
99
+ emit_event "pipeline.prompt_truncated" \
100
+ "stage=$stage_name" \
101
+ "original=$prompt_len" \
102
+ "budget=$max_chars" 2>/dev/null || true
103
+
104
+ printf '%s\n\n... [CONTEXT TRUNCATED: %s prompt exceeded %d char budget. Focus on the goal and requirements.]' \
105
+ "${prompt:0:$max_chars}" "$stage_name" "$max_chars"
106
+ }
107
+
6
108
  # ─── Safe git helpers ────────────────────────────────────────────────────────
7
109
  # BASE_BRANCH may not exist locally (e.g. --local mode with no remote).
8
110
  # These helpers return empty output instead of crashing under set -euo pipefail.
@@ -178,6 +280,7 @@ ${ISSUE_BODY}
178
280
 
179
281
  # Inject architecture context (import graph, modules, test map)
180
282
  if [[ -n "$arch_context" ]]; then
283
+ arch_context=$(prune_context_section "architecture" "$arch_context" 5000)
181
284
  plan_prompt="${plan_prompt}
182
285
  ## Architecture Context
183
286
  ${arch_context}
@@ -189,6 +292,7 @@ ${arch_context}
189
292
  if [[ -f "$_context_bundle" ]]; then
190
293
  local _cb_content
191
294
  _cb_content=$(cat "$_context_bundle" 2>/dev/null | head -100 || true)
295
+ _cb_content=$(prune_context_section "context-bundle" "$_cb_content" 8000)
192
296
  if [[ -n "$_cb_content" ]]; then
193
297
  plan_prompt="${plan_prompt}
194
298
  ## Pipeline Context
@@ -204,6 +308,7 @@ ${_cb_content}
204
308
  if [[ -n "$plan_memory" && "$plan_memory" != *'"results":[]'* && "$plan_memory" != *'"error"'* ]]; then
205
309
  local memory_summary
206
310
  memory_summary=$(echo "$plan_memory" | jq -r '.results[]? | "- \(.)"' 2>/dev/null | head -10 || true)
311
+ memory_summary=$(prune_context_section "memory" "$memory_summary" 10000)
207
312
  if [[ -n "$memory_summary" ]]; then
208
313
  plan_prompt="${plan_prompt}
209
314
  ## Historical Context (from previous pipelines)
@@ -228,6 +333,7 @@ ${plan_hint}
228
333
  if [[ -x "$SCRIPT_DIR/sw-discovery.sh" ]]; then
229
334
  local plan_discoveries
230
335
  plan_discoveries=$("$SCRIPT_DIR/sw-discovery.sh" inject "*.md,*.json" 2>/dev/null | head -20 || true)
336
+ plan_discoveries=$(prune_context_section "discoveries" "$plan_discoveries" 3000)
231
337
  if [[ -n "$plan_discoveries" ]]; then
232
338
  plan_prompt="${plan_prompt}
233
339
  ## Discoveries from Other Pipelines
@@ -248,6 +354,7 @@ ${plan_discoveries}
248
354
  "Patterns: \((.patterns // []) | join(", "))",
249
355
  "Rules: \((.rules // []) | join("; "))"
250
356
  ' "$arch_file_plan" 2>/dev/null || true)
357
+ arch_patterns=$(prune_context_section "intelligence" "$arch_patterns" 5000)
251
358
  if [[ -n "$arch_patterns" ]]; then
252
359
  plan_prompt="${plan_prompt}
253
360
  ## Architecture Patterns
@@ -284,6 +391,12 @@ Focus on: threat modeling, OWASP top 10, input validation, authentication/author
284
391
  - Test command: ${TEST_CMD:-not configured}
285
392
  - Task type: ${TASK_TYPE:-feature}
286
393
 
394
+ ## Context Efficiency
395
+ - Batch independent tool calls in parallel when possible
396
+ - Read specific file sections (offset/limit) instead of entire large files
397
+ - Use targeted grep searches — avoid scanning entire codebases into context
398
+ - Delegate multi-file analysis to subagents when available
399
+
287
400
  ## Required Output
288
401
  Create a Markdown plan with these sections:
289
402
 
@@ -306,6 +419,9 @@ How to verify the implementation works.
306
419
  Checklist of completion criteria.
307
420
  "
308
421
 
422
+ # Guard total prompt size
423
+ plan_prompt=$(guard_prompt_size "plan" "$plan_prompt")
424
+
309
425
  local plan_model
310
426
  plan_model=$(jq -r --arg id "plan" '(.stages[] | select(.id == $id) | .config.model) // .defaults.model // "opus"' "$PIPELINE_CONFIG" 2>/dev/null) || true
311
427
  [[ -n "$MODEL" ]] && plan_model="$MODEL"
@@ -615,6 +731,7 @@ stage_design() {
615
731
  if type gather_architecture_context &>/dev/null; then
616
732
  arch_struct_context=$(gather_architecture_context "${PROJECT_ROOT:-.}" 2>/dev/null || true)
617
733
  fi
734
+ arch_struct_context=$(prune_context_section "architecture" "$arch_struct_context" 5000)
618
735
 
619
736
  # Memory integration — inject context if memory system available
620
737
  local memory_context=""
@@ -625,12 +742,14 @@ stage_design() {
625
742
  if [[ -z "$memory_context" ]] && [[ -x "$SCRIPT_DIR/sw-memory.sh" ]]; then
626
743
  memory_context=$(bash "$SCRIPT_DIR/sw-memory.sh" inject "design" 2>/dev/null) || true
627
744
  fi
745
+ memory_context=$(prune_context_section "memory" "$memory_context" 10000)
628
746
 
629
747
  # Inject cross-pipeline discoveries for design stage
630
748
  local design_discoveries=""
631
749
  if [[ -x "$SCRIPT_DIR/sw-discovery.sh" ]]; then
632
750
  design_discoveries=$("$SCRIPT_DIR/sw-discovery.sh" inject "*.md,*.ts,*.tsx,*.js" 2>/dev/null | head -20 || true)
633
751
  fi
752
+ design_discoveries=$(prune_context_section "discoveries" "$design_discoveries" 3000)
634
753
 
635
754
  # Inject architecture model patterns if available
636
755
  local arch_context=""
@@ -654,6 +773,7 @@ ${arch_patterns}
654
773
  ${arch_layers}}"
655
774
  fi
656
775
  fi
776
+ arch_context=$(prune_context_section "intelligence" "$arch_context" 5000)
657
777
 
658
778
  # Inject rejected design approaches and anti-patterns from memory
659
779
  local design_antipatterns=""
@@ -661,6 +781,7 @@ ${arch_layers}}"
661
781
  local rejected_designs
662
782
  rejected_designs=$(intelligence_search_memory "rejected design approaches anti-patterns for: ${GOAL:-}" "${HOME}/.shipwright/memory" 3 2>/dev/null) || true
663
783
  if [[ -n "$rejected_designs" ]]; then
784
+ rejected_designs=$(prune_context_section "antipatterns" "$rejected_designs" 5000)
664
785
  design_antipatterns="
665
786
  ## Rejected Approaches (from past reviews)
666
787
  These design approaches were rejected in past reviews. Avoid repeating them:
@@ -726,6 +847,9 @@ Produce this EXACT format:
726
847
 
727
848
  Be concrete and specific. Reference actual file paths in the codebase. Consider edge cases and failure modes."
728
849
 
850
+ # Guard total prompt size
851
+ design_prompt=$(guard_prompt_size "design" "$design_prompt")
852
+
729
853
  local design_model
730
854
  design_model=$(jq -r --arg id "design" '(.stages[] | select(.id == $id) | .config.model) // .defaults.model // "opus"' "$PIPELINE_CONFIG" 2>/dev/null) || true
731
855
  [[ -n "$MODEL" ]] && design_model="$MODEL"
@@ -1242,7 +1366,8 @@ stage_test() {
1242
1366
  # Post failure to GitHub with more context
1243
1367
  if [[ -n "$ISSUE_NUMBER" ]]; then
1244
1368
  local log_lines
1245
- log_lines=$(wc -l < "$test_log" 2>/dev/null || echo "0")
1369
+ log_lines=$(wc -l < "$test_log" 2>/dev/null || true)
1370
+ log_lines="${log_lines:-0}"
1246
1371
  local log_excerpt
1247
1372
  if [[ "$log_lines" -lt 60 ]]; then
1248
1373
  log_excerpt="$(cat "$test_log" 2>/dev/null || true)"
@@ -1383,6 +1508,7 @@ If no issues are found, write: \"Review clean — no issues found.\"
1383
1508
  if type intelligence_search_memory >/dev/null 2>&1; then
1384
1509
  local review_memory
1385
1510
  review_memory=$(intelligence_search_memory "code review findings anti-patterns for: ${GOAL:-}" "${HOME}/.shipwright/memory" 5 2>/dev/null) || true
1511
+ review_memory=$(prune_context_section "memory" "$review_memory" 10000)
1386
1512
  if [[ -n "$review_memory" ]]; then
1387
1513
  review_prompt+="
1388
1514
  ## Known Issues from Previous Reviews
@@ -1430,6 +1556,9 @@ $(cat "$dod_file")
1430
1556
  ## Diff to Review
1431
1557
  $(cat "$diff_file")"
1432
1558
 
1559
+ # Guard total prompt size
1560
+ review_prompt=$(guard_prompt_size "review" "$review_prompt")
1561
+
1433
1562
  # Skip permissions — pipeline runs headlessly (claude -p) and has no terminal
1434
1563
  # for interactive permission prompts. Same rationale as build stage (line ~1083).
1435
1564
  local review_args=(--print --model "$review_model" --max-turns 25 --dangerously-skip-permissions)
@@ -1837,7 +1966,8 @@ stage_pr() {
1837
1966
  real_changes=$(_safe_base_diff --name-only \
1838
1967
  -- . ':!.claude/loop-state.md' ':!.claude/pipeline-state.md' \
1839
1968
  ':!.claude/pipeline-artifacts/*' ':!**/progress.md' \
1840
- ':!**/error-summary.json' | wc -l | xargs || echo "0")
1969
+ ':!**/error-summary.json' | wc -l | xargs || true)
1970
+ real_changes="${real_changes:-0}"
1841
1971
  if [[ "${real_changes:-0}" -eq 0 ]]; then
1842
1972
  error "No meaningful code changes detected — only bookkeeping files modified"
1843
1973
  error "Refusing to create PR with zero real changes"
package/scripts/sw CHANGED
@@ -5,7 +5,7 @@
5
5
  # ╚═══════════════════════════════════════════════════════════════════════════╝
6
6
  set -euo pipefail
7
7
 
8
- VERSION="3.1.0"
8
+ VERSION="3.2.0"
9
9
 
10
10
  # Resolve symlinks (required for npm global install where bin/ symlinks to node_modules/)
11
11
  SOURCE="${BASH_SOURCE[0]}"
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="3.1.0"
9
+ VERSION="3.2.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -332,7 +332,6 @@ cmd_stats() {
332
332
  local end_time=""
333
333
 
334
334
  # Read directly to avoid subshell issues
335
- echo "DEBUG: Starting read loop..." >&2
336
335
  while IFS= read -r line; do
337
336
  [ -z "$line" ] && continue
338
337
 
@@ -363,14 +362,9 @@ cmd_stats() {
363
362
  end_time="$ts"
364
363
  done < <(grep -v '^$' "$EVENTS_FILE" 2>/dev/null)
365
364
 
366
- echo "DEBUG: Read complete, total=$total_events" >&2
367
- echo "DEBUG: agents_seen length: ${#agents_seen}" >&2
368
365
  local unique_agents
369
- echo "DEBUG: About to compute unique..." >&2
370
366
  unique_agents=$(sort -u <<< "$agents_seen" | grep -v '^$' | wc -l | tr -d ' ')
371
- echo "DEBUG: unique_agents=$unique_agents" >&2
372
367
 
373
- echo "DEBUG: About to print results..." >&2
374
368
  printf "${BOLD}Total Events:${RESET} %d\n" "$total_events"
375
369
  printf "${BOLD}Commits:${RESET} %d\n" "$commits"
376
370
  printf "${BOLD}Tests:${RESET} %d\n" "$tests"
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env bash
2
2
  # ╔═══════════════════════════════════════════════════════════════════════════╗
3
3
  # ║ shipwright adaptive — data-driven pipeline tuning ║
4
- # ║ Replace 83+ hardcoded values with learned defaults from historical runs
4
+ # ║ Replace static defaults with learned values from historical runs
5
5
  # ╚═══════════════════════════════════════════════════════════════════════════╝
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="3.1.0"
9
+ VERSION="3.2.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -712,10 +712,10 @@ cmd_compare() {
712
712
  esac
713
713
  done
714
714
 
715
- info "Learned vs Hardcoded Values for ${CYAN}${repo}${RESET}"
715
+ info "Learned vs Default Values for ${CYAN}${repo}${RESET}"
716
716
  echo ""
717
717
 
718
- printf "%-25s %-15s %-15s %-15s\n" "Metric" "Hardcoded" "Learned" "Difference"
718
+ printf "%-25s %-15s %-15s %-15s\n" "Metric" "Default" "Learned" "Difference"
719
719
  printf "%s\n" "$(printf '%.0s─' {1..70})"
720
720
 
721
721
  # Timeout
@@ -841,7 +841,7 @@ ${BOLD}USAGE${RESET}
841
841
 
842
842
  ${BOLD}SUBCOMMANDS${RESET}
843
843
  ${CYAN}get${RESET} <metric> [--stage S] [--repo R] [--complexity C] [--default V]
844
- Return adaptive value for a metric (replaces hardcoded defaults)
844
+ Return adaptive value for a metric (replaces static defaults)
845
845
  Metrics: timeout, iterations, model, team_size, template, poll_interval,
846
846
  retry_limit, quality_threshold, coverage_min
847
847
 
@@ -852,7 +852,7 @@ ${BOLD}SUBCOMMANDS${RESET}
852
852
  Rebuild models from events.jsonl (run after significant pipeline activity)
853
853
 
854
854
  ${CYAN}compare${RESET} [--repo REPO]
855
- Side-by-side table: learned vs hardcoded values
855
+ Side-by-side table: learned vs default values
856
856
 
857
857
  ${CYAN}recommend${RESET} --issue N [--repo REPO]
858
858
  Full JSON recommendation for an issue (template, model, team_size, etc.)
@@ -876,7 +876,7 @@ ${BOLD}EXAMPLES${RESET}
876
876
  # Get complete recommendation for issue #42
877
877
  sw adaptive recommend --issue 42
878
878
 
879
- # Compare learned vs hardcoded
879
+ # Compare learned vs defaults
880
880
  sw adaptive compare
881
881
 
882
882
  ${BOLD}STORAGE${RESET}
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="3.1.0"
9
+ VERSION="3.2.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="3.1.0"
9
+ VERSION="3.2.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="3.1.0"
9
+ VERSION="3.2.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -7,7 +7,7 @@
7
7
  set -euo pipefail
8
8
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
9
9
 
10
- VERSION="3.1.0"
10
+ VERSION="3.2.0"
11
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
12
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
13
13
 
@@ -7,7 +7,7 @@
7
7
  set -euo pipefail
8
8
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
9
9
 
10
- VERSION="3.1.0"
10
+ VERSION="3.2.0"
11
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
12
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
13
13
 
@@ -8,7 +8,7 @@
8
8
  set -euo pipefail
9
9
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
10
10
 
11
- VERSION="3.1.0"
11
+ VERSION="3.2.0"
12
12
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
13
13
 
14
14
  # ─── Cross-platform compatibility ──────────────────────────────────────────
package/scripts/sw-ci.sh CHANGED
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="3.1.0"
9
+ VERSION="3.2.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -227,15 +227,20 @@ cmd_analyze() {
227
227
  info "Analyzing workflow efficiency"
228
228
 
229
229
  local job_count step_count matrix_enabled
230
- job_count=$(grep -c "^ [a-z_-]*:" "$workflow_file" 2>/dev/null || echo "0")
231
- step_count=$(grep -c " - name:" "$workflow_file" 2>/dev/null || echo "0")
232
- matrix_enabled=$(grep -c "matrix:" "$workflow_file" 2>/dev/null || echo "0")
230
+ job_count=$(grep -c "^ [a-z_-]*:" "$workflow_file" 2>/dev/null || true)
231
+ job_count="${job_count:-0}"
232
+ step_count=$(grep -c " - name:" "$workflow_file" 2>/dev/null || true)
233
+ step_count="${step_count:-0}"
234
+ matrix_enabled=$(grep -c "matrix:" "$workflow_file" 2>/dev/null || true)
235
+ matrix_enabled="${matrix_enabled:-0}"
233
236
 
234
237
  local has_cache
235
- has_cache=$(grep -c "actions/cache" "$workflow_file" 2>/dev/null || echo "0")
238
+ has_cache=$(grep -c "actions/cache" "$workflow_file" 2>/dev/null || true)
239
+ has_cache="${has_cache:-0}"
236
240
 
237
241
  local has_timeout
238
- has_timeout=$(grep -c "timeout-minutes:" "$workflow_file" 2>/dev/null || echo "0")
242
+ has_timeout=$(grep -c "timeout-minutes:" "$workflow_file" 2>/dev/null || true)
243
+ has_timeout="${has_timeout:-0}"
239
244
 
240
245
  echo ""
241
246
  echo -e "${BOLD}Workflow Analysis: $(basename "$workflow_file")${RESET}"
@@ -5,7 +5,7 @@
5
5
  # ║ Default: dry-run (shows what would be cleaned). ║
6
6
  # ║ Use --force to actually kill sessions and remove files. ║
7
7
  # ╚═══════════════════════════════════════════════════════════════════════════╝
8
- VERSION="3.1.0"
8
+ VERSION="3.2.0"
9
9
  set -euo pipefail
10
10
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
11
11
 
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="3.1.0"
9
+ VERSION="3.2.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -72,7 +72,8 @@ detect_code_smells() {
72
72
 
73
73
  # Check 1: Long functions (>60 lines in bash)
74
74
  local func_count
75
- func_count=$(grep -c "^[a-zA-Z_][a-zA-Z0-9_]*().*{" "$target_file" 2>/dev/null || echo "0")
75
+ func_count=$(grep -c "^[a-zA-Z_][a-zA-Z0-9_]*().*{" "$target_file" 2>/dev/null || true)
76
+ func_count="${func_count:-0}"
76
77
  if [[ "$func_count" -gt 0 ]]; then
77
78
  while IFS= read -r line; do
78
79
  if [[ "$line" =~ ^[a-zA-Z_][a-zA-Z0-9_]*\(\).*\{ ]]; then
@@ -106,7 +107,8 @@ detect_code_smells() {
106
107
 
107
108
  # Check 3: Duplicate code patterns (repeated >3 times)
108
109
  local dup_count=0
109
- dup_count=$(grep -c '^\s*\(cd\|cd\|mkdir\|rm\|echo\)' "$target_file" 2>/dev/null || echo "0")
110
+ dup_count=$(grep -c '^\s*\(cd\|cd\|mkdir\|rm\|echo\)' "$target_file" 2>/dev/null || true)
111
+ dup_count="${dup_count:-0}"
110
112
  if [[ $dup_count -gt 3 ]]; then
111
113
  issues+=("REPEATED_PATTERNS: Common operations appear $dup_count times (consider helper functions)")
112
114
  fi
@@ -139,7 +141,8 @@ check_solid_principles() {
139
141
 
140
142
  # Single Responsibility: Check if scripts do multiple unrelated things
141
143
  local sourced_count
142
- sourced_count=$(grep -c '^\s*source\|^\s*\.\s' "$target_file" 2>/dev/null || echo "0")
144
+ sourced_count=$(grep -c '^\s*source\|^\s*\.\s' "$target_file" 2>/dev/null || true)
145
+ sourced_count="${sourced_count:-0}"
143
146
  if [[ $sourced_count -gt 3 ]]; then
144
147
  violations+=("SRP_VIOLATION: Script sources $sourced_count modules (too many responsibilities)")
145
148
  fi
@@ -159,7 +162,8 @@ check_solid_principles() {
159
162
  if [[ "$line" =~ ^[a-zA-Z_][a-zA-Z0-9_]*\(\) ]]; then
160
163
  local func_name="${line%%(*}"
161
164
  local body
162
- body=$(awk "/^${func_name}\\(\\)/,/^}/" "$target_file" | grep -c '\$[0-9]' || echo "0")
165
+ body=$(awk "/^${func_name}\\(\\)/,/^}/" "$target_file" | grep -c '\$[0-9]' || true)
166
+ body="${body:-0}"
163
167
  if [[ $body -gt 5 ]]; then
164
168
  violations+=("ISP_VIOLATION: Function $func_name uses >5 parameters")
165
169
  fi
@@ -227,7 +231,9 @@ analyze_complexity() {
227
231
 
228
232
  # Cyclomatic complexity: count decision points (if, elif, case, &&, ||)
229
233
  local cc=1
230
- cc=$((cc + $(sed -n "${start_line},${end_line}p" "$target_file" | grep -cE '\s(if|elif|case|&&|\|\|)' || echo 0)))
234
+ local _cc_count
235
+ _cc_count=$(sed -n "${start_line},${end_line}p" "$target_file" | grep -cE '\s(if|elif|case|&&|\|\|)' || true)
236
+ cc=$((cc + ${_cc_count:-0}))
231
237
 
232
238
  # Lines of code
233
239
  local loc=$((end_line - start_line))
@@ -266,8 +272,12 @@ check_style_consistency() {
266
272
  local has_trap=false
267
273
  local has_set_e=false
268
274
 
269
- [[ $(grep -c 'trap.*ERR' "$target_file" 2>/dev/null || echo 0) -gt 0 ]] && has_trap=true
270
- [[ $(grep -c 'set -e' "$target_file" 2>/dev/null || echo 0) -gt 0 ]] && has_set_e=true
275
+ local _trap_count
276
+ _trap_count=$(grep -c 'trap.*ERR' "$target_file" 2>/dev/null || true)
277
+ [[ "${_trap_count:-0}" -gt 0 ]] && has_trap=true
278
+ local _set_e_count
279
+ _set_e_count=$(grep -c 'set -e' "$target_file" 2>/dev/null || true)
280
+ [[ "${_set_e_count:-0}" -gt 0 ]] && has_set_e=true
271
281
 
272
282
  if [[ "$has_set_e" == "true" ]] && [[ "$has_trap" == "false" ]]; then
273
283
  issues+=("STYLE: Missing ERR trap despite 'set -e' (inconsistent error handling)")
@@ -276,8 +286,10 @@ check_style_consistency() {
276
286
  # Check for inconsistent quote usage
277
287
  local single_quotes
278
288
  local double_quotes
279
- single_quotes=$(grep -o "'" "$target_file" 2>/dev/null | wc -l || echo 0)
280
- double_quotes=$(grep -o '"' "$target_file" 2>/dev/null | wc -l || echo 0)
289
+ single_quotes=$(grep -o "'" "$target_file" 2>/dev/null | wc -l || true)
290
+ single_quotes="${single_quotes:-0}"
291
+ double_quotes=$(grep -o '"' "$target_file" 2>/dev/null | wc -l || true)
292
+ double_quotes="${double_quotes:-0}"
281
293
  if [[ $single_quotes -gt $((double_quotes * 3)) ]] || [[ $double_quotes -gt $((single_quotes * 3)) ]]; then
282
294
  issues+=("STYLE: Inconsistent quote style (mix of single and double quotes)")
283
295
  fi
@@ -285,8 +297,10 @@ check_style_consistency() {
285
297
  # Check for inconsistent spacing/indentation
286
298
  local tab_count
287
299
  local space_count
288
- tab_count=$(grep -c $'^\t' "$target_file" 2>/dev/null || echo 0)
289
- space_count=$(grep -c '^ ' "$target_file" 2>/dev/null || echo 0)
300
+ tab_count=$(grep -c $'^\t' "$target_file" 2>/dev/null || true)
301
+ tab_count="${tab_count:-0}"
302
+ space_count=$(grep -c '^ ' "$target_file" 2>/dev/null || true)
303
+ space_count="${space_count:-0}"
290
304
  if [[ $tab_count -gt 0 ]] && [[ $space_count -gt 0 ]]; then
291
305
  issues+=("STYLE: Mixed tabs and spaces")
292
306
  fi
@@ -333,7 +347,8 @@ auto_fix() {
333
347
 
334
348
  # Fix 2: Trailing whitespace
335
349
  local trailing_ws
336
- trailing_ws=$(grep -c '[[:space:]]$' "$target_file" 2>/dev/null || echo "0")
350
+ trailing_ws=$(grep -c '[[:space:]]$' "$target_file" 2>/dev/null || true)
351
+ trailing_ws="${trailing_ws:-0}"
337
352
  if [[ $trailing_ws -gt 0 ]]; then
338
353
  sed -i '' 's/[[:space:]]*$//' "$target_file"
339
354
  info "Removed $trailing_ws lines of trailing whitespace"
@@ -349,7 +364,8 @@ auto_fix() {
349
364
 
350
365
  # Fix 4: Consistent spacing around operators (simple cases)
351
366
  local spacing_fixes=0
352
- spacing_fixes=$(grep -c '==' "$target_file" 2>/dev/null || echo "0")
367
+ spacing_fixes=$(grep -c '==' "$target_file" 2>/dev/null || true)
368
+ spacing_fixes="${spacing_fixes:-0}"
353
369
  if [[ $spacing_fixes -gt 0 ]]; then
354
370
  info "Flagged $spacing_fixes operator spacing cases (manual review recommended)"
355
371
  fi
@@ -507,9 +523,12 @@ scan_codebase() {
507
523
  local solids=0
508
524
  local arch_issues=0
509
525
 
510
- smells=$(detect_code_smells "$file" 2>/dev/null | wc -l || echo 0)
511
- solids=$(check_solid_principles "$file" 2>/dev/null | wc -l || echo 0)
512
- arch_issues=$(check_architecture_boundaries "$file" 2>/dev/null | wc -l || echo 0)
526
+ smells=$(detect_code_smells "$file" 2>/dev/null | wc -l || true)
527
+ smells="${smells:-0}"
528
+ solids=$(check_solid_principles "$file" 2>/dev/null | wc -l || true)
529
+ solids="${solids:-0}"
530
+ arch_issues=$(check_architecture_boundaries "$file" 2>/dev/null | wc -l || true)
531
+ arch_issues="${arch_issues:-0}"
513
532
 
514
533
  local file_issues=$((smells + solids + arch_issues))
515
534
  total_issues=$((total_issues + file_issues))
@@ -8,7 +8,7 @@
8
8
  set -euo pipefail
9
9
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
10
10
 
11
- VERSION="3.1.0"
11
+ VERSION="3.2.0"
12
12
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
13
 
14
14
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="3.1.0"
9
+ VERSION="3.2.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="${SHIPWRIGHT_REPO_DIR:-$(cd "$SCRIPT_DIR/.." && pwd)}"
12
12