shipwright-cli 2.1.1 → 2.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 (129) hide show
  1. package/.claude/agents/devops-engineer.md +14 -12
  2. package/.claude/agents/doc-fleet-agent.md +99 -0
  3. package/.claude/agents/test-specialist.md +5 -3
  4. package/README.md +48 -27
  5. package/claude-code/CLAUDE.md.shipwright +2 -2
  6. package/config/policy.json +73 -0
  7. package/config/policy.schema.json +75 -0
  8. package/docs/AGI-PLATFORM-PLAN.md +122 -0
  9. package/docs/AGI-WHATS-NEXT.md +69 -0
  10. package/docs/KNOWN-ISSUES.md +1 -23
  11. package/docs/PLATFORM-TODO-BACKLOG.md +41 -0
  12. package/docs/PLATFORM-TODO-TRIAGE.md +56 -0
  13. package/docs/README.md +83 -0
  14. package/docs/TIPS.md +39 -2
  15. package/docs/config-policy.md +40 -0
  16. package/docs/definition-of-done.example.md +2 -0
  17. package/docs/patterns/README.md +5 -0
  18. package/docs/strategy/02-mission-and-brand.md +3 -3
  19. package/docs/strategy/README.md +4 -3
  20. package/docs/tmux-research/TMUX-AUDIT.md +2 -0
  21. package/docs/tmux-research/TMUX-RESEARCH-INDEX.md +17 -0
  22. package/package.json +3 -2
  23. package/scripts/lib/daemon-health.sh +32 -0
  24. package/scripts/lib/pipeline-quality.sh +23 -0
  25. package/scripts/lib/policy.sh +32 -0
  26. package/scripts/sw +5 -1
  27. package/scripts/sw-activity.sh +35 -46
  28. package/scripts/sw-adaptive.sh +30 -39
  29. package/scripts/sw-adversarial.sh +30 -36
  30. package/scripts/sw-architecture-enforcer.sh +30 -33
  31. package/scripts/sw-auth.sh +30 -42
  32. package/scripts/sw-autonomous.sh +60 -40
  33. package/scripts/sw-changelog.sh +29 -30
  34. package/scripts/sw-checkpoint.sh +30 -18
  35. package/scripts/sw-ci.sh +30 -42
  36. package/scripts/sw-cleanup.sh +32 -15
  37. package/scripts/sw-code-review.sh +26 -32
  38. package/scripts/sw-connect.sh +30 -19
  39. package/scripts/sw-context.sh +30 -19
  40. package/scripts/sw-cost.sh +30 -40
  41. package/scripts/sw-daemon.sh +150 -39
  42. package/scripts/sw-dashboard.sh +31 -40
  43. package/scripts/sw-db.sh +30 -20
  44. package/scripts/sw-decompose.sh +30 -38
  45. package/scripts/sw-deps.sh +30 -41
  46. package/scripts/sw-developer-simulation.sh +30 -36
  47. package/scripts/sw-discovery.sh +36 -19
  48. package/scripts/sw-doc-fleet.sh +822 -0
  49. package/scripts/sw-docs-agent.sh +30 -36
  50. package/scripts/sw-docs.sh +29 -31
  51. package/scripts/sw-doctor.sh +52 -20
  52. package/scripts/sw-dora.sh +29 -34
  53. package/scripts/sw-durable.sh +30 -20
  54. package/scripts/sw-e2e-orchestrator.sh +36 -21
  55. package/scripts/sw-eventbus.sh +30 -17
  56. package/scripts/sw-feedback.sh +30 -41
  57. package/scripts/sw-fix.sh +30 -40
  58. package/scripts/sw-fleet-discover.sh +30 -41
  59. package/scripts/sw-fleet-viz.sh +30 -20
  60. package/scripts/sw-fleet.sh +30 -40
  61. package/scripts/sw-github-app.sh +30 -41
  62. package/scripts/sw-github-checks.sh +30 -41
  63. package/scripts/sw-github-deploy.sh +30 -41
  64. package/scripts/sw-github-graphql.sh +30 -38
  65. package/scripts/sw-guild.sh +30 -37
  66. package/scripts/sw-heartbeat.sh +30 -19
  67. package/scripts/sw-hygiene.sh +134 -42
  68. package/scripts/sw-incident.sh +30 -39
  69. package/scripts/sw-init.sh +31 -14
  70. package/scripts/sw-instrument.sh +30 -41
  71. package/scripts/sw-intelligence.sh +39 -44
  72. package/scripts/sw-jira.sh +31 -41
  73. package/scripts/sw-launchd.sh +30 -17
  74. package/scripts/sw-linear.sh +31 -41
  75. package/scripts/sw-logs.sh +32 -17
  76. package/scripts/sw-loop.sh +55 -26
  77. package/scripts/sw-memory.sh +90 -99
  78. package/scripts/sw-mission-control.sh +31 -40
  79. package/scripts/sw-model-router.sh +30 -20
  80. package/scripts/sw-otel.sh +30 -20
  81. package/scripts/sw-oversight.sh +30 -36
  82. package/scripts/sw-patrol-meta.sh +31 -0
  83. package/scripts/sw-pipeline-composer.sh +30 -39
  84. package/scripts/sw-pipeline-vitals.sh +30 -44
  85. package/scripts/sw-pipeline.sh +315 -6388
  86. package/scripts/sw-pm.sh +31 -41
  87. package/scripts/sw-pr-lifecycle.sh +30 -42
  88. package/scripts/sw-predictive.sh +32 -34
  89. package/scripts/sw-prep.sh +47 -32
  90. package/scripts/sw-ps.sh +32 -17
  91. package/scripts/sw-public-dashboard.sh +30 -40
  92. package/scripts/sw-quality.sh +42 -40
  93. package/scripts/sw-reaper.sh +32 -15
  94. package/scripts/sw-recruit.sh +428 -48
  95. package/scripts/sw-regression.sh +30 -38
  96. package/scripts/sw-release-manager.sh +30 -38
  97. package/scripts/sw-release.sh +29 -31
  98. package/scripts/sw-remote.sh +31 -40
  99. package/scripts/sw-replay.sh +30 -18
  100. package/scripts/sw-retro.sh +33 -42
  101. package/scripts/sw-scale.sh +41 -24
  102. package/scripts/sw-security-audit.sh +30 -20
  103. package/scripts/sw-self-optimize.sh +33 -37
  104. package/scripts/sw-session.sh +31 -15
  105. package/scripts/sw-setup.sh +30 -16
  106. package/scripts/sw-standup.sh +30 -20
  107. package/scripts/sw-status.sh +33 -13
  108. package/scripts/sw-strategic.sh +55 -43
  109. package/scripts/sw-stream.sh +33 -37
  110. package/scripts/sw-swarm.sh +30 -21
  111. package/scripts/sw-team-stages.sh +30 -38
  112. package/scripts/sw-templates.sh +31 -16
  113. package/scripts/sw-testgen.sh +30 -31
  114. package/scripts/sw-tmux-pipeline.sh +29 -31
  115. package/scripts/sw-tmux-role-color.sh +31 -0
  116. package/scripts/sw-tmux-status.sh +31 -0
  117. package/scripts/sw-tmux.sh +31 -15
  118. package/scripts/sw-trace.sh +30 -19
  119. package/scripts/sw-tracker-github.sh +31 -0
  120. package/scripts/sw-tracker-jira.sh +31 -0
  121. package/scripts/sw-tracker-linear.sh +31 -0
  122. package/scripts/sw-tracker.sh +30 -40
  123. package/scripts/sw-triage.sh +68 -61
  124. package/scripts/sw-upgrade.sh +30 -16
  125. package/scripts/sw-ux.sh +30 -35
  126. package/scripts/sw-webhook.sh +30 -25
  127. package/scripts/sw-widgets.sh +30 -19
  128. package/scripts/sw-worktree.sh +32 -15
  129. package/tmux/templates/doc-fleet.json +43 -0
@@ -7,33 +7,42 @@
7
7
  set -euo pipefail
8
8
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
9
9
 
10
- VERSION="2.1.1"
10
+ VERSION="2.2.0"
11
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
12
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
13
13
 
14
- # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
15
- CYAN='\033[38;2;0;212;255m' # #00d4ff — primary accent
16
- PURPLE='\033[38;2;124;58;237m' # #7c3aed — secondary
17
- BLUE='\033[38;2;0;102;255m' # #0066ff — tertiary
18
- GREEN='\033[38;2;74;222;128m' # success
19
- YELLOW='\033[38;2;250;204;21m' # warning
20
- RED='\033[38;2;248;113;113m' # error
21
- DIM='\033[2m'
22
- BOLD='\033[1m'
23
- RESET='\033[0m'
24
-
25
14
  # ─── Cross-platform compatibility ──────────────────────────────────────────
26
15
  # shellcheck source=lib/compat.sh
27
16
  [[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
28
-
29
- # ─── Output Helpers ─────────────────────────────────────────────────────────
30
- info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
31
- success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
32
- warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
33
- error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
34
-
35
- now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
36
- now_epoch() { date +%s; }
17
+ # Canonical helpers (colors, output, events)
18
+ # shellcheck source=lib/helpers.sh
19
+ [[ -f "$SCRIPT_DIR/lib/helpers.sh" ]] && source "$SCRIPT_DIR/lib/helpers.sh"
20
+ # Fallbacks when helpers not loaded (e.g. test env with overridden SCRIPT_DIR)
21
+ [[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
22
+ [[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
23
+ [[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
24
+ [[ "$(type -t error 2>/dev/null)" == "function" ]] || error() { echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m $*" >&2; }
25
+ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
26
+ now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
27
+ now_epoch() { date +%s; }
28
+ fi
29
+ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
30
+ emit_event() {
31
+ local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
32
+ local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
33
+ while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
34
+ echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
35
+ }
36
+ fi
37
+ CYAN="${CYAN:-\033[38;2;0;212;255m}"
38
+ PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
39
+ BLUE="${BLUE:-\033[38;2;0;102;255m}"
40
+ GREEN="${GREEN:-\033[38;2;74;222;128m}"
41
+ YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
42
+ RED="${RED:-\033[38;2;248;113;113m}"
43
+ DIM="${DIM:-\033[2m}"
44
+ BOLD="${BOLD:-\033[1m}"
45
+ RESET="${RESET:-\033[0m}"
37
46
 
38
47
  format_duration() {
39
48
  local secs="$1"
@@ -49,24 +58,6 @@ format_duration() {
49
58
  # ─── Structured Event Log ──────────────────────────────────────────────────
50
59
  EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
51
60
 
52
- emit_event() {
53
- local event_type="$1"
54
- shift
55
- local json_fields=""
56
- for kv in "$@"; do
57
- local key="${kv%%=*}"
58
- local val="${kv#*=}"
59
- if [[ "$val" =~ ^-?[0-9]+\.?[0-9]*$ ]]; then
60
- json_fields="${json_fields},\"${key}\":${val}"
61
- else
62
- val="${val//\"/\\\"}"
63
- json_fields="${json_fields},\"${key}\":\"${val}\""
64
- fi
65
- done
66
- mkdir -p "${HOME}/.shipwright"
67
- echo "{\"ts\":\"$(now_iso)\",\"ts_epoch\":$(now_epoch),\"type\":\"${event_type}\"${json_fields}}" >> "$EVENTS_FILE"
68
- }
69
-
70
61
  # ─── State & Config Paths ─────────────────────────────────────────────────
71
62
  STATE_DIR="${HOME}/.shipwright/autonomous"
72
63
  STATE_FILE="${STATE_DIR}/state.json"
@@ -310,8 +301,22 @@ trigger_pipeline_for_finding() {
310
301
  if [[ ! -x "$SCRIPT_DIR/sw-pipeline.sh" ]]; then
311
302
  return 0
312
303
  fi
304
+
305
+ # Use recruit for model/team selection when available
306
+ local -a recruit_args=()
307
+ if [[ -x "$SCRIPT_DIR/sw-recruit.sh" ]]; then
308
+ local recruit_match
309
+ recruit_match=$(bash "$SCRIPT_DIR/sw-recruit.sh" match --json "$title" 2>/dev/null) || true
310
+ if [[ -n "$recruit_match" ]]; then
311
+ local rec_model
312
+ rec_model=$(echo "$recruit_match" | jq -r '.model // ""' 2>/dev/null) || true
313
+ [[ -n "$rec_model" && "$rec_model" != "null" ]] && recruit_args=(--model "$rec_model")
314
+ emit_event "autonomous.recruit_match" "issue=$issue_num" "role=$(echo "$recruit_match" | jq -r '.primary_role // ""' 2>/dev/null)" "model=$rec_model"
315
+ fi
316
+ fi
317
+
313
318
  info "Triggering pipeline for finding issue #${issue_num}: $title"
314
- (cd "$REPO_DIR" && export REPO_DIR SCRIPT_DIR && bash "$SCRIPT_DIR/sw-pipeline.sh" start --issue "$issue_num" 2>/dev/null) &
319
+ (cd "$REPO_DIR" && export REPO_DIR SCRIPT_DIR && bash "$SCRIPT_DIR/sw-pipeline.sh" start --issue "$issue_num" "${recruit_args[@]}" 2>/dev/null) &
315
320
  emit_event "autonomous.pipeline_triggered" "issue=$issue_num" "title=$title"
316
321
  }
317
322
 
@@ -400,6 +405,21 @@ process_findings() {
400
405
  labels="${category}${labels:+,$labels}"
401
406
  fi
402
407
 
408
+ # Use recruit to decompose complex findings and assess team needs
409
+ if [[ -x "$SCRIPT_DIR/sw-recruit.sh" && "$effort" == "L" ]]; then
410
+ local team_json
411
+ team_json=$(bash "$SCRIPT_DIR/sw-recruit.sh" team --json "$title" 2>/dev/null) || true
412
+ if [[ -n "$team_json" ]]; then
413
+ local team_size
414
+ team_size=$(echo "$team_json" | jq -r '.agents // 0' 2>/dev/null) || true
415
+ [[ -n "$team_size" && "$team_size" -gt 0 ]] && \
416
+ description="${description}
417
+
418
+ ---
419
+ **Recruit Recommendation:** ${team_size}-agent team ($(echo "$team_json" | jq -r '.team | join(", ")' 2>/dev/null))"
420
+ fi
421
+ fi
422
+
403
423
  local issue_num
404
424
  issue_num=$(create_issue_from_finding "$title" "$description" "$priority" "$effort" "$labels")
405
425
  if [[ $? -eq 0 && -n "$issue_num" ]]; then
@@ -7,43 +7,42 @@
7
7
  set -euo pipefail
8
8
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
9
9
 
10
- VERSION="2.1.1"
10
+ VERSION="2.2.0"
11
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
12
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
13
13
 
14
- # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
15
- CYAN='\033[38;2;0;212;255m' # #00d4ff — primary accent
16
- PURPLE='\033[38;2;124;58;237m' # #7c3aed — secondary
17
- BLUE='\033[38;2;0;102;255m' # #0066ff — tertiary
18
- GREEN='\033[38;2;74;222;128m' # success
19
- YELLOW='\033[38;2;250;204;21m' # warning
20
- RED='\033[38;2;248;113;113m' # error
21
- DIM='\033[2m'
22
- BOLD='\033[1m'
23
- RESET='\033[0m'
24
-
25
14
  # ─── Cross-platform compatibility ──────────────────────────────────────────
26
15
  [[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
27
16
 
28
- # ─── 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
- emit_event() {
35
- local event_type="$1"; shift
36
- local events_file="${HOME}/.shipwright/events.jsonl"
37
- mkdir -p "$(dirname "$events_file")"
17
+ # Canonical helpers (colors, output, events)
18
+ # shellcheck source=lib/helpers.sh
19
+ [[ -f "$SCRIPT_DIR/lib/helpers.sh" ]] && source "$SCRIPT_DIR/lib/helpers.sh"
20
+ # Fallbacks when helpers not loaded (e.g. test env with overridden SCRIPT_DIR)
21
+ [[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
22
+ [[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
23
+ [[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
24
+ [[ "$(type -t error 2>/dev/null)" == "function" ]] || error() { echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m $*" >&2; }
25
+ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
26
+ now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
27
+ now_epoch() { date +%s; }
28
+ fi
29
+ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
30
+ emit_event() {
31
+ local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
38
32
  local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
39
- while [[ $# -gt 0 ]]; do
40
- local key="${1%%=*}" val="${1#*=}"
41
- payload="${payload},\"${key}\":\"${val}\""
42
- shift
43
- done
44
- payload="${payload}}"
45
- echo "$payload" >> "$events_file"
46
- }
33
+ while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
34
+ echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
35
+ }
36
+ fi
37
+ CYAN="${CYAN:-\033[38;2;0;212;255m}"
38
+ PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
39
+ BLUE="${BLUE:-\033[38;2;0;102;255m}"
40
+ GREEN="${GREEN:-\033[38;2;74;222;128m}"
41
+ YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
42
+ RED="${RED:-\033[38;2;248;113;113m}"
43
+ DIM="${DIM:-\033[2m}"
44
+ BOLD="${BOLD:-\033[1m}"
45
+ RESET="${RESET:-\033[0m}"
47
46
 
48
47
  # ─── Commit Parsing ──────────────────────────────────────────────────────────
49
48
 
@@ -8,30 +8,42 @@
8
8
  set -euo pipefail
9
9
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
10
10
 
11
- VERSION="2.1.1"
11
+ VERSION="2.2.0"
12
12
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
13
 
14
- # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
15
- CYAN='\033[38;2;0;212;255m' # #00d4ff — primary accent
16
- PURPLE='\033[38;2;124;58;237m' # #7c3aed — secondary
17
- BLUE='\033[38;2;0;102;255m' # #0066ff — tertiary
18
- GREEN='\033[38;2;74;222;128m' # success
19
- YELLOW='\033[38;2;250;204;21m' # warning
20
- RED='\033[38;2;248;113;113m' # error
21
- DIM='\033[2m'
22
- BOLD='\033[1m'
23
- RESET='\033[0m'
24
-
25
14
  # ─── Cross-platform compatibility ──────────────────────────────────────────
26
15
  # shellcheck source=lib/compat.sh
27
16
  [[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
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
17
 
34
- now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
18
+ # Canonical helpers (colors, output, events)
19
+ # shellcheck source=lib/helpers.sh
20
+ [[ -f "$SCRIPT_DIR/lib/helpers.sh" ]] && source "$SCRIPT_DIR/lib/helpers.sh"
21
+ # Fallbacks when helpers not loaded (e.g. test env with overridden SCRIPT_DIR)
22
+ [[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
23
+ [[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
24
+ [[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
25
+ [[ "$(type -t error 2>/dev/null)" == "function" ]] || error() { echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m $*" >&2; }
26
+ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
27
+ now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
28
+ now_epoch() { date +%s; }
29
+ fi
30
+ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
31
+ emit_event() {
32
+ local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
33
+ local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
34
+ while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
35
+ echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
36
+ }
37
+ fi
38
+ CYAN="${CYAN:-\033[38;2;0;212;255m}"
39
+ PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
40
+ BLUE="${BLUE:-\033[38;2;0;102;255m}"
41
+ GREEN="${GREEN:-\033[38;2;74;222;128m}"
42
+ YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
43
+ RED="${RED:-\033[38;2;248;113;113m}"
44
+ DIM="${DIM:-\033[2m}"
45
+ BOLD="${BOLD:-\033[1m}"
46
+ RESET="${RESET:-\033[0m}"
35
47
 
36
48
  # ─── Checkpoint Directory ───────────────────────────────────────────────────
37
49
  CHECKPOINT_DIR=".claude/pipeline-artifacts/checkpoints"
package/scripts/sw-ci.sh CHANGED
@@ -6,55 +6,43 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.1.1"
9
+ VERSION="2.2.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
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
13
  # ─── Cross-platform compatibility ──────────────────────────────────────────
25
14
  # shellcheck source=lib/compat.sh
26
15
  [[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
27
16
 
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
- local escaped_val
51
- escaped_val=$(printf '%s' "$val" | jq -Rs '.' 2>/dev/null || printf '"%s"' "${val//\"/\\\"}")
52
- json_fields="${json_fields},\"${key}\":${escaped_val}"
53
- fi
54
- done
55
- mkdir -p "${HOME}/.shipwright"
56
- echo "{\"ts\":\"$(now_iso)\",\"ts_epoch\":$(now_epoch),\"type\":\"${event_type}\"${json_fields}}" >> "$EVENTS_FILE"
57
- }
17
+ # Canonical helpers (colors, output, events)
18
+ # shellcheck source=lib/helpers.sh
19
+ [[ -f "$SCRIPT_DIR/lib/helpers.sh" ]] && source "$SCRIPT_DIR/lib/helpers.sh"
20
+ # Fallbacks when helpers not loaded (e.g. test env with overridden SCRIPT_DIR)
21
+ [[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
22
+ [[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
23
+ [[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
24
+ [[ "$(type -t error 2>/dev/null)" == "function" ]] || error() { echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m $*" >&2; }
25
+ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
26
+ now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
27
+ now_epoch() { date +%s; }
28
+ fi
29
+ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
30
+ emit_event() {
31
+ local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
32
+ local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
33
+ while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
34
+ echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
35
+ }
36
+ fi
37
+ CYAN="${CYAN:-\033[38;2;0;212;255m}"
38
+ PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
39
+ BLUE="${BLUE:-\033[38;2;0;102;255m}"
40
+ GREEN="${GREEN:-\033[38;2;74;222;128m}"
41
+ YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
42
+ RED="${RED:-\033[38;2;248;113;113m}"
43
+ DIM="${DIM:-\033[2m}"
44
+ BOLD="${BOLD:-\033[1m}"
45
+ RESET="${RESET:-\033[0m}"
58
46
 
59
47
  # ─── Generate Workflow YAML from Pipeline Template ──────────────────────────
60
48
 
@@ -5,24 +5,41 @@
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="2.1.1"
8
+ VERSION="2.2.0"
9
9
  set -euo pipefail
10
10
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
11
11
 
12
- # ─── Colors ──────────────────────────────────────────────────────────────────
13
- CYAN='\033[38;2;0;212;255m'
14
- PURPLE='\033[38;2;124;58;237m'
15
- GREEN='\033[38;2;74;222;128m'
16
- YELLOW='\033[38;2;250;204;21m'
17
- RED='\033[38;2;248;113;113m'
18
- DIM='\033[2m'
19
- BOLD='\033[1m'
20
- RESET='\033[0m'
21
-
22
- info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
23
- success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
24
- warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
25
- error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
12
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
+
14
+ # Canonical helpers (colors, output, events)
15
+ # shellcheck source=lib/helpers.sh
16
+ [[ -f "$SCRIPT_DIR/lib/helpers.sh" ]] && source "$SCRIPT_DIR/lib/helpers.sh"
17
+ # Fallbacks when helpers not loaded (e.g. test env with overridden SCRIPT_DIR)
18
+ [[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
19
+ [[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
20
+ [[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
21
+ [[ "$(type -t error 2>/dev/null)" == "function" ]] || error() { echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m $*" >&2; }
22
+ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
23
+ now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
24
+ now_epoch() { date +%s; }
25
+ fi
26
+ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
27
+ emit_event() {
28
+ local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
29
+ local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
30
+ while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
31
+ echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
32
+ }
33
+ fi
34
+ CYAN="${CYAN:-\033[38;2;0;212;255m}"
35
+ PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
36
+ BLUE="${BLUE:-\033[38;2;0;102;255m}"
37
+ GREEN="${GREEN:-\033[38;2;74;222;128m}"
38
+ YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
39
+ RED="${RED:-\033[38;2;248;113;113m}"
40
+ DIM="${DIM:-\033[2m}"
41
+ BOLD="${BOLD:-\033[1m}"
42
+ RESET="${RESET:-\033[0m}"
26
43
 
27
44
  # ─── Parse Args ──────────────────────────────────────────────────────────────
28
45
 
@@ -6,44 +6,38 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.1.1"
9
+ VERSION="2.2.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
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
13
  # ─── Cross-platform compatibility ──────────────────────────────────────────
25
14
  # shellcheck source=lib/compat.sh
26
15
  [[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
27
-
28
- # ─── 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
- emit_event() {
35
- local type="$1"; shift
36
- local entry="{\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$type\""
37
- while [[ $# -gt 0 ]]; do
38
- local key="${1%%=*}"; local val="${1#*=}"
39
- val="${val//\"/\\\"}"
40
- entry="$entry,\"${key}\":\"${val}\""
41
- shift
42
- done
43
- entry="$entry}"
44
- mkdir -p "$HOME/.shipwright"
45
- echo "$entry" >> "$HOME/.shipwright/events.jsonl"
46
- }
16
+ # Canonical helpers (colors, output, events)
17
+ # shellcheck source=lib/helpers.sh
18
+ [[ -f "$SCRIPT_DIR/lib/helpers.sh" ]] && source "$SCRIPT_DIR/lib/helpers.sh"
19
+ # Fallbacks when helpers not loaded (e.g. test env with overridden SCRIPT_DIR)
20
+ [[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
21
+ [[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
22
+ [[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
23
+ [[ "$(type -t error 2>/dev/null)" == "function" ]] || error() { echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m $*" >&2; }
24
+ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
25
+ emit_event() {
26
+ local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
27
+ local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
28
+ while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
29
+ echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
30
+ }
31
+ fi
32
+ CYAN="${CYAN:-\033[38;2;0;212;255m}"
33
+ PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
34
+ BLUE="${BLUE:-\033[38;2;0;102;255m}"
35
+ GREEN="${GREEN:-\033[38;2;74;222;128m}"
36
+ YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
37
+ RED="${RED:-\033[38;2;248;113;113m}"
38
+ DIM="${DIM:-\033[2m}"
39
+ BOLD="${BOLD:-\033[1m}"
40
+ RESET="${RESET:-\033[0m}"
47
41
 
48
42
  # ─── Configuration ───────────────────────────────────────────────────────
49
43
  REVIEW_CONFIG="${REPO_DIR}/.claude/code-review.json"
@@ -8,29 +8,42 @@
8
8
  set -euo pipefail
9
9
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
10
10
 
11
- VERSION="2.1.1"
11
+ VERSION="2.2.0"
12
12
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
13
 
14
- # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
15
- CYAN='\033[38;2;0;212;255m' # #00d4ff — primary accent
16
- PURPLE='\033[38;2;124;58;237m' # #7c3aed — secondary
17
- BLUE='\033[38;2;0;102;255m' # #0066ff — tertiary
18
- GREEN='\033[38;2;74;222;128m' # success
19
- YELLOW='\033[38;2;250;204;21m' # warning
20
- RED='\033[38;2;248;113;113m' # error
21
- DIM='\033[2m'
22
- BOLD='\033[1m'
23
- RESET='\033[0m'
24
-
25
14
  # ─── Cross-platform compatibility ──────────────────────────────────────────
26
15
  # shellcheck source=lib/compat.sh
27
16
  [[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
28
17
 
29
- # ─── Output Helpers ─────────────────────────────────────────────────────────
30
- info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
31
- success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
32
- warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
33
- error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
18
+ # Canonical helpers (colors, output, events)
19
+ # shellcheck source=lib/helpers.sh
20
+ [[ -f "$SCRIPT_DIR/lib/helpers.sh" ]] && source "$SCRIPT_DIR/lib/helpers.sh"
21
+ # Fallbacks when helpers not loaded (e.g. test env with overridden SCRIPT_DIR)
22
+ [[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
23
+ [[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
24
+ [[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
25
+ [[ "$(type -t error 2>/dev/null)" == "function" ]] || error() { echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m $*" >&2; }
26
+ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
27
+ now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
28
+ now_epoch() { date +%s; }
29
+ fi
30
+ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
31
+ emit_event() {
32
+ local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
33
+ local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
34
+ while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
35
+ echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
36
+ }
37
+ fi
38
+ CYAN="${CYAN:-\033[38;2;0;212;255m}"
39
+ PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
40
+ BLUE="${BLUE:-\033[38;2;0;102;255m}"
41
+ GREEN="${GREEN:-\033[38;2;74;222;128m}"
42
+ YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
43
+ RED="${RED:-\033[38;2;248;113;113m}"
44
+ DIM="${DIM:-\033[2m}"
45
+ BOLD="${BOLD:-\033[1m}"
46
+ RESET="${RESET:-\033[0m}"
34
47
 
35
48
  # ─── Constants ──────────────────────────────────────────────────────────────
36
49
  SHIPWRIGHT_DIR="$HOME/.shipwright"
@@ -43,8 +56,6 @@ CONNECT_LOG="$SHIPWRIGHT_DIR/connect.log"
43
56
  DEFAULT_URL="http://localhost:8767"
44
57
  HEARTBEAT_INTERVAL=10
45
58
 
46
- now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
47
-
48
59
  ensure_dir() {
49
60
  mkdir -p "$SHIPWRIGHT_DIR"
50
61
  }
@@ -6,32 +6,43 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.1.1"
9
+ VERSION="2.2.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
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
13
  # ─── Cross-platform compatibility ──────────────────────────────────────────
25
14
  # shellcheck source=lib/compat.sh
26
15
  [[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
27
16
 
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"; }
17
+ # Canonical helpers (colors, output, events)
18
+ # shellcheck source=lib/helpers.sh
19
+ [[ -f "$SCRIPT_DIR/lib/helpers.sh" ]] && source "$SCRIPT_DIR/lib/helpers.sh"
20
+ # Fallbacks when helpers not loaded (e.g. test env with overridden SCRIPT_DIR)
21
+ [[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
22
+ [[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
23
+ [[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
24
+ [[ "$(type -t error 2>/dev/null)" == "function" ]] || error() { echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m $*" >&2; }
25
+ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
26
+ now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
27
+ now_epoch() { date +%s; }
28
+ fi
29
+ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
30
+ emit_event() {
31
+ local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
32
+ local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
33
+ while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
34
+ echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
35
+ }
36
+ fi
37
+ CYAN="${CYAN:-\033[38;2;0;212;255m}"
38
+ PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
39
+ BLUE="${BLUE:-\033[38;2;0;102;255m}"
40
+ GREEN="${GREEN:-\033[38;2;74;222;128m}"
41
+ YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
42
+ RED="${RED:-\033[38;2;248;113;113m}"
43
+ DIM="${DIM:-\033[2m}"
44
+ BOLD="${BOLD:-\033[1m}"
45
+ RESET="${RESET:-\033[0m}"
35
46
 
36
47
  # ─── Paths ────────────────────────────────────────────────────────────────
37
48
  ARTIFACTS_DIR="${REPO_DIR}/.claude/pipeline-artifacts"