shipwright-cli 1.10.0 → 2.1.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 (121) hide show
  1. package/README.md +221 -55
  2. package/completions/_shipwright +264 -32
  3. package/completions/shipwright.bash +118 -26
  4. package/completions/shipwright.fish +80 -2
  5. package/dashboard/server.ts +208 -0
  6. package/docs/strategy/01-market-research.md +619 -0
  7. package/docs/strategy/02-mission-and-brand.md +587 -0
  8. package/docs/strategy/03-gtm-and-roadmap.md +759 -0
  9. package/docs/strategy/QUICK-START.txt +289 -0
  10. package/docs/strategy/README.md +172 -0
  11. package/docs/tmux-research/TMUX-ARCHITECTURE.md +567 -0
  12. package/docs/tmux-research/TMUX-AUDIT.md +925 -0
  13. package/docs/tmux-research/TMUX-BEST-PRACTICES-2025-2026.md +829 -0
  14. package/docs/tmux-research/TMUX-QUICK-REFERENCE.md +543 -0
  15. package/docs/tmux-research/TMUX-RESEARCH-INDEX.md +438 -0
  16. package/package.json +4 -2
  17. package/scripts/lib/helpers.sh +7 -0
  18. package/scripts/sw +323 -2
  19. package/scripts/sw-activity.sh +500 -0
  20. package/scripts/sw-adaptive.sh +925 -0
  21. package/scripts/sw-adversarial.sh +1 -1
  22. package/scripts/sw-architecture-enforcer.sh +1 -1
  23. package/scripts/sw-auth.sh +613 -0
  24. package/scripts/sw-autonomous.sh +754 -0
  25. package/scripts/sw-changelog.sh +704 -0
  26. package/scripts/sw-checkpoint.sh +1 -1
  27. package/scripts/sw-ci.sh +602 -0
  28. package/scripts/sw-cleanup.sh +1 -1
  29. package/scripts/sw-code-review.sh +698 -0
  30. package/scripts/sw-connect.sh +1 -1
  31. package/scripts/sw-context.sh +605 -0
  32. package/scripts/sw-cost.sh +44 -3
  33. package/scripts/sw-daemon.sh +568 -138
  34. package/scripts/sw-dashboard.sh +1 -1
  35. package/scripts/sw-db.sh +1380 -0
  36. package/scripts/sw-decompose.sh +539 -0
  37. package/scripts/sw-deps.sh +551 -0
  38. package/scripts/sw-developer-simulation.sh +1 -1
  39. package/scripts/sw-discovery.sh +412 -0
  40. package/scripts/sw-docs-agent.sh +539 -0
  41. package/scripts/sw-docs.sh +1 -1
  42. package/scripts/sw-doctor.sh +107 -1
  43. package/scripts/sw-dora.sh +615 -0
  44. package/scripts/sw-durable.sh +710 -0
  45. package/scripts/sw-e2e-orchestrator.sh +535 -0
  46. package/scripts/sw-eventbus.sh +393 -0
  47. package/scripts/sw-feedback.sh +479 -0
  48. package/scripts/sw-fix.sh +1 -1
  49. package/scripts/sw-fleet-discover.sh +567 -0
  50. package/scripts/sw-fleet-viz.sh +404 -0
  51. package/scripts/sw-fleet.sh +8 -1
  52. package/scripts/sw-github-app.sh +596 -0
  53. package/scripts/sw-github-checks.sh +4 -4
  54. package/scripts/sw-github-deploy.sh +1 -1
  55. package/scripts/sw-github-graphql.sh +1 -1
  56. package/scripts/sw-guild.sh +569 -0
  57. package/scripts/sw-heartbeat.sh +1 -1
  58. package/scripts/sw-hygiene.sh +559 -0
  59. package/scripts/sw-incident.sh +656 -0
  60. package/scripts/sw-init.sh +237 -24
  61. package/scripts/sw-instrument.sh +699 -0
  62. package/scripts/sw-intelligence.sh +1 -1
  63. package/scripts/sw-jira.sh +1 -1
  64. package/scripts/sw-launchd.sh +363 -28
  65. package/scripts/sw-linear.sh +1 -1
  66. package/scripts/sw-logs.sh +1 -1
  67. package/scripts/sw-loop.sh +267 -21
  68. package/scripts/sw-memory.sh +18 -1
  69. package/scripts/sw-mission-control.sh +487 -0
  70. package/scripts/sw-model-router.sh +545 -0
  71. package/scripts/sw-otel.sh +596 -0
  72. package/scripts/sw-oversight.sh +764 -0
  73. package/scripts/sw-pipeline-composer.sh +1 -1
  74. package/scripts/sw-pipeline-vitals.sh +1 -1
  75. package/scripts/sw-pipeline.sh +947 -35
  76. package/scripts/sw-pm.sh +758 -0
  77. package/scripts/sw-pr-lifecycle.sh +522 -0
  78. package/scripts/sw-predictive.sh +8 -1
  79. package/scripts/sw-prep.sh +1 -1
  80. package/scripts/sw-ps.sh +1 -1
  81. package/scripts/sw-public-dashboard.sh +798 -0
  82. package/scripts/sw-quality.sh +595 -0
  83. package/scripts/sw-reaper.sh +1 -1
  84. package/scripts/sw-recruit.sh +2248 -0
  85. package/scripts/sw-regression.sh +642 -0
  86. package/scripts/sw-release-manager.sh +736 -0
  87. package/scripts/sw-release.sh +706 -0
  88. package/scripts/sw-remote.sh +1 -1
  89. package/scripts/sw-replay.sh +520 -0
  90. package/scripts/sw-retro.sh +691 -0
  91. package/scripts/sw-scale.sh +444 -0
  92. package/scripts/sw-security-audit.sh +505 -0
  93. package/scripts/sw-self-optimize.sh +1 -1
  94. package/scripts/sw-session.sh +1 -1
  95. package/scripts/sw-setup.sh +263 -127
  96. package/scripts/sw-standup.sh +712 -0
  97. package/scripts/sw-status.sh +44 -2
  98. package/scripts/sw-strategic.sh +806 -0
  99. package/scripts/sw-stream.sh +450 -0
  100. package/scripts/sw-swarm.sh +620 -0
  101. package/scripts/sw-team-stages.sh +511 -0
  102. package/scripts/sw-templates.sh +4 -4
  103. package/scripts/sw-testgen.sh +566 -0
  104. package/scripts/sw-tmux-pipeline.sh +554 -0
  105. package/scripts/sw-tmux-role-color.sh +58 -0
  106. package/scripts/sw-tmux-status.sh +128 -0
  107. package/scripts/sw-tmux.sh +1 -1
  108. package/scripts/sw-trace.sh +485 -0
  109. package/scripts/sw-tracker-github.sh +188 -0
  110. package/scripts/sw-tracker-jira.sh +172 -0
  111. package/scripts/sw-tracker-linear.sh +251 -0
  112. package/scripts/sw-tracker.sh +117 -2
  113. package/scripts/sw-triage.sh +627 -0
  114. package/scripts/sw-upgrade.sh +1 -1
  115. package/scripts/sw-ux.sh +677 -0
  116. package/scripts/sw-webhook.sh +627 -0
  117. package/scripts/sw-widgets.sh +530 -0
  118. package/scripts/sw-worktree.sh +1 -1
  119. package/templates/pipelines/autonomous.json +2 -2
  120. package/tmux/shipwright-overlay.conf +35 -17
  121. package/tmux/tmux.conf +23 -21
@@ -0,0 +1,479 @@
1
+ #!/usr/bin/env bash
2
+ # ╔═══════════════════════════════════════════════════════════════════════════╗
3
+ # ║ shipwright feedback — Production Feedback Loop ║
4
+ # ║ Error collection · Auto-issue creation · Rollback trigger · Learning ║
5
+ # ╚═══════════════════════════════════════════════════════════════════════════╝
6
+ set -euo pipefail
7
+ trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
+
9
+ VERSION="2.1.0"
10
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
+ REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
+
13
+ # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
14
+ CYAN='\033[38;2;0;212;255m' # #00d4ff — primary accent
15
+ PURPLE='\033[38;2;124;58;237m' # #7c3aed — secondary
16
+ BLUE='\033[38;2;0;102;255m' # #0066ff — tertiary
17
+ GREEN='\033[38;2;74;222;128m' # success
18
+ YELLOW='\033[38;2;250;204;21m' # warning
19
+ RED='\033[38;2;248;113;113m' # error
20
+ DIM='\033[2m'
21
+ BOLD='\033[1m'
22
+ RESET='\033[0m'
23
+
24
+ # ─── Cross-platform compatibility ──────────────────────────────────────────
25
+ # shellcheck source=lib/compat.sh
26
+ [[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
27
+
28
+ # ─── Output Helpers ─────────────────────────────────────────────────────────
29
+ info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
30
+ success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
31
+ warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
32
+ error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
33
+
34
+ now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
35
+ now_epoch() { date +%s; }
36
+
37
+ # ─── Structured Event Log ────────────────────────────────────────────────────
38
+ EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
39
+
40
+ emit_event() {
41
+ local event_type="$1"
42
+ shift
43
+ local json_fields=""
44
+ for kv in "$@"; do
45
+ local key="${kv%%=*}"
46
+ local val="${kv#*=}"
47
+ if [[ "$val" =~ ^-?[0-9]+\.?[0-9]*$ ]]; then
48
+ json_fields="${json_fields},\"${key}\":${val}"
49
+ else
50
+ val="${val//\"/\\\"}"
51
+ json_fields="${json_fields},\"${key}\":\"${val}\""
52
+ fi
53
+ done
54
+ mkdir -p "${HOME}/.shipwright"
55
+ echo "{\"ts\":\"$(now_iso)\",\"ts_epoch\":$(now_epoch),\"type\":\"${event_type}\"${json_fields}}" >> "$EVENTS_FILE"
56
+ }
57
+
58
+ # ─── Storage Paths ──────────────────────────────────────────────────────────
59
+ INCIDENTS_FILE="${HOME}/.shipwright/incidents.jsonl"
60
+ ERROR_THRESHOLD=5 # Create issue if error count >= threshold
61
+ ERROR_LOG_DIR="${REPO_DIR}/.claude/pipeline-artifacts"
62
+ ARTIFACTS_DIR="${ARTIFACTS_DIR:-${REPO_DIR}/.claude/pipeline-artifacts}"
63
+
64
+ # ─── Initialize directories ────────────────────────────────────────────────
65
+ ensure_dirs() {
66
+ mkdir -p "${HOME}/.shipwright"
67
+ mkdir -p "$ARTIFACTS_DIR"
68
+ }
69
+
70
+ # ─── Detect owner/repo from git remote ─────────────────────────────────────
71
+ get_owner_repo() {
72
+ local remote_url
73
+ remote_url=$(git -C "$REPO_DIR" remote get-url origin 2>/dev/null || true)
74
+ if [[ -z "$remote_url" ]]; then
75
+ return 1
76
+ fi
77
+ echo "$remote_url" | sed -E 's#^(https?://github\.com/|git@github\.com:)##; s#\.git$##'
78
+ }
79
+
80
+ # ─── Parse log files for error patterns ──────────────────────────────────────
81
+ parse_error_patterns() {
82
+ local log_file="$1"
83
+ local error_count=0
84
+ local error_types=""
85
+ local stack_traces=""
86
+
87
+ if [[ ! -f "$log_file" ]]; then
88
+ return 1
89
+ fi
90
+
91
+ # Count errors and extract stack traces
92
+ while IFS= read -r line; do
93
+ if [[ "$line" =~ (Error|Exception|panic|fatal).*: ]]; then
94
+ error_count=$((error_count + 1))
95
+ # Extract error message
96
+ local err_msg=$(echo "$line" | sed -E 's/^.*\[.*\] //; s/:.*//')
97
+ error_types="${error_types}${err_msg};"
98
+ fi
99
+ done < "$log_file"
100
+
101
+ # Output CSV: count|types|first_stack_trace
102
+ local first_stack=$(head -50 "$log_file" | tail -20)
103
+ echo "$error_count|$error_types|$first_stack"
104
+ }
105
+
106
+ # ─── Find commit that likely introduced regression ───────────────────────────
107
+ find_regression_commit() {
108
+ local error_pattern="$1"
109
+ local max_commits="${2:-20}"
110
+
111
+ # Search recent commits for changes that might have introduced the error
112
+ # Pattern: look for commits touching error-related code
113
+ local commit_hash
114
+ commit_hash=$(cd "$REPO_DIR" && git log --all -n "$max_commits" --pretty=format:"%H %s" | \
115
+ while read -r hash subject; do
116
+ # Simple heuristic: commits that touched multiple files or had large diffs
117
+ local files_changed
118
+ files_changed=$(cd "$REPO_DIR" && git show "$hash" --stat | tail -1 | grep -oE '[0-9]+ files? changed' | head -1)
119
+ if [[ -n "$files_changed" ]]; then
120
+ echo "$hash"
121
+ break
122
+ fi
123
+ done)
124
+
125
+ echo "${commit_hash:0:7}"
126
+ }
127
+
128
+ # ─── Collect errors from monitor stage output ────────────────────────────────
129
+ cmd_collect() {
130
+ local log_path="${1:-.}"
131
+
132
+ info "Collecting error patterns from: $log_path"
133
+ ensure_dirs
134
+
135
+ local error_file="${ARTIFACTS_DIR}/errors-collected.json"
136
+ local total_errors=0
137
+ local error_summary=""
138
+
139
+ if [[ -f "$log_path" ]]; then
140
+ # Single file
141
+ local result
142
+ result=$(parse_error_patterns "$log_path")
143
+ IFS='|' read -r count types traces <<< "$result"
144
+ total_errors=$((total_errors + count))
145
+ error_summary="${types}"
146
+ elif [[ -d "$log_path" ]]; then
147
+ # Directory of logs
148
+ while IFS= read -r file; do
149
+ local result
150
+ result=$(parse_error_patterns "$file") || continue
151
+ IFS='|' read -r count types traces <<< "$result"
152
+ total_errors=$((total_errors + count))
153
+ error_summary="${error_summary}${types};"
154
+ done < <(find "$log_path" -name "*.log" -type f 2>/dev/null)
155
+ fi
156
+
157
+ # Save to artifacts
158
+ local error_json
159
+ error_json=$(jq -n \
160
+ --arg ts "$(now_iso)" \
161
+ --arg summary "$error_summary" \
162
+ --arg count "$total_errors" \
163
+ '{timestamp: $ts, total_errors: ($count | tonumber), error_types: $summary}')
164
+
165
+ echo "$error_json" > "$error_file"
166
+ success "Collected $total_errors errors"
167
+ info "Saved to: $error_file"
168
+
169
+ emit_event "feedback_collect" "errors=$total_errors" "file=$error_file"
170
+ }
171
+
172
+ # ─── Analyze collected errors ────────────────────────────────────────────────
173
+ cmd_analyze() {
174
+ local error_file="${1:-${ARTIFACTS_DIR}/errors-collected.json}"
175
+
176
+ if [[ ! -f "$error_file" ]]; then
177
+ error "Error file not found: $error_file"
178
+ return 1
179
+ fi
180
+
181
+ info "Analyzing error patterns..."
182
+
183
+ local error_count
184
+ error_count=$(jq -r '.total_errors // 0' "$error_file")
185
+ local error_types
186
+ error_types=$(jq -r '.error_types // ""' "$error_file")
187
+
188
+ echo ""
189
+ info "Error Analysis Report"
190
+ echo " ${DIM}Total Errors: ${RESET}${error_count}"
191
+ echo " ${DIM}Error Types: ${RESET}$(echo "$error_types" | tr ';' '\n' | sort | uniq -c | head -5)"
192
+
193
+ if [[ "$error_count" -ge "$ERROR_THRESHOLD" ]]; then
194
+ warn "Error threshold exceeded! ($error_count >= $ERROR_THRESHOLD)"
195
+ echo " Recommended: Create hotfix issue"
196
+ return 0
197
+ fi
198
+
199
+ success "Error count within threshold"
200
+ }
201
+
202
+ # ─── Create GitHub issue for regression ──────────────────────────────────────
203
+ cmd_create_issue() {
204
+ local error_file="${1:-${ARTIFACTS_DIR}/errors-collected.json}"
205
+
206
+ if [[ ! -f "$error_file" ]]; then
207
+ error "Error file not found: $error_file"
208
+ return 1
209
+ fi
210
+
211
+ ensure_dirs
212
+
213
+ local error_count
214
+ error_count=$(jq -r '.total_errors // 0' "$error_file")
215
+
216
+ if [[ "$error_count" -lt "$ERROR_THRESHOLD" ]]; then
217
+ warn "Error count ($error_count) below threshold ($ERROR_THRESHOLD) — skipping issue creation"
218
+ return 0
219
+ fi
220
+
221
+ info "Creating GitHub issue for regression..."
222
+
223
+ # Check if NO_GITHUB is set before attempting GitHub operations
224
+ if [[ "${NO_GITHUB:-}" == "true" || "${NO_GITHUB:-}" == "1" ]]; then
225
+ warn "NO_GITHUB set — skipping GitHub issue creation"
226
+ return 0
227
+ fi
228
+
229
+ # Get repo info
230
+ local owner_repo
231
+ owner_repo=$(get_owner_repo) || {
232
+ error "Could not detect GitHub repo"
233
+ return 1
234
+ }
235
+
236
+ local error_types
237
+ error_types=$(jq -r '.error_types // ""' "$error_file")
238
+
239
+ # Find likely regression commit
240
+ local regression_commit
241
+ regression_commit=$(find_regression_commit "$error_types")
242
+
243
+ # Build issue body
244
+ local issue_body
245
+ issue_body=$(cat <<EOF
246
+ ## Production Regression Detected
247
+
248
+ **Total Errors**: $error_count
249
+ **Threshold**: $ERROR_THRESHOLD
250
+ **Timestamp**: $(now_iso)
251
+
252
+ ### Error Types
253
+ \`\`\`
254
+ $(echo "$error_types" | tr ';' '\n' | sort | uniq -c | head -10)
255
+ \`\`\`
256
+
257
+ ### Likely Regression Commit
258
+ \`$regression_commit\`
259
+
260
+ \`\`\`bash
261
+ git show $regression_commit
262
+ \`\`\`
263
+
264
+ ### Suggested Fix
265
+ 1. Review the commit above for problematic changes
266
+ 2. Run tests: \`npm test\`
267
+ 3. Check error logs: \`./.claude/pipeline-artifacts/errors-collected.json\`
268
+ 4. Deploy hotfix with: \`shipwright pipeline start --issue <N> --template hotfix\`
269
+
270
+ ---
271
+ **Auto-created by**: Shipwright Production Feedback Loop
272
+ **Component**: $0
273
+ EOF
274
+ )
275
+
276
+ if ! command -v gh &>/dev/null; then
277
+ error "gh CLI not found — cannot create issue"
278
+ return 1
279
+ fi
280
+
281
+ # Create issue via gh
282
+ local issue_url
283
+ issue_url=$(gh issue create \
284
+ --repo "$owner_repo" \
285
+ --title "Production Regression: $error_count errors detected" \
286
+ --body "$issue_body" \
287
+ --label "shipwright" \
288
+ --label "hotfix" \
289
+ 2>&1 | tail -1)
290
+
291
+ if [[ -n "$issue_url" ]]; then
292
+ success "Created issue: $issue_url"
293
+ emit_event "feedback_issue_created" "url=$issue_url" "errors=$error_count"
294
+ echo "$issue_url" > "${ARTIFACTS_DIR}/last-issue.txt"
295
+ else
296
+ warn "Could not create issue (check gh auth)"
297
+ fi
298
+ }
299
+
300
+ # ─── Trigger rollback via GitHub Deployments API ─────────────────────────────
301
+ cmd_rollback() {
302
+ local environment="${1:-production}"
303
+ local reason="${2:-Rollback due to production errors}"
304
+
305
+ info "Triggering rollback for environment: $environment"
306
+
307
+ # Check for sw-github-deploy.sh
308
+ if [[ ! -f "$SCRIPT_DIR/sw-github-deploy.sh" ]]; then
309
+ error "sw-github-deploy.sh not found"
310
+ return 1
311
+ fi
312
+
313
+ ensure_dirs
314
+
315
+ # Get current deployment
316
+ local owner_repo
317
+ owner_repo=$(get_owner_repo) || {
318
+ error "Could not detect GitHub repo"
319
+ return 1
320
+ }
321
+
322
+ # Trigger real rollback via sw-github-deploy.sh (GitHub Deployments API)
323
+ local rollback_status="initiated"
324
+ local rollback_rc=0
325
+ bash "$SCRIPT_DIR/sw-github-deploy.sh" rollback "$environment" 2>&1 | tee -a "${ARTIFACTS_DIR}/rollback-output.log"
326
+ rollback_rc=${PIPESTATUS[0]:-$?}
327
+ if [[ "$rollback_rc" -eq 0 ]]; then
328
+ rollback_status="executed"
329
+ success "Rollback executed for $environment via GitHub Deployments API"
330
+ else
331
+ warn "GitHub Deployments rollback failed or unavailable (exit $rollback_rc, see rollback-output.log)"
332
+ fi
333
+
334
+ local rollback_entry
335
+ rollback_entry=$(jq -n \
336
+ --arg ts "$(now_iso)" \
337
+ --arg env "$environment" \
338
+ --arg reason "$reason" \
339
+ --arg status "$rollback_status" \
340
+ '{timestamp: $ts, environment: $env, reason: $reason, status: $status}')
341
+
342
+ echo "$rollback_entry" >> "${ARTIFACTS_DIR}/rollbacks.jsonl"
343
+ emit_event "feedback_rollback" "environment=$environment" "reason=$reason" "status=$rollback_status"
344
+ }
345
+
346
+ # ─── Capture incident in memory system ───────────────────────────────────────
347
+ cmd_learn() {
348
+ local root_cause="${1:-Unknown}"
349
+ local fix_applied="${2:-}"
350
+
351
+ info "Capturing incident learning..."
352
+ ensure_dirs
353
+
354
+ local incident_entry
355
+ incident_entry=$(jq -c -n \
356
+ --arg ts "$(now_iso)" \
357
+ --arg cause "$root_cause" \
358
+ --arg fix "$fix_applied" \
359
+ --arg repo "$(basename "$REPO_DIR")" \
360
+ '{
361
+ timestamp: $ts,
362
+ repository: $repo,
363
+ root_cause: $cause,
364
+ fix_applied: $fix,
365
+ resolution_time: 0,
366
+ tags: ["production", "feedback-loop"]
367
+ }')
368
+
369
+ echo "$incident_entry" >> "$INCIDENTS_FILE"
370
+ success "Incident captured in $INCIDENTS_FILE"
371
+ emit_event "feedback_incident_learned" "cause=$root_cause"
372
+ }
373
+
374
+ # ─── Report on recent incidents ──────────────────────────────────────────────
375
+ cmd_report() {
376
+ local days="${1:-7}"
377
+
378
+ if [[ ! -f "$INCIDENTS_FILE" ]]; then
379
+ warn "No incidents recorded yet"
380
+ return 0
381
+ fi
382
+
383
+ info "Incident Report (last $days days)"
384
+ echo ""
385
+
386
+ local incident_count=0
387
+
388
+ while IFS= read -r line; do
389
+ incident_count=$((incident_count + 1))
390
+
391
+ local ts
392
+ ts=$(echo "$line" | jq -r '.timestamp // "Unknown"')
393
+ local cause
394
+ cause=$(echo "$line" | jq -r '.root_cause // "Unknown"')
395
+ local fixed
396
+ fixed=$(echo "$line" | jq -r '.fix_applied // "Pending"')
397
+
398
+ echo " ${CYAN}Incident $incident_count${RESET} ${DIM}($ts)${RESET}"
399
+ echo " ${DIM}Cause: ${RESET}$cause"
400
+ echo " ${DIM}Fix: ${RESET}$fixed"
401
+ done < "$INCIDENTS_FILE"
402
+
403
+ echo ""
404
+ success "Total incidents: $incident_count"
405
+ }
406
+
407
+ # ─── Show help ───────────────────────────────────────────────────────────────
408
+ show_help() {
409
+ cat <<EOF
410
+ ${BOLD}shipwright feedback${RESET} — Production Feedback Loop
411
+
412
+ ${BOLD}USAGE${RESET}
413
+ shipwright feedback <subcommand> [options]
414
+
415
+ ${BOLD}SUBCOMMANDS${RESET}
416
+ ${CYAN}collect${RESET} [path] Collect error patterns from logs
417
+ ${CYAN}analyze${RESET} [error-file] Analyze collected errors
418
+ ${CYAN}create-issue${RESET} [error-file] Create GitHub issue for regression
419
+ ${CYAN}rollback${RESET} [env] [reason] Trigger rollback via Deployments API
420
+ ${CYAN}learn${RESET} [cause] [fix] Capture incident in memory system
421
+ ${CYAN}report${RESET} [days] Show recent incidents (default: 7 days)
422
+ ${CYAN}help${RESET} Show this help message
423
+
424
+ ${BOLD}EXAMPLES${RESET}
425
+ ${DIM}shipwright feedback collect ./logs${RESET}
426
+ ${DIM}shipwright feedback analyze${RESET}
427
+ ${DIM}shipwright feedback create-issue${RESET}
428
+ ${DIM}shipwright feedback rollback production "Hotfix v1.2.3 regression"${RESET}
429
+ ${DIM}shipwright feedback learn "Off-by-one error in pagination" "Fixed in PR #456"${RESET}
430
+ ${DIM}shipwright feedback report 30${RESET}
431
+
432
+ ${BOLD}STORAGE${RESET}
433
+ Incidents: $INCIDENTS_FILE
434
+ Errors: ${ARTIFACTS_DIR}/errors-collected.json
435
+ Rollbacks: ${ARTIFACTS_DIR}/rollbacks.jsonl
436
+
437
+ ${BOLD}VERSION${RESET}
438
+ $VERSION
439
+ EOF
440
+ }
441
+
442
+ # ─── Main entry point ────────────────────────────────────────────────────────
443
+ main() {
444
+ local cmd="${1:-help}"
445
+ shift 2>/dev/null || true
446
+
447
+ case "$cmd" in
448
+ collect)
449
+ cmd_collect "$@"
450
+ ;;
451
+ analyze)
452
+ cmd_analyze "$@"
453
+ ;;
454
+ create-issue)
455
+ cmd_create_issue "$@"
456
+ ;;
457
+ rollback)
458
+ cmd_rollback "$@"
459
+ ;;
460
+ learn)
461
+ cmd_learn "$@"
462
+ ;;
463
+ report)
464
+ cmd_report "$@"
465
+ ;;
466
+ help|-h|--help)
467
+ show_help
468
+ ;;
469
+ *)
470
+ error "Unknown subcommand: $cmd"
471
+ show_help
472
+ exit 1
473
+ ;;
474
+ esac
475
+ }
476
+
477
+ if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
478
+ main "$@"
479
+ fi
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="1.10.0"
9
+ VERSION="2.1.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────