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,471 @@
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.0.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="${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
+ # Get repo info
224
+ local owner_repo
225
+ owner_repo=$(get_owner_repo) || {
226
+ error "Could not detect GitHub repo"
227
+ return 1
228
+ }
229
+
230
+ local error_types
231
+ error_types=$(jq -r '.error_types // ""' "$error_file")
232
+
233
+ # Find likely regression commit
234
+ local regression_commit
235
+ regression_commit=$(find_regression_commit "$error_types")
236
+
237
+ # Build issue body
238
+ local issue_body
239
+ issue_body=$(cat <<EOF
240
+ ## Production Regression Detected
241
+
242
+ **Total Errors**: $error_count
243
+ **Threshold**: $ERROR_THRESHOLD
244
+ **Timestamp**: $(now_iso)
245
+
246
+ ### Error Types
247
+ \`\`\`
248
+ $(echo "$error_types" | tr ';' '\n' | sort | uniq -c | head -10)
249
+ \`\`\`
250
+
251
+ ### Likely Regression Commit
252
+ \`$regression_commit\`
253
+
254
+ \`\`\`bash
255
+ git show $regression_commit
256
+ \`\`\`
257
+
258
+ ### Suggested Fix
259
+ 1. Review the commit above for problematic changes
260
+ 2. Run tests: \`npm test\`
261
+ 3. Check error logs: \`./.claude/pipeline-artifacts/errors-collected.json\`
262
+ 4. Deploy hotfix with: \`shipwright pipeline start --issue <N> --template hotfix\`
263
+
264
+ ---
265
+ **Auto-created by**: Shipwright Production Feedback Loop
266
+ **Component**: $0
267
+ EOF
268
+ )
269
+
270
+ # Check if gh is available and NO_GITHUB is not set
271
+ if [[ "${NO_GITHUB:-}" == "true" || "${NO_GITHUB:-}" == "1" ]]; then
272
+ warn "NO_GITHUB set — skipping GitHub issue creation"
273
+ return 0
274
+ fi
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
+ # For now, just log the rollback intent
323
+ # Full implementation would call sw-github-deploy.sh and update deployment status
324
+ local rollback_entry
325
+ rollback_entry=$(jq -n \
326
+ --arg ts "$(now_iso)" \
327
+ --arg env "$environment" \
328
+ --arg reason "$reason" \
329
+ '{timestamp: $ts, environment: $env, reason: $reason, status: "initiated"}')
330
+
331
+ echo "$rollback_entry" >> "${ARTIFACTS_DIR}/rollbacks.jsonl"
332
+
333
+ success "Rollback initiated for $environment"
334
+ emit_event "feedback_rollback" "environment=$environment" "reason=$reason"
335
+ info "Note: Full rollback requires manual GitHub Deployments API call"
336
+ }
337
+
338
+ # ─── Capture incident in memory system ───────────────────────────────────────
339
+ cmd_learn() {
340
+ local root_cause="${1:-Unknown}"
341
+ local fix_applied="${2:-}"
342
+
343
+ info "Capturing incident learning..."
344
+ ensure_dirs
345
+
346
+ local incident_entry
347
+ incident_entry=$(jq -c -n \
348
+ --arg ts "$(now_iso)" \
349
+ --arg cause "$root_cause" \
350
+ --arg fix "$fix_applied" \
351
+ --arg repo "$(basename "$REPO_DIR")" \
352
+ '{
353
+ timestamp: $ts,
354
+ repository: $repo,
355
+ root_cause: $cause,
356
+ fix_applied: $fix,
357
+ resolution_time: 0,
358
+ tags: ["production", "feedback-loop"]
359
+ }')
360
+
361
+ echo "$incident_entry" >> "$INCIDENTS_FILE"
362
+ success "Incident captured in $INCIDENTS_FILE"
363
+ emit_event "feedback_incident_learned" "cause=$root_cause"
364
+ }
365
+
366
+ # ─── Report on recent incidents ──────────────────────────────────────────────
367
+ cmd_report() {
368
+ local days="${1:-7}"
369
+
370
+ if [[ ! -f "$INCIDENTS_FILE" ]]; then
371
+ warn "No incidents recorded yet"
372
+ return 0
373
+ fi
374
+
375
+ info "Incident Report (last $days days)"
376
+ echo ""
377
+
378
+ local incident_count=0
379
+
380
+ while IFS= read -r line; do
381
+ incident_count=$((incident_count + 1))
382
+
383
+ local ts
384
+ ts=$(echo "$line" | jq -r '.timestamp // "Unknown"')
385
+ local cause
386
+ cause=$(echo "$line" | jq -r '.root_cause // "Unknown"')
387
+ local fixed
388
+ fixed=$(echo "$line" | jq -r '.fix_applied // "Pending"')
389
+
390
+ echo " ${CYAN}Incident $incident_count${RESET} ${DIM}($ts)${RESET}"
391
+ echo " ${DIM}Cause: ${RESET}$cause"
392
+ echo " ${DIM}Fix: ${RESET}$fixed"
393
+ done < "$INCIDENTS_FILE"
394
+
395
+ echo ""
396
+ success "Total incidents: $incident_count"
397
+ }
398
+
399
+ # ─── Show help ───────────────────────────────────────────────────────────────
400
+ show_help() {
401
+ cat <<EOF
402
+ ${BOLD}shipwright feedback${RESET} — Production Feedback Loop
403
+
404
+ ${BOLD}USAGE${RESET}
405
+ shipwright feedback <subcommand> [options]
406
+
407
+ ${BOLD}SUBCOMMANDS${RESET}
408
+ ${CYAN}collect${RESET} [path] Collect error patterns from logs
409
+ ${CYAN}analyze${RESET} [error-file] Analyze collected errors
410
+ ${CYAN}create-issue${RESET} [error-file] Create GitHub issue for regression
411
+ ${CYAN}rollback${RESET} [env] [reason] Trigger rollback via Deployments API
412
+ ${CYAN}learn${RESET} [cause] [fix] Capture incident in memory system
413
+ ${CYAN}report${RESET} [days] Show recent incidents (default: 7 days)
414
+ ${CYAN}help${RESET} Show this help message
415
+
416
+ ${BOLD}EXAMPLES${RESET}
417
+ ${DIM}shipwright feedback collect ./logs${RESET}
418
+ ${DIM}shipwright feedback analyze${RESET}
419
+ ${DIM}shipwright feedback create-issue${RESET}
420
+ ${DIM}shipwright feedback rollback production "Hotfix v1.2.3 regression"${RESET}
421
+ ${DIM}shipwright feedback learn "Off-by-one error in pagination" "Fixed in PR #456"${RESET}
422
+ ${DIM}shipwright feedback report 30${RESET}
423
+
424
+ ${BOLD}STORAGE${RESET}
425
+ Incidents: $INCIDENTS_FILE
426
+ Errors: ${ARTIFACTS_DIR}/errors-collected.json
427
+ Rollbacks: ${ARTIFACTS_DIR}/rollbacks.jsonl
428
+
429
+ ${BOLD}VERSION${RESET}
430
+ $VERSION
431
+ EOF
432
+ }
433
+
434
+ # ─── Main entry point ────────────────────────────────────────────────────────
435
+ main() {
436
+ local cmd="${1:-help}"
437
+ shift 2>/dev/null || true
438
+
439
+ case "$cmd" in
440
+ collect)
441
+ cmd_collect "$@"
442
+ ;;
443
+ analyze)
444
+ cmd_analyze "$@"
445
+ ;;
446
+ create-issue)
447
+ cmd_create_issue "$@"
448
+ ;;
449
+ rollback)
450
+ cmd_rollback "$@"
451
+ ;;
452
+ learn)
453
+ cmd_learn "$@"
454
+ ;;
455
+ report)
456
+ cmd_report "$@"
457
+ ;;
458
+ help|-h|--help)
459
+ show_help
460
+ ;;
461
+ *)
462
+ error "Unknown subcommand: $cmd"
463
+ show_help
464
+ exit 1
465
+ ;;
466
+ esac
467
+ }
468
+
469
+ if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
470
+ main "$@"
471
+ 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.0.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────