shipwright-cli 2.1.2 → 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 +66 -36
  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 +32 -19
  77. package/scripts/sw-memory.sh +32 -43
  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 +275 -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 +30 -19
  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
@@ -6,33 +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.2"
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; }
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}"
36
46
 
37
47
  format_duration() {
38
48
  local secs="$1"
@@ -48,24 +58,6 @@ format_duration() {
48
58
  # ─── Structured Event Log ──────────────────────────────────────────────────
49
59
  EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
50
60
 
51
- emit_event() {
52
- local event_type="$1"
53
- shift
54
- local json_fields=""
55
- for kv in "$@"; do
56
- local key="${kv%%=*}"
57
- local val="${kv#*=}"
58
- if [[ "$val" =~ ^-?[0-9]+\.?[0-9]*$ ]]; then
59
- json_fields="${json_fields},\"${key}\":${val}"
60
- else
61
- val="${val//\"/\\\"}"
62
- json_fields="${json_fields},\"${key}\":\"${val}\""
63
- fi
64
- done
65
- mkdir -p "${HOME}/.shipwright"
66
- echo "{\"ts\":\"$(now_iso)\",\"ts_epoch\":$(now_epoch),\"type\":\"${event_type}\"${json_fields}}" >> "$EVENTS_FILE"
67
- }
68
-
69
61
  # ─── Regression Storage ────────────────────────────────────────────────────
70
62
  BASELINES_DIR="${HOME}/.shipwright/baselines"
71
63
  LATEST_BASELINE="${BASELINES_DIR}/latest.json"
@@ -6,55 +6,47 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.1.2"
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; }
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}"
36
46
 
37
47
  # ─── Structured Event Log ──────────────────────────────────────────────────
38
48
  EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
39
49
 
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
50
  # ─── Release State Storage ─────────────────────────────────────────────────
59
51
  RELEASE_STATE_DIR="${HOME}/.shipwright/releases"
60
52
 
@@ -6,45 +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.2"
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
- # ─── Helpers ─────────────────────────────────────────────────────────────────
29
-
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
- emit_event() {
36
- local event_type="$1"; shift
37
- local events_file="${HOME}/.shipwright/events.jsonl"
38
- 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"
39
32
  local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
40
- while [[ $# -gt 0 ]]; do
41
- local key="${1%%=*}" val="${1#*=}"
42
- payload="${payload},\"${key}\":\"${val}\""
43
- shift
44
- done
45
- payload="${payload}}"
46
- echo "$payload" >> "$events_file"
47
- }
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}"
48
46
 
49
47
  # ─── Parse flags ───────────────────────────────────────────────────────────
50
48
  DRY_RUN=false
@@ -6,52 +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.2"
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
- # ─── Output Helpers ─────────────────────────────────────────────────────────
28
- info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
29
- success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
30
- warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
31
- error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
32
-
33
- now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
34
-
35
- # ─── Structured Event Log ──────────────────────────────────────────────────
36
- EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
37
-
38
- emit_event() {
39
- local event_type="$1"
40
- shift
41
- local json_fields=""
42
- for kv in "$@"; do
43
- local key="${kv%%=*}"
44
- local val="${kv#*=}"
45
- if [[ "$val" =~ ^-?[0-9]+\.?[0-9]*$ ]]; then
46
- json_fields="${json_fields},\"${key}\":${val}"
47
- else
48
- val="${val//\"/\\\"}"
49
- json_fields="${json_fields},\"${key}\":\"${val}\""
50
- fi
51
- done
52
- mkdir -p "${HOME}/.shipwright"
53
- echo "{\"ts\":\"$(now_iso)\",\"type\":\"${event_type}\"${json_fields}}" >> "$EVENTS_FILE"
54
- }
16
+
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}"
55
46
 
56
47
  # ─── Defaults ───────────────────────────────────────────────────────────────
57
48
  MACHINES_FILE="$HOME/.shipwright/machines.json"
@@ -6,31 +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.2"
9
+ VERSION="2.2.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
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
- # ─── Helpers ─────────────────────────────────────────────────────────────────
29
-
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; }
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}"
34
46
 
35
47
  # Check if jq is available
36
48
  check_jq() {
@@ -6,32 +6,42 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.1.2"
9
+ VERSION="2.2.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
- # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
13
- CYAN='\033[38;2;0;212;255m' # #00d4ff — primary accent
14
- PURPLE='\033[38;2;124;58;237m' # #7c3aed — secondary
15
- BLUE='\033[38;2;0;102;255m' # #0066ff — tertiary
16
- GREEN='\033[38;2;74;222;128m' # #4ade80 — success
17
- YELLOW='\033[38;2;250;204;21m' # #faca15 — warning
18
- RED='\033[38;2;248;113;113m' # #f87171 — error
19
- DIM='\033[2m'
20
- BOLD='\033[1m'
21
- RESET='\033[0m'
22
-
23
12
  # ─── Cross-platform compatibility ──────────────────────────────────────────
24
13
  _COMPAT="$SCRIPT_DIR/lib/compat.sh"
25
14
  [[ -f "$_COMPAT" ]] && source "$_COMPAT"
26
15
 
27
- # ─── Helpers ────────────────────────────────────────────────────────────────
28
- info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
29
- success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
30
- warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
31
- error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
32
-
33
- now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
34
- now_epoch() { date +%s; }
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 now_iso 2>/dev/null)" != "function" ]]; then
25
+ now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
26
+ now_epoch() { date +%s; }
27
+ fi
28
+ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
29
+ emit_event() {
30
+ local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
31
+ local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
32
+ while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
33
+ echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
34
+ }
35
+ fi
36
+ CYAN="${CYAN:-\033[38;2;0;212;255m}"
37
+ PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
38
+ BLUE="${BLUE:-\033[38;2;0;102;255m}"
39
+ GREEN="${GREEN:-\033[38;2;74;222;128m}"
40
+ YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
41
+ RED="${RED:-\033[38;2;248;113;113m}"
42
+ DIM="${DIM:-\033[2m}"
43
+ BOLD="${BOLD:-\033[1m}"
44
+ RESET="${RESET:-\033[0m}"
35
45
 
36
46
  epoch_to_iso() {
37
47
  local epoch="$1"
@@ -52,25 +62,6 @@ format_duration() {
52
62
  fi
53
63
  }
54
64
 
55
- emit_event() {
56
- local event_type="$1"; shift
57
- local events_file="${HOME}/.shipwright/events.jsonl"
58
- mkdir -p "$(dirname "$events_file")"
59
- local payload="{\"ts\":\"$(now_iso)\",\"ts_epoch\":$(now_epoch),\"type\":\"$event_type\""
60
- for kv in "$@"; do
61
- local key="${kv%%=*}"
62
- local val="${kv#*=}"
63
- if [[ "$val" =~ ^-?[0-9]+\.?[0-9]*$ ]]; then
64
- payload="${payload},\"${key}\":${val}"
65
- else
66
- val="${val//\"/\\\"}"
67
- payload="${payload},\"${key}\":\"${val}\""
68
- fi
69
- done
70
- payload="${payload}}"
71
- echo "$payload" >> "$events_file"
72
- }
73
-
74
65
  # ─── State Storage ──────────────────────────────────────────────────────────
75
66
  RETRO_DIR="${HOME}/.shipwright/retros"
76
67
  ensure_retro_dir() {
@@ -166,9 +157,9 @@ analyze_agent_performance() {
166
157
 
167
158
  jq -s --argjson from "$from_epoch" --argjson to "$to_epoch" '
168
159
  [.[] | select(.ts_epoch >= $from and .ts_epoch <= $to)] as $events |
169
- [$events[] | select(.type == "pipeline.completed" and .agent)] as $completions |
170
- $completions | group_by(.agent) | map({
171
- agent: .[0].agent,
160
+ [$events[] | select(.type == "pipeline.completed" and (.agent_id // .agent))] as $completions |
161
+ $completions | group_by(.agent_id // .agent) | map({
162
+ agent: .[0].agent_id // .[0].agent,
172
163
  completed: length,
173
164
  succeeded: ([.[] | select(.result == "success")] | length),
174
165
  failed: ([.[] | select(.result == "failure")] | length),
@@ -6,38 +6,54 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.1.2"
9
+ VERSION="2.2.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
- # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
13
- CYAN='\033[38;2;0;212;255m' # #00d4ff primary accent
14
- PURPLE='\033[38;2;124;58;237m' # #7c3aed secondary
15
- BLUE='\033[38;2;0;102;255m' # #0066ff — tertiary
16
- GREEN='\033[38;2;74;222;128m' # success
17
- YELLOW='\033[38;2;250;204;21m' # warning
18
- RED='\033[38;2;248;113;113m' # error
19
- DIM='\033[2m'
20
- BOLD='\033[1m'
21
- RESET='\033[0m'
12
+ # ─── Dependency check ─────────────────────────────────────────────────────────
13
+ if ! command -v jq &>/dev/null; then
14
+ echo "ERROR: sw-scale.sh requires 'jq'. Install with: brew install jq (macOS) or apt install jq (Linux)" >&2
15
+ exit 1
16
+ fi
22
17
 
23
18
  # ─── Cross-platform compatibility ──────────────────────────────────────────
24
19
  # shellcheck source=lib/compat.sh
25
20
  [[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
26
21
 
27
- # ─── Output Helpers ─────────────────────────────────────────────────────────
28
- info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
29
- success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
30
- warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
31
- error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
22
+ # Canonical helpers (colors, output, events)
23
+ # shellcheck source=lib/helpers.sh
24
+ [[ -f "$SCRIPT_DIR/lib/helpers.sh" ]] && source "$SCRIPT_DIR/lib/helpers.sh"
25
+ # Fallbacks when helpers not loaded (e.g. test env with overridden SCRIPT_DIR)
26
+ [[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
27
+ [[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
28
+ [[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
29
+ [[ "$(type -t error 2>/dev/null)" == "function" ]] || error() { echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m $*" >&2; }
30
+ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
31
+ now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
32
+ now_epoch() { date +%s; }
33
+ fi
34
+ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
35
+ emit_event() {
36
+ local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
37
+ local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
38
+ while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
39
+ echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
40
+ }
41
+ fi
42
+ CYAN="${CYAN:-\033[38;2;0;212;255m}"
43
+ PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
44
+ BLUE="${BLUE:-\033[38;2;0;102;255m}"
45
+ GREEN="${GREEN:-\033[38;2;74;222;128m}"
46
+ YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
47
+ RED="${RED:-\033[38;2;248;113;113m}"
48
+ DIM="${DIM:-\033[2m}"
49
+ BOLD="${BOLD:-\033[1m}"
50
+ RESET="${RESET:-\033[0m}"
32
51
 
33
52
  # ─── Constants ──────────────────────────────────────────────────────────────
34
53
  SCALE_RULES_FILE="${HOME}/.shipwright/scale-rules.json"
35
54
  SCALE_EVENTS_FILE="${HOME}/.shipwright/scale-events.jsonl"
36
55
  SCALE_STATE_FILE="${HOME}/.shipwright/scale-state.json"
37
56
 
38
- now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
39
- now_unix() { date +%s; }
40
-
41
57
  # ─── Ensure directories exist ──────────────────────────────────────────────
42
58
  ensure_dirs() {
43
59
  mkdir -p "$HOME/.shipwright"
@@ -56,7 +72,7 @@ init_rules() {
56
72
  "budget_check": true,
57
73
  "cooldown_seconds": 120,
58
74
  "max_team_size": 8,
59
- "roles": ["builder", "reviewer", "tester", "security"]
75
+ "roles": ["builder", "reviewer", "tester", "security-auditor"]
60
76
  }
61
77
  JSON
62
78
  mv "$tmp_file" "$SCALE_RULES_FILE"
@@ -80,7 +96,7 @@ in_cooldown() {
80
96
  local last_time
81
97
  last_time=$(get_last_scale_time)
82
98
  local now
83
- now=$(now_unix)
99
+ now=$(now_epoch)
84
100
  local elapsed=$((now - last_time))
85
101
  [[ $elapsed -lt $cooldown ]]
86
102
  }
@@ -92,12 +108,12 @@ update_scale_state() {
92
108
 
93
109
  if [[ -f "$SCALE_STATE_FILE" ]]; then
94
110
  # Update existing state
95
- jq --arg now "$(now_unix)" '.last_scale_time = ($now | tonumber)' "$SCALE_STATE_FILE" > "$tmp_file"
111
+ jq --arg now "$(now_epoch)" '.last_scale_time = ($now | tonumber)' "$SCALE_STATE_FILE" > "$tmp_file"
96
112
  else
97
113
  # Create new state
98
114
  cat > "$tmp_file" << JSON
99
115
  {
100
- "last_scale_time": $(now_unix),
116
+ "last_scale_time": $(now_epoch),
101
117
  "team_size": 0,
102
118
  "events_count": 0
103
119
  }
@@ -126,6 +142,7 @@ emit_scale_event() {
126
142
  '{ts: $ts, action: $action, role: $role, reason: $reason, context: $context}')
127
143
 
128
144
  echo "$event" >> "$SCALE_EVENTS_FILE"
145
+ type rotate_jsonl &>/dev/null 2>&1 && rotate_jsonl "$SCALE_EVENTS_FILE" 5000
129
146
  }
130
147
 
131
148
  # ─── Scale Up: spawn new agent ───────────────────────────────────────────
@@ -137,7 +154,7 @@ cmd_up() {
137
154
  init_rules
138
155
 
139
156
  # Validate role
140
- local valid_roles="builder reviewer tester security"
157
+ local valid_roles="builder reviewer tester security-auditor architect docs-writer optimizer devops pm incident-responder"
141
158
  if ! echo "$valid_roles" | grep -q "$role"; then
142
159
  error "Invalid role: $role. Valid roles: $valid_roles"
143
160
  return 1