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
@@ -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
 
@@ -154,7 +154,7 @@ cost_record() {
154
154
  fi
155
155
  local tmp_file
156
156
  tmp_file=$(mktemp "${COST_FILE}.tmp.XXXXXX")
157
- jq --argjson input "$input_tokens" \
157
+ if ! jq --argjson input "$input_tokens" \
158
158
  --argjson output "$output_tokens" \
159
159
  --arg model "$model" \
160
160
  --arg stage "$stage" \
@@ -172,7 +172,16 @@ cost_record() {
172
172
  ts: $ts,
173
173
  ts_epoch: $epoch
174
174
  }] | .entries = (.entries | .[-1000:])' \
175
- "$COST_FILE" > "$tmp_file" && mv "$tmp_file" "$COST_FILE" || rm -f "$tmp_file"
175
+ "$COST_FILE" > "$tmp_file" 2>/dev/null; then
176
+ error "Cost jq transformation failed — entry may be lost"
177
+ rm -f "$tmp_file"
178
+ # Continue without updating cost file
179
+ else
180
+ mv "$tmp_file" "$COST_FILE" || {
181
+ error "Failed to update cost file"
182
+ rm -f "$tmp_file"
183
+ }
184
+ fi
176
185
  ) 200>"${COST_FILE}.lock"
177
186
 
178
187
  emit_event "cost.record" \
@@ -790,6 +799,54 @@ cost_dashboard() {
790
799
  fi
791
800
  fi
792
801
 
802
+ # Context efficiency (from loop.context_efficiency events)
803
+ local events_file="${HOME}/.shipwright/events.jsonl"
804
+ if [[ -f "$events_file" ]]; then
805
+ local ctx_events
806
+ ctx_events=$(grep '"type":"loop.context_efficiency"' "$events_file" 2>/dev/null | tail -200 || true)
807
+ local ctx_count
808
+ ctx_count=$(echo "$ctx_events" | grep -c '"loop.context_efficiency"' 2>/dev/null || echo "0")
809
+ ctx_count="${ctx_count:-0}"
810
+
811
+ if [[ "$ctx_count" -gt 0 ]]; then
812
+ # Parse metrics using awk (Bash 3.2 safe — no arrays needed)
813
+ local avg_utilization avg_trim_ratio total_raw total_trimmed trim_count
814
+ eval "$(echo "$ctx_events" | awk -F'"' '
815
+ /"loop.context_efficiency"/ {
816
+ for (i=1; i<=NF; i++) {
817
+ if ($i == "budget_utilization") util = $(i+2)
818
+ if ($i == "trim_ratio") ratio = $(i+2)
819
+ if ($i == "raw_prompt_chars") raw = $(i+2)
820
+ if ($i == "trimmed_prompt_chars") trimmed = $(i+2)
821
+ }
822
+ total_util += util; total_ratio += ratio
823
+ total_raw += raw; total_trimmed += trimmed
824
+ if (ratio + 0 > 0) trim_events++
825
+ n++
826
+ }
827
+ END {
828
+ if (n > 0) {
829
+ printf "avg_utilization=%.1f\n", total_util / n
830
+ printf "avg_trim_ratio=%.1f\n", total_ratio / n
831
+ } else {
832
+ printf "avg_utilization=0\navg_trim_ratio=0\n"
833
+ }
834
+ printf "total_raw=%d\ntotal_trimmed=%d\ntrim_count=%d\n", total_raw, total_trimmed, (trim_events+0)
835
+ }
836
+ ')"
837
+
838
+ local total_discarded=$(( total_raw - total_trimmed ))
839
+
840
+ echo -e "${BOLD} CONTEXT EFFICIENCY${RESET}"
841
+ echo -e " Avg budget used ${avg_utilization}%"
842
+ echo -e " Avg trim ratio ${avg_trim_ratio}%"
843
+ echo -e " Chars generated $(printf "%'d" "$total_raw")"
844
+ echo -e " Chars discarded $(printf "%'d" "$total_discarded")"
845
+ echo -e " Trim events ${trim_count} / ${ctx_count} iterations"
846
+ echo ""
847
+ fi
848
+ fi
849
+
793
850
  echo -e "${PURPLE}${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
794
851
  echo ""
795
852
  }
@@ -8,8 +8,11 @@ trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
9
  # Allow spawning Claude CLI from within a Claude Code session (daemon, fleet, etc.)
10
10
  unset CLAUDECODE 2>/dev/null || true
11
+ # Ignore SIGHUP so daemon survives tmux attach/detach
12
+ trap '' HUP
13
+ trap '' SIGPIPE
11
14
 
12
- VERSION="3.1.0"
15
+ VERSION="3.2.0"
13
16
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
14
17
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
15
18
 
@@ -554,7 +557,7 @@ setup_dirs() {
554
557
  # ─── Adaptive Threshold Helpers ──────────────────────────────────────────────
555
558
  # When intelligence.adaptive_enabled=true, operational thresholds are learned
556
559
  # from historical data instead of using fixed defaults.
557
- # Every function falls back to the current hardcoded value when no data exists.
560
+ # Every function falls back to the config default when no data exists.
558
561
 
559
562
  ADAPTIVE_THRESHOLDS_ENABLED="${ADAPTIVE_THRESHOLDS_ENABLED:-false}"
560
563
  PRIORITY_STRATEGY="${PRIORITY_STRATEGY:-quick-wins-first}"
@@ -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 ──────────────────────────────────────────
package/scripts/sw-db.sh CHANGED
@@ -14,7 +14,7 @@ if [[ -n "${_SW_DB_LOADED:-}" ]] && [[ "${BASH_SOURCE[0]}" != "$0" ]]; then
14
14
  fi
15
15
  _SW_DB_LOADED=1
16
16
 
17
- VERSION="3.1.0"
17
+ VERSION="3.2.0"
18
18
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
19
19
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
20
20
 
@@ -812,7 +812,9 @@ add_event() {
812
812
 
813
813
  # Try SQLite first
814
814
  if db_available; then
815
- _db_exec "INSERT OR IGNORE INTO events (ts, ts_epoch, type, job_id, stage, status, duration_secs, metadata, created_at, synced) VALUES ('${ts}', ${ts_epoch}, '${event_type}', '${job_id}', '${stage}', '${status}', ${duration_secs}, '${metadata}', '${ts}', 0);" || true
815
+ if ! _db_exec "INSERT OR IGNORE INTO events (ts, ts_epoch, type, job_id, stage, status, duration_secs, metadata, created_at, synced) VALUES ('${ts}', ${ts_epoch}, '${event_type}', '${job_id}', '${stage}', '${status}', ${duration_secs}, '${metadata}', '${ts}', 0);" 2>/dev/null; then
816
+ warn "db_add_event: SQLite insert failed for event type=${event_type}" >&2
817
+ fi
816
818
  fi
817
819
 
818
820
  # Always write to JSONL for backward compat (dual-write period)
@@ -934,7 +936,9 @@ db_dequeue_next() {
934
936
  next=$(_db_query "SELECT issue_key FROM daemon_queue ORDER BY added_at ASC LIMIT 1;" || echo "")
935
937
  if [[ -n "$next" ]]; then
936
938
  escaped="${next//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
937
- _db_exec "DELETE FROM daemon_queue WHERE issue_key = '${escaped}';" 2>/dev/null || true
939
+ if ! _db_exec "DELETE FROM daemon_queue WHERE issue_key = '${escaped}';" 2>/dev/null; then
940
+ warn "db_dequeue_next: failed to delete queue entry for ${next}" >&2
941
+ fi
938
942
  echo "$next"
939
943
  fi
940
944
  }
@@ -1829,8 +1833,12 @@ main() {
1829
1833
  ensure_db_dir
1830
1834
  init_schema
1831
1835
  # Set schema version
1832
- _db_exec "INSERT OR REPLACE INTO _schema (version, created_at, applied_at) VALUES (${SCHEMA_VERSION}, '$(now_iso)', '$(now_iso)');" 2>/dev/null || true
1833
- _db_exec "INSERT OR IGNORE INTO _sync_metadata (key, value, updated_at) VALUES ('device_id', '$(uname -n)-$$-$(now_epoch)', '$(now_iso)');" 2>/dev/null || true
1836
+ if ! _db_exec "INSERT OR REPLACE INTO _schema (version, created_at, applied_at) VALUES (${SCHEMA_VERSION}, '$(now_iso)', '$(now_iso)');" 2>/dev/null; then
1837
+ warn "db init: failed to write schema version ${SCHEMA_VERSION}" >&2
1838
+ fi
1839
+ if ! _db_exec "INSERT OR IGNORE INTO _sync_metadata (key, value, updated_at) VALUES ('device_id', '$(uname -n)-$$-$(now_epoch)', '$(now_iso)');" 2>/dev/null; then
1840
+ warn "db init: failed to write device_id metadata" >&2
1841
+ fi
1834
1842
  success "Database initialized at ${DB_FILE} (WAL mode, schema v${SCHEMA_VERSION})"
1835
1843
  ;;
1836
1844
  migrate)
@@ -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
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
10
 
11
11
  # ─── Dependencies ─────────────────────────────────────────────────────────────
@@ -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
 
12
12
  # ─── 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="$(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
 
@@ -156,10 +156,60 @@ query_discoveries() {
156
156
  fi
157
157
  done < "$DISCOVERIES_FILE"
158
158
 
159
- # Optionally use Claude to rank when many candidates
159
+ # Use Claude to rank candidates by relevance when there are many
160
160
  if [[ "${INTELLIGENCE_ENABLED:-auto}" != "false" ]] && command -v claude &>/dev/null 2>&1 && [[ ${#candidates[@]} -gt 5 ]]; then
161
- # TODO: batch Claude call to rank by relevance (future enhancement)
162
- :
161
+ local ranked_json=""
162
+ local candidate_summaries=""
163
+ local idx=0
164
+ for line in "${candidates[@]+"${candidates[@]}"}"; do
165
+ local disc cat_name
166
+ disc=$(echo "$line" | jq -r '.discovery // ""' 2>/dev/null || echo "")
167
+ cat_name=$(echo "$line" | jq -r '.category // ""' 2>/dev/null || echo "")
168
+ candidate_summaries="${candidate_summaries}${idx}: [${cat_name}] ${disc}"$'\n'
169
+ idx=$((idx + 1))
170
+ done
171
+
172
+ local rank_prompt
173
+ rank_prompt="Given these discoveries and the query context '${query_context}', return a JSON array of indices sorted by relevance (most relevant first). Only return the JSON array, no explanation.
174
+
175
+ Discoveries:
176
+ ${candidate_summaries}"
177
+
178
+ local _claude_timeout
179
+ _claude_timeout=30
180
+ local _timeout_cmd=""
181
+ if command -v gtimeout >/dev/null 2>&1; then _timeout_cmd="gtimeout $_claude_timeout"
182
+ elif command -v timeout >/dev/null 2>&1; then _timeout_cmd="timeout $_claude_timeout"
183
+ fi
184
+
185
+ ranked_json=$($_timeout_cmd claude -p "$rank_prompt" 2>/dev/null || true)
186
+
187
+ # Extract array from response (handle markdown fences)
188
+ local indices_str=""
189
+ if [[ -n "$ranked_json" ]]; then
190
+ indices_str=$(echo "$ranked_json" | sed -n 's/.*\(\[[ 0-9,]*\]\).*/\1/p' | head -1)
191
+ fi
192
+
193
+ if [[ -n "$indices_str" ]] && echo "$indices_str" | jq -e 'type == "array"' >/dev/null 2>&1; then
194
+ local reordered=()
195
+ local seen=""
196
+ while IFS= read -r rank_idx; do
197
+ [[ -z "$rank_idx" ]] && continue
198
+ if [[ "$rank_idx" -ge 0 && "$rank_idx" -lt "${#candidates[@]}" ]]; then
199
+ # Avoid duplicates
200
+ case " $seen " in *" $rank_idx "*) continue ;; esac
201
+ seen="$seen $rank_idx"
202
+ reordered+=("${candidates[$rank_idx]}")
203
+ fi
204
+ done < <(echo "$indices_str" | jq -r '.[]' 2>/dev/null)
205
+ # Append any candidates not in the ranking
206
+ idx=0
207
+ for line in "${candidates[@]+"${candidates[@]}"}"; do
208
+ case " $seen " in *" $idx "*) ;; *) reordered+=("$line") ;; esac
209
+ idx=$((idx + 1))
210
+ done
211
+ candidates=("${reordered[@]+"${reordered[@]}"}")
212
+ fi
163
213
  fi
164
214
 
165
215
  # Output up to limit
@@ -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
 
@@ -4,7 +4,7 @@
4
4
  # ║ ║
5
5
  # ║ Checks prerequisites, installed files, PATH, and common issues. ║
6
6
  # ╚═══════════════════════════════════════════════════════════════════════════╝
7
- VERSION="3.1.0"
7
+ VERSION="3.2.0"
8
8
  set -euo pipefail
9
9
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
10
10
 
@@ -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 ──────────────────────────────────────────
@@ -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
 
13
13
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -481,17 +481,21 @@ cmd_status() {
481
481
  locks_dir="${DURABLE_DIR}/locks"
482
482
 
483
483
  local log_events log_size
484
- log_events=$(wc -l < "$log_file" 2>/dev/null || echo "0")
484
+ log_events=$(wc -l < "$log_file" 2>/dev/null || true)
485
+ log_events="${log_events:-0}"
485
486
  log_size=$(du -h "$log_file" 2>/dev/null | awk '{print $1}' || echo "0")
486
487
 
487
488
  local dlq_events
488
- dlq_events=$(wc -l < "$dlq_file" 2>/dev/null || echo "0")
489
+ dlq_events=$(wc -l < "$dlq_file" 2>/dev/null || true)
490
+ dlq_events="${dlq_events:-0}"
489
491
 
490
492
  local consumer_count
491
- consumer_count=$(find "$offsets_dir" -name "consumer-*.offset" 2>/dev/null | wc -l || echo "0")
493
+ consumer_count=$(find "$offsets_dir" -name "consumer-*.offset" 2>/dev/null | wc -l || true)
494
+ consumer_count="${consumer_count:-0}"
492
495
 
493
496
  local active_locks
494
- active_locks=$(find "$locks_dir" -type d -mindepth 1 2>/dev/null | wc -l || echo "0")
497
+ active_locks=$(find "$locks_dir" -type d -mindepth 1 2>/dev/null | wc -l || true)
498
+ active_locks="${active_locks:-0}"
495
499
 
496
500
  echo ""
497
501
  echo -e "${CYAN}${BOLD} Durable Workflow Status${RESET} ${DIM}v${VERSION}${RESET}"
@@ -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
  # ─── Script directory resolution ────────────────────────────────────────────
11
11
  SOURCE="${BASH_SOURCE[0]}"
@@ -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
 
13
13
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -251,7 +251,8 @@ cmd_status() {
251
251
  fi
252
252
  elif [[ -f "$EVENTS_FILE" ]]; then
253
253
  local total_events last_event_ts
254
- total_events=$(wc -l < "$EVENTS_FILE" || echo 0)
254
+ total_events=$(wc -l < "$EVENTS_FILE" || true)
255
+ total_events="${total_events:-0}"
255
256
  last_event_ts=$(tail -1 "$EVENTS_FILE" | jq -r '.ts // "never"' 2>/dev/null || echo "never")
256
257
  echo -e " ${CYAN}Event Store:${RESET} $EVENTS_FILE (file fallback)"
257
258
  echo -e " ${CYAN}Total Events:${RESET} ${BOLD}${total_events}${RESET}"
@@ -290,7 +291,8 @@ cmd_clean() {
290
291
  elif [[ -f "$EVENTS_FILE" ]]; then
291
292
  info "Cleaning events older than ${ttl_days} days..."
292
293
  local old_count tmp_file new_count removed
293
- old_count=$(grep -c "ts" "$EVENTS_FILE" 2>/dev/null || echo 0)
294
+ old_count=$(grep -c "ts" "$EVENTS_FILE" 2>/dev/null || true)
295
+ old_count="${old_count:-0}"
294
296
  tmp_file="$(mktemp)"
295
297
  while IFS= read -r line; do
296
298
  [[ -z "$line" ]] && continue
@@ -299,7 +301,8 @@ cmd_clean() {
299
301
  [[ -n "$ts" && "$ts" > "$cutoff_iso" ]] && echo "$line" >> "$tmp_file"
300
302
  done < "$EVENTS_FILE"
301
303
  mv "$tmp_file" "$EVENTS_FILE"
302
- new_count=$(wc -l < "$EVENTS_FILE" || echo 0)
304
+ new_count=$(wc -l < "$EVENTS_FILE" || true)
305
+ new_count="${new_count:-0}"
303
306
  removed=$((old_count - new_count))
304
307
  success "Removed $removed old events. Remaining: $new_count"
305
308
  else
@@ -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
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
14
14
 
@@ -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
 
package/scripts/sw-fix.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
 
12
12
  # ─── 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="$(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
 
@@ -176,7 +176,7 @@ show_workers() {
176
176
  echo -e "${BOLD}Remote Machines:${RESET}"
177
177
  echo ""
178
178
 
179
- if jq '.machines[]?' "$MACHINES_FILE" 2>/dev/null | grep -q .; then
179
+ if jq '.machines[]?' "$MACHINES_FILE" 2>/dev/null | grep -q . 2>/dev/null; then
180
180
  jq -r '.machines[]? | "\(.name) (\(.hostname)) — \(.status) — \(.active_jobs // 0) active"' "$MACHINES_FILE" 2>/dev/null | while read -r machine; do
181
181
  echo -e " ${machine}"
182
182
  done
@@ -205,8 +205,10 @@ show_insights() {
205
205
 
206
206
  # Fleet-wide success rate (last 30 days)
207
207
  local total_pipelines successful_pipelines
208
- total_pipelines=$(grep '"type":"pipeline.completed"' "$EVENTS_FILE" 2>/dev/null | tail -5000 | wc -l || echo "0")
209
- successful_pipelines=$(grep '"type":"pipeline.completed".*"status":"success"' "$EVENTS_FILE" 2>/dev/null | tail -5000 | wc -l || echo "0")
208
+ total_pipelines=$(grep '"type":"pipeline.completed"' "$EVENTS_FILE" 2>/dev/null | tail -5000 | wc -l || true)
209
+ total_pipelines="${total_pipelines:-0}"
210
+ successful_pipelines=$(grep '"type":"pipeline.completed".*"status":"success"' "$EVENTS_FILE" 2>/dev/null | tail -5000 | wc -l || true)
211
+ successful_pipelines="${successful_pipelines:-0}"
210
212
 
211
213
  local success_rate=0
212
214
  if [[ "$total_pipelines" -gt 0 ]]; then
@@ -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
 
@@ -454,7 +454,8 @@ cmd_status() {
454
454
  # Show recent webhook events
455
455
  if [[ -f "$WEBHOOK_LOG" ]]; then
456
456
  local count
457
- count=$(wc -l < "$WEBHOOK_LOG" 2>/dev/null || echo 0)
457
+ count=$(wc -l < "$WEBHOOK_LOG" 2>/dev/null || true)
458
+ count="${count:-0}"
458
459
  if [[ "$count" -gt 0 ]]; then
459
460
  echo -e "${BOLD}Recent Webhook Events (last 10):${RESET}"
460
461
  tail -10 "$WEBHOOK_LOG" | jq '{timestamp, event_type}' -c
@@ -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="${SCRIPT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}"
11
11
  REPO_DIR="${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
 
@@ -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 ──────────────────────────────────────────
@@ -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 ──────────────────────────────────────────
@@ -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
 
@@ -103,7 +103,8 @@ detect_dead_code() {
103
103
 
104
104
  # Check if function is used in other files (count lines with this function name)
105
105
  local usage_count
106
- usage_count=$(grep -r "$func" "$REPO_DIR/scripts" --include="*.sh" 2>/dev/null | wc -l) || usage_count="0"
106
+ usage_count=$(grep -r "$func" "$REPO_DIR/scripts" --include="*.sh" 2>/dev/null | wc -l || true)
107
+ usage_count="${usage_count:-0}"
107
108
  usage_count=$(printf '%s' "$usage_count" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
108
109
 
109
110
  # Function definition counts as 1 usage; if only 1, it's unused
@@ -417,7 +418,8 @@ scan_platform_refactor() {
417
418
  sizes_file=$(mktemp)
418
419
  find "$scripts_dir" -maxdepth 1 -name "*.sh" -type f 2>/dev/null | while read -r f; do
419
420
  local lines
420
- lines=$(wc -l < "$f" 2>/dev/null || echo 0)
421
+ lines=$(wc -l < "$f" 2>/dev/null || true)
422
+ lines="${lines:-0}"
421
423
  printf '{"script":"%s","lines":%s}\n' "$(basename "$f")" "$lines"
422
424
  done | jq -s 'sort_by(-.lines) | .[0:15]' 2>/dev/null > "$sizes_file"
423
425
  local script_sizes
@@ -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
 
@@ -133,10 +133,14 @@ analyze_root_cause() {
133
133
  local config="$2"
134
134
 
135
135
  local timeout_hits error_hits memory_hits dependency_hits
136
- timeout_hits=$(echo "$failure_log" | grep -ic "timeout\|deadline\|too slow" || echo "0")
137
- memory_hits=$(echo "$failure_log" | grep -ic "out of memory\|OOM\|heap" || echo "0")
138
- dependency_hits=$(echo "$failure_log" | grep -ic "dependency\|import\|require\|not found" || echo "0")
139
- error_hits=$(echo "$failure_log" | grep -c . || echo "0")
136
+ timeout_hits=$(echo "$failure_log" | grep -ic "timeout\|deadline\|too slow" || true)
137
+ timeout_hits="${timeout_hits:-0}"
138
+ memory_hits=$(echo "$failure_log" | grep -ic "out of memory\|OOM\|heap" || true)
139
+ memory_hits="${memory_hits:-0}"
140
+ dependency_hits=$(echo "$failure_log" | grep -ic "dependency\|import\|require\|not found" || true)
141
+ dependency_hits="${dependency_hits:-0}"
142
+ error_hits=$(echo "$failure_log" | grep -c . || true)
143
+ error_hits="${error_hits:-0}"
140
144
 
141
145
  if [[ "$timeout_hits" -gt 0 ]]; then
142
146
  echo "Performance degradation: Timeout detected (${timeout_hits} occurrences)"