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.
- package/README.md +114 -36
- package/completions/_shipwright +212 -32
- package/completions/shipwright.bash +97 -25
- package/docs/strategy/01-market-research.md +619 -0
- package/docs/strategy/02-mission-and-brand.md +587 -0
- package/docs/strategy/03-gtm-and-roadmap.md +759 -0
- package/docs/strategy/QUICK-START.txt +289 -0
- package/docs/strategy/README.md +172 -0
- package/package.json +4 -2
- package/scripts/sw +208 -1
- package/scripts/sw-activity.sh +500 -0
- package/scripts/sw-adaptive.sh +925 -0
- package/scripts/sw-adversarial.sh +1 -1
- package/scripts/sw-architecture-enforcer.sh +1 -1
- package/scripts/sw-auth.sh +613 -0
- package/scripts/sw-autonomous.sh +664 -0
- package/scripts/sw-changelog.sh +704 -0
- package/scripts/sw-checkpoint.sh +1 -1
- package/scripts/sw-ci.sh +602 -0
- package/scripts/sw-cleanup.sh +1 -1
- package/scripts/sw-code-review.sh +637 -0
- package/scripts/sw-connect.sh +1 -1
- package/scripts/sw-context.sh +605 -0
- package/scripts/sw-cost.sh +1 -1
- package/scripts/sw-daemon.sh +432 -130
- package/scripts/sw-dashboard.sh +1 -1
- package/scripts/sw-db.sh +540 -0
- package/scripts/sw-decompose.sh +539 -0
- package/scripts/sw-deps.sh +551 -0
- package/scripts/sw-developer-simulation.sh +1 -1
- package/scripts/sw-discovery.sh +412 -0
- package/scripts/sw-docs-agent.sh +539 -0
- package/scripts/sw-docs.sh +1 -1
- package/scripts/sw-doctor.sh +59 -1
- package/scripts/sw-dora.sh +615 -0
- package/scripts/sw-durable.sh +710 -0
- package/scripts/sw-e2e-orchestrator.sh +535 -0
- package/scripts/sw-eventbus.sh +393 -0
- package/scripts/sw-feedback.sh +471 -0
- package/scripts/sw-fix.sh +1 -1
- package/scripts/sw-fleet-discover.sh +567 -0
- package/scripts/sw-fleet-viz.sh +404 -0
- package/scripts/sw-fleet.sh +8 -1
- package/scripts/sw-github-app.sh +596 -0
- package/scripts/sw-github-checks.sh +1 -1
- package/scripts/sw-github-deploy.sh +1 -1
- package/scripts/sw-github-graphql.sh +1 -1
- package/scripts/sw-guild.sh +569 -0
- package/scripts/sw-heartbeat.sh +1 -1
- package/scripts/sw-hygiene.sh +559 -0
- package/scripts/sw-incident.sh +617 -0
- package/scripts/sw-init.sh +88 -1
- package/scripts/sw-instrument.sh +699 -0
- package/scripts/sw-intelligence.sh +1 -1
- package/scripts/sw-jira.sh +1 -1
- package/scripts/sw-launchd.sh +363 -28
- package/scripts/sw-linear.sh +1 -1
- package/scripts/sw-logs.sh +1 -1
- package/scripts/sw-loop.sh +64 -3
- package/scripts/sw-memory.sh +1 -1
- package/scripts/sw-mission-control.sh +487 -0
- package/scripts/sw-model-router.sh +545 -0
- package/scripts/sw-otel.sh +596 -0
- package/scripts/sw-oversight.sh +689 -0
- package/scripts/sw-pipeline-composer.sh +1 -1
- package/scripts/sw-pipeline-vitals.sh +1 -1
- package/scripts/sw-pipeline.sh +687 -24
- package/scripts/sw-pm.sh +693 -0
- package/scripts/sw-pr-lifecycle.sh +522 -0
- package/scripts/sw-predictive.sh +1 -1
- package/scripts/sw-prep.sh +1 -1
- package/scripts/sw-ps.sh +1 -1
- package/scripts/sw-public-dashboard.sh +798 -0
- package/scripts/sw-quality.sh +595 -0
- package/scripts/sw-reaper.sh +1 -1
- package/scripts/sw-recruit.sh +573 -0
- package/scripts/sw-regression.sh +642 -0
- package/scripts/sw-release-manager.sh +736 -0
- package/scripts/sw-release.sh +706 -0
- package/scripts/sw-remote.sh +1 -1
- package/scripts/sw-replay.sh +520 -0
- package/scripts/sw-retro.sh +691 -0
- package/scripts/sw-scale.sh +444 -0
- package/scripts/sw-security-audit.sh +505 -0
- package/scripts/sw-self-optimize.sh +1 -1
- package/scripts/sw-session.sh +1 -1
- package/scripts/sw-setup.sh +1 -1
- package/scripts/sw-standup.sh +712 -0
- package/scripts/sw-status.sh +1 -1
- package/scripts/sw-strategic.sh +658 -0
- package/scripts/sw-stream.sh +450 -0
- package/scripts/sw-swarm.sh +583 -0
- package/scripts/sw-team-stages.sh +511 -0
- package/scripts/sw-templates.sh +1 -1
- package/scripts/sw-testgen.sh +515 -0
- package/scripts/sw-tmux-pipeline.sh +554 -0
- package/scripts/sw-tmux.sh +1 -1
- package/scripts/sw-trace.sh +485 -0
- package/scripts/sw-tracker-github.sh +188 -0
- package/scripts/sw-tracker-jira.sh +172 -0
- package/scripts/sw-tracker-linear.sh +251 -0
- package/scripts/sw-tracker.sh +117 -2
- package/scripts/sw-triage.sh +603 -0
- package/scripts/sw-upgrade.sh +1 -1
- package/scripts/sw-ux.sh +677 -0
- package/scripts/sw-webhook.sh +627 -0
- package/scripts/sw-widgets.sh +530 -0
- package/scripts/sw-worktree.sh +1 -1
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
# ║ shipwright activity — Live agent activity stream ║
|
|
4
|
+
# ║ Watch Claude think in real-time with formatted event streaming ║
|
|
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
|
+
|
|
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'
|
|
22
|
+
|
|
23
|
+
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
|
24
|
+
# shellcheck source=lib/compat.sh
|
|
25
|
+
[ -f "$SCRIPT_DIR/lib/compat.sh" ] && source "$SCRIPT_DIR/lib/compat.sh"
|
|
26
|
+
|
|
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
|
+
# ─── Event File & Filters ─────────────────────────────────────────────────────
|
|
34
|
+
EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
|
|
35
|
+
FILTER_TYPE=""
|
|
36
|
+
FILTER_AGENT=""
|
|
37
|
+
FILTER_TEAM=""
|
|
38
|
+
FILTER_STAGE=""
|
|
39
|
+
FILTER_START=""
|
|
40
|
+
FILTER_END=""
|
|
41
|
+
|
|
42
|
+
# Event type icons for terminal display (bash 3.2 compatible)
|
|
43
|
+
get_icon_for_type() {
|
|
44
|
+
local type="$1"
|
|
45
|
+
case "$type" in
|
|
46
|
+
commit) echo "📦" ;;
|
|
47
|
+
test.passed) echo "✅" ;;
|
|
48
|
+
test.failed) echo "❌" ;;
|
|
49
|
+
build) echo "🔨" ;;
|
|
50
|
+
review) echo "👀" ;;
|
|
51
|
+
stage.started) echo "▶" ;;
|
|
52
|
+
stage.completed) echo "⏹" ;;
|
|
53
|
+
pipeline.started) echo "🚀" ;;
|
|
54
|
+
pipeline.completed) echo "🎯" ;;
|
|
55
|
+
file.modified) echo "✏" ;;
|
|
56
|
+
error) echo "⚠" ;;
|
|
57
|
+
*) echo "•" ;;
|
|
58
|
+
esac
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# Color agents by a simple hash (bash 3.2 compatible)
|
|
62
|
+
agent_color() {
|
|
63
|
+
local agent="$1"
|
|
64
|
+
local hash=0
|
|
65
|
+
local i
|
|
66
|
+
for (( i=0; i<${#agent}; i++ )); do
|
|
67
|
+
hash=$(( (hash << 5) - hash + $(printf '%d' "'${agent:$i:1}") ))
|
|
68
|
+
done
|
|
69
|
+
# Simple modulo to pick a color
|
|
70
|
+
local idx=$(( (hash % 5 + 5) % 5 ))
|
|
71
|
+
case "$idx" in
|
|
72
|
+
0) echo "$CYAN" ;;
|
|
73
|
+
1) echo "$PURPLE" ;;
|
|
74
|
+
2) echo "$BLUE" ;;
|
|
75
|
+
3) echo "$GREEN" ;;
|
|
76
|
+
4) echo "$YELLOW" ;;
|
|
77
|
+
esac
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# Emit structured event (for testing/logging)
|
|
81
|
+
emit_event() {
|
|
82
|
+
local event_type="$1"
|
|
83
|
+
shift
|
|
84
|
+
local json_fields=""
|
|
85
|
+
for kv in "$@"; do
|
|
86
|
+
local key="${kv%%=*}"
|
|
87
|
+
local val="${kv#*=}"
|
|
88
|
+
if echo "$val" | grep -qE '^-?[0-9]+\.?[0-9]*$'; then
|
|
89
|
+
json_fields="${json_fields},\"${key}\":${val}"
|
|
90
|
+
else
|
|
91
|
+
local escaped_val
|
|
92
|
+
escaped_val=$(printf '%s' "$val" | jq -Rs '.' 2>/dev/null || printf '"%s"' "${val//\"/\\\"}")
|
|
93
|
+
json_fields="${json_fields},\"${key}\":${escaped_val}"
|
|
94
|
+
fi
|
|
95
|
+
done
|
|
96
|
+
mkdir -p "${HOME}/.shipwright"
|
|
97
|
+
local iso_time
|
|
98
|
+
iso_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
99
|
+
local epoch_time
|
|
100
|
+
epoch_time=$(date +%s)
|
|
101
|
+
echo "{\"ts\":\"${iso_time}\",\"ts_epoch\":${epoch_time},\"type\":\"${event_type}\"${json_fields}}" >> "$EVENTS_FILE"
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# ─── Formatting Helpers ────────────────────────────────────────────────────────
|
|
105
|
+
format_timestamp() {
|
|
106
|
+
local ts="$1"
|
|
107
|
+
echo "$ts" | sed 's/T/ /; s/Z//'
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
get_event_icon() {
|
|
111
|
+
local event_type="$1"
|
|
112
|
+
get_icon_for_type "$event_type"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
format_event_line() {
|
|
116
|
+
local ts="$1"
|
|
117
|
+
local type="$2"
|
|
118
|
+
local agent="${3:-system}"
|
|
119
|
+
local message="$4"
|
|
120
|
+
|
|
121
|
+
local ts_fmt
|
|
122
|
+
ts_fmt=$(format_timestamp "$ts")
|
|
123
|
+
local icon
|
|
124
|
+
icon=$(get_event_icon "$type")
|
|
125
|
+
local agent_color_code
|
|
126
|
+
agent_color_code=$(agent_color "$agent")
|
|
127
|
+
|
|
128
|
+
printf "%s %s ${BOLD}%s${RESET} %s %s\n" \
|
|
129
|
+
"${DIM}${ts_fmt}${RESET}" \
|
|
130
|
+
"$icon" \
|
|
131
|
+
"${agent_color_code}${agent:0:12}${RESET}" \
|
|
132
|
+
"${BLUE}${type}${RESET}" \
|
|
133
|
+
"$message"
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# ─── Live Watch Mode (default) ──────────────────────────────────────────────────
|
|
137
|
+
cmd_watch() {
|
|
138
|
+
local poll_interval=1
|
|
139
|
+
local last_size=0
|
|
140
|
+
|
|
141
|
+
info "Watching agent activity (Ctrl+C to stop)..."
|
|
142
|
+
echo ""
|
|
143
|
+
|
|
144
|
+
# Create initial size check
|
|
145
|
+
if [ ! -f "$EVENTS_FILE" ]; then
|
|
146
|
+
warn "No events file yet (waiting for first pipeline run)..."
|
|
147
|
+
touch "$EVENTS_FILE"
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
last_size=$(wc -c < "$EVENTS_FILE" 2>/dev/null || echo 0)
|
|
151
|
+
|
|
152
|
+
while true; do
|
|
153
|
+
local current_size
|
|
154
|
+
current_size=$(wc -c < "$EVENTS_FILE" 2>/dev/null || echo 0)
|
|
155
|
+
|
|
156
|
+
# If file grew, tail new lines
|
|
157
|
+
if [ $current_size -gt $last_size ]; then
|
|
158
|
+
tail -c $((current_size - last_size)) "$EVENTS_FILE" 2>/dev/null | while IFS= read -r line; do
|
|
159
|
+
[ -z "$line" ] && continue
|
|
160
|
+
|
|
161
|
+
# Extract JSON fields
|
|
162
|
+
local ts type agent stage issue
|
|
163
|
+
ts=$(echo "$line" | jq -r '.ts // ""' 2>/dev/null || true)
|
|
164
|
+
type=$(echo "$line" | jq -r '.type // ""' 2>/dev/null || true)
|
|
165
|
+
agent=$(echo "$line" | jq -r '.agent // "system"' 2>/dev/null || true)
|
|
166
|
+
stage=$(echo "$line" | jq -r '.stage // ""' 2>/dev/null || true)
|
|
167
|
+
issue=$(echo "$line" | jq -r '.issue // ""' 2>/dev/null || true)
|
|
168
|
+
|
|
169
|
+
# Apply filters
|
|
170
|
+
if [ -n "$FILTER_TYPE" ] && [ "$type" != "$FILTER_TYPE" ]; then
|
|
171
|
+
continue
|
|
172
|
+
fi
|
|
173
|
+
if [ -n "$FILTER_AGENT" ] && [ "$agent" != "$FILTER_AGENT" ]; then
|
|
174
|
+
continue
|
|
175
|
+
fi
|
|
176
|
+
if [ -n "$FILTER_STAGE" ] && [ "$stage" != "$FILTER_STAGE" ]; then
|
|
177
|
+
continue
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
# Format message based on event type
|
|
181
|
+
local msg=""
|
|
182
|
+
case "$type" in
|
|
183
|
+
stage.started)
|
|
184
|
+
msg="Started ${stage} stage"
|
|
185
|
+
if [ -n "$issue" ]; then
|
|
186
|
+
msg="$msg (issue #${issue})"
|
|
187
|
+
fi
|
|
188
|
+
;;
|
|
189
|
+
stage.completed)
|
|
190
|
+
local duration
|
|
191
|
+
duration=$(echo "$line" | jq -r '.duration_s // "?"' 2>/dev/null || true)
|
|
192
|
+
msg="Completed ${stage} in ${duration}s"
|
|
193
|
+
;;
|
|
194
|
+
pipeline.started)
|
|
195
|
+
local pipeline
|
|
196
|
+
pipeline=$(echo "$line" | jq -r '.pipeline // "unknown"' 2>/dev/null || true)
|
|
197
|
+
msg="Started ${pipeline} pipeline"
|
|
198
|
+
;;
|
|
199
|
+
pipeline.completed)
|
|
200
|
+
local result
|
|
201
|
+
result=$(echo "$line" | jq -r '.result // "?"' 2>/dev/null || true)
|
|
202
|
+
msg="Pipeline finished: ${result}"
|
|
203
|
+
;;
|
|
204
|
+
file.modified)
|
|
205
|
+
local file
|
|
206
|
+
file=$(echo "$line" | jq -r '.file // "?"' 2>/dev/null || true)
|
|
207
|
+
msg="Modified: ${file}"
|
|
208
|
+
;;
|
|
209
|
+
test.passed)
|
|
210
|
+
local test_count
|
|
211
|
+
test_count=$(echo "$line" | jq -r '.count // "1"' 2>/dev/null || true)
|
|
212
|
+
msg="Tests passed: ${test_count}"
|
|
213
|
+
;;
|
|
214
|
+
test.failed)
|
|
215
|
+
local failure
|
|
216
|
+
failure=$(echo "$line" | jq -r '.reason // "unknown"' 2>/dev/null || true)
|
|
217
|
+
msg="Test failed: ${failure}"
|
|
218
|
+
;;
|
|
219
|
+
commit)
|
|
220
|
+
local commit_msg
|
|
221
|
+
commit_msg=$(echo "$line" | jq -r '.message // "?"' 2>/dev/null | cut -c1-50)
|
|
222
|
+
msg="Committed: ${commit_msg}"
|
|
223
|
+
;;
|
|
224
|
+
*)
|
|
225
|
+
msg="$type"
|
|
226
|
+
;;
|
|
227
|
+
esac
|
|
228
|
+
|
|
229
|
+
format_event_line "$ts" "$type" "$agent" "$msg"
|
|
230
|
+
done
|
|
231
|
+
last_size=$current_size
|
|
232
|
+
fi
|
|
233
|
+
|
|
234
|
+
sleep "$poll_interval"
|
|
235
|
+
done
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
# ─── Snapshot Mode ────────────────────────────────────────────────────────────
|
|
239
|
+
cmd_snapshot() {
|
|
240
|
+
[ ! -f "$EVENTS_FILE" ] && { error "No events yet"; exit 1; }
|
|
241
|
+
|
|
242
|
+
info "Current agent activity snapshot:"
|
|
243
|
+
echo ""
|
|
244
|
+
|
|
245
|
+
# Group by agent, show last event for each
|
|
246
|
+
local last_agent=""
|
|
247
|
+
local last_time=""
|
|
248
|
+
local last_event=""
|
|
249
|
+
|
|
250
|
+
tac "$EVENTS_FILE" 2>/dev/null | while IFS= read -r line; do
|
|
251
|
+
[ -z "$line" ] && continue
|
|
252
|
+
|
|
253
|
+
local agent stage ts
|
|
254
|
+
agent=$(echo "$line" | jq -r '.agent // "system"' 2>/dev/null || true)
|
|
255
|
+
stage=$(echo "$line" | jq -r '.stage // ""' 2>/dev/null || true)
|
|
256
|
+
ts=$(echo "$line" | jq -r '.ts // ""' 2>/dev/null || true)
|
|
257
|
+
|
|
258
|
+
# Skip if we've seen this agent already
|
|
259
|
+
if [ "$agent" = "$last_agent" ]; then
|
|
260
|
+
continue
|
|
261
|
+
fi
|
|
262
|
+
|
|
263
|
+
local msg
|
|
264
|
+
local type
|
|
265
|
+
type=$(echo "$line" | jq -r '.type // ""' 2>/dev/null || true)
|
|
266
|
+
msg=$(echo "$line" | jq -r '.type // "unknown"' 2>/dev/null || true)
|
|
267
|
+
|
|
268
|
+
printf "%s ${BOLD}%s${RESET}\n" \
|
|
269
|
+
"${DIM}${ts}${RESET}" \
|
|
270
|
+
"$(agent_color "$agent")${agent}${RESET}"
|
|
271
|
+
printf " └─ %s (${DIM}${type}${RESET})\n" "$msg"
|
|
272
|
+
echo ""
|
|
273
|
+
|
|
274
|
+
last_agent="$agent"
|
|
275
|
+
done
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
# ─── History Mode ────────────────────────────────────────────────────────────
|
|
279
|
+
cmd_history() {
|
|
280
|
+
local range="$1" # "1h", "10m", "all", or ISO timestamp
|
|
281
|
+
|
|
282
|
+
[ ! -f "$EVENTS_FILE" ] && { error "No events yet"; exit 1; }
|
|
283
|
+
|
|
284
|
+
local cutoff_epoch=0
|
|
285
|
+
|
|
286
|
+
# Parse time range
|
|
287
|
+
if [ "$range" = "all" ]; then
|
|
288
|
+
cutoff_epoch=0
|
|
289
|
+
elif echo "$range" | grep -qE '^[0-9]+[smhd]$'; then
|
|
290
|
+
local num="${range%[smhd]}"
|
|
291
|
+
local unit="${range##[0-9]}"
|
|
292
|
+
local seconds=0
|
|
293
|
+
case "$unit" in
|
|
294
|
+
s) seconds="$num" ;;
|
|
295
|
+
m) seconds=$((num * 60)) ;;
|
|
296
|
+
h) seconds=$((num * 3600)) ;;
|
|
297
|
+
d) seconds=$((num * 86400)) ;;
|
|
298
|
+
esac
|
|
299
|
+
cutoff_epoch=$(($(date +%s) - seconds))
|
|
300
|
+
else
|
|
301
|
+
# Assume ISO timestamp
|
|
302
|
+
cutoff_epoch=$(date -d "$range" +%s 2>/dev/null || date -j -f "%Y-%m-%dT%H:%M:%SZ" "$range" +%s 2>/dev/null || echo 0)
|
|
303
|
+
fi
|
|
304
|
+
|
|
305
|
+
info "Activity from last ${range}:"
|
|
306
|
+
echo ""
|
|
307
|
+
|
|
308
|
+
grep -v '^$' "$EVENTS_FILE" 2>/dev/null | while IFS= read -r line; do
|
|
309
|
+
[ -z "$line" ] && continue
|
|
310
|
+
|
|
311
|
+
local epoch agent ts type
|
|
312
|
+
epoch=$(echo "$line" | jq -r '.ts_epoch // 0' 2>/dev/null || true)
|
|
313
|
+
agent=$(echo "$line" | jq -r '.agent // "system"' 2>/dev/null || true)
|
|
314
|
+
ts=$(echo "$line" | jq -r '.ts // ""' 2>/dev/null || true)
|
|
315
|
+
type=$(echo "$line" | jq -r '.type // ""' 2>/dev/null || true)
|
|
316
|
+
|
|
317
|
+
if [ $epoch -lt $cutoff_epoch ]; then
|
|
318
|
+
continue
|
|
319
|
+
fi
|
|
320
|
+
|
|
321
|
+
local msg="$type"
|
|
322
|
+
case "$type" in
|
|
323
|
+
stage.started)
|
|
324
|
+
local stage
|
|
325
|
+
stage=$(echo "$line" | jq -r '.stage // ""' 2>/dev/null || true)
|
|
326
|
+
msg="Started ${stage} stage"
|
|
327
|
+
;;
|
|
328
|
+
pipeline.completed)
|
|
329
|
+
local result
|
|
330
|
+
result=$(echo "$line" | jq -r '.result // "?"' 2>/dev/null || true)
|
|
331
|
+
msg="Pipeline completed: ${result}"
|
|
332
|
+
;;
|
|
333
|
+
esac
|
|
334
|
+
|
|
335
|
+
format_event_line "$ts" "$type" "$agent" "$msg"
|
|
336
|
+
done
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
# ─── Statistics Mode ────────────────────────────────────────────────────────────
|
|
340
|
+
cmd_stats() {
|
|
341
|
+
[ ! -f "$EVENTS_FILE" ] && { error "No events yet"; exit 1; }
|
|
342
|
+
|
|
343
|
+
info "Activity statistics:"
|
|
344
|
+
echo ""
|
|
345
|
+
|
|
346
|
+
local total_events=0
|
|
347
|
+
local commits=0
|
|
348
|
+
local tests=0
|
|
349
|
+
local stages=0
|
|
350
|
+
local pipelines=0
|
|
351
|
+
local agents_seen=""
|
|
352
|
+
local start_time=""
|
|
353
|
+
local end_time=""
|
|
354
|
+
|
|
355
|
+
# Read directly to avoid subshell issues
|
|
356
|
+
echo "DEBUG: Starting read loop..." >&2
|
|
357
|
+
while IFS= read -r line; do
|
|
358
|
+
[ -z "$line" ] && continue
|
|
359
|
+
|
|
360
|
+
total_events=$((total_events + 1))
|
|
361
|
+
|
|
362
|
+
local type agent ts
|
|
363
|
+
type=$(echo "$line" | jq -r '.type // ""' 2>/dev/null || true)
|
|
364
|
+
agent=$(echo "$line" | jq -r '.agent // "system"' 2>/dev/null || true)
|
|
365
|
+
ts=$(echo "$line" | jq -r '.ts // ""' 2>/dev/null || true)
|
|
366
|
+
|
|
367
|
+
# Track unique agents
|
|
368
|
+
if [ -n "$agent" ] && [ "$agent" != "system" ]; then
|
|
369
|
+
agents_seen="${agents_seen}${agent}"$'\n'
|
|
370
|
+
fi
|
|
371
|
+
|
|
372
|
+
# Track event types
|
|
373
|
+
case "$type" in
|
|
374
|
+
commit) commits=$((commits + 1)) ;;
|
|
375
|
+
test.*) tests=$((tests + 1)) ;;
|
|
376
|
+
stage.*) stages=$((stages + 1)) ;;
|
|
377
|
+
pipeline.*) pipelines=$((pipelines + 1)) ;;
|
|
378
|
+
esac
|
|
379
|
+
|
|
380
|
+
# Track first and last event times
|
|
381
|
+
if [ -z "$start_time" ]; then
|
|
382
|
+
start_time="$ts"
|
|
383
|
+
fi
|
|
384
|
+
end_time="$ts"
|
|
385
|
+
done < <(grep -v '^$' "$EVENTS_FILE" 2>/dev/null)
|
|
386
|
+
|
|
387
|
+
echo "DEBUG: Read complete, total=$total_events" >&2
|
|
388
|
+
echo "DEBUG: agents_seen length: ${#agents_seen}" >&2
|
|
389
|
+
local unique_agents
|
|
390
|
+
echo "DEBUG: About to compute unique..." >&2
|
|
391
|
+
unique_agents=$(sort -u <<< "$agents_seen" | grep -v '^$' | wc -l | tr -d ' ')
|
|
392
|
+
echo "DEBUG: unique_agents=$unique_agents" >&2
|
|
393
|
+
|
|
394
|
+
echo "DEBUG: About to print results..." >&2
|
|
395
|
+
printf "${BOLD}Total Events:${RESET} %d\n" "$total_events"
|
|
396
|
+
printf "${BOLD}Commits:${RESET} %d\n" "$commits"
|
|
397
|
+
printf "${BOLD}Tests:${RESET} %d\n" "$tests"
|
|
398
|
+
printf "${BOLD}Stages:${RESET} %d\n" "$stages"
|
|
399
|
+
printf "${BOLD}Pipelines:${RESET} %d\n" "$pipelines"
|
|
400
|
+
printf "${BOLD}Unique Agents:${RESET} %d\n" "$unique_agents"
|
|
401
|
+
printf "${BOLD}Time Range:${RESET} ${DIM}%s${RESET} to ${DIM}%s${RESET}\n" "$start_time" "$end_time"
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
# ─── Agents Mode ────────────────────────────────────────────────────────────────
|
|
405
|
+
cmd_agents() {
|
|
406
|
+
[ ! -f "$EVENTS_FILE" ] && { error "No events yet"; exit 1; }
|
|
407
|
+
|
|
408
|
+
info "Known agents and last activity:"
|
|
409
|
+
echo ""
|
|
410
|
+
|
|
411
|
+
# Use tac to go backwards and capture unique agents
|
|
412
|
+
tac "$EVENTS_FILE" 2>/dev/null | while IFS= read -r line; do
|
|
413
|
+
[ -z "$line" ] && continue
|
|
414
|
+
|
|
415
|
+
local agent ts type
|
|
416
|
+
agent=$(echo "$line" | jq -r '.agent // "system"' 2>/dev/null || true)
|
|
417
|
+
ts=$(echo "$line" | jq -r '.ts // ""' 2>/dev/null || true)
|
|
418
|
+
type=$(echo "$line" | jq -r '.type // ""' 2>/dev/null || true)
|
|
419
|
+
|
|
420
|
+
if [ "$agent" = "system" ] || [ -z "$agent" ]; then
|
|
421
|
+
continue
|
|
422
|
+
fi
|
|
423
|
+
|
|
424
|
+
# Only print once per agent (first occurrence in reverse iteration)
|
|
425
|
+
local seen_file="${HOME}/.shipwright/activity-agents-seen"
|
|
426
|
+
mkdir -p "$(dirname "$seen_file")"
|
|
427
|
+
if ! grep -q "^${agent}$" "$seen_file" 2>/dev/null; then
|
|
428
|
+
printf "%s ${BOLD}%s${RESET} ${DIM}%s${RESET} ${BLUE}%s${RESET}\n" \
|
|
429
|
+
"$ts" \
|
|
430
|
+
"$(agent_color "$agent")${agent}${RESET}" \
|
|
431
|
+
"$type" \
|
|
432
|
+
""
|
|
433
|
+
echo "$agent" >> "$seen_file"
|
|
434
|
+
fi
|
|
435
|
+
done
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
# ─── Help ────────────────────────────────────────────────────────────────────
|
|
439
|
+
cmd_help() {
|
|
440
|
+
echo -e "${CYAN}${BOLD}shipwright activity${RESET} — Live agent activity stream"
|
|
441
|
+
echo ""
|
|
442
|
+
echo -e "${BOLD}USAGE${RESET}"
|
|
443
|
+
echo " shipwright activity [subcommand] [options]"
|
|
444
|
+
echo ""
|
|
445
|
+
echo -e "${BOLD}SUBCOMMANDS${RESET}"
|
|
446
|
+
echo " watch Live stream of agent activity (default)"
|
|
447
|
+
echo " snapshot Current state of all active agents"
|
|
448
|
+
echo " history [range] Replay past activity (e.g., '1h', '10m', 'all')"
|
|
449
|
+
echo " stats Running counters (events, commits, tests, agents)"
|
|
450
|
+
echo " agents List known agents and last activity"
|
|
451
|
+
echo " help Show this help message"
|
|
452
|
+
echo ""
|
|
453
|
+
echo -e "${BOLD}OPTIONS${RESET}"
|
|
454
|
+
echo " --type <type> Filter events by type (e.g., 'stage.completed')"
|
|
455
|
+
echo " --agent <name> Filter by agent name"
|
|
456
|
+
echo " --team <name> Filter by team"
|
|
457
|
+
echo " --stage <name> Filter by pipeline stage (e.g., 'build')"
|
|
458
|
+
echo ""
|
|
459
|
+
echo -e "${BOLD}EXAMPLES${RESET}"
|
|
460
|
+
echo " ${DIM}shipwright activity${RESET} # Live stream"
|
|
461
|
+
echo " ${DIM}shipwright activity watch --type stage.*${RESET} # Only stage events"
|
|
462
|
+
echo " ${DIM}shipwright activity history 1h${RESET} # Last hour"
|
|
463
|
+
echo " ${DIM}shipwright activity snapshot${RESET} # Current state"
|
|
464
|
+
echo " ${DIM}shipwright activity stats${RESET} # Counters"
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
# ─── Main ────────────────────────────────────────────────────────────────────────
|
|
468
|
+
main() {
|
|
469
|
+
local cmd="${1:-watch}"
|
|
470
|
+
shift 2>/dev/null || true
|
|
471
|
+
|
|
472
|
+
# Parse global options
|
|
473
|
+
while [ $# -gt 0 ]; do
|
|
474
|
+
case "$1" in
|
|
475
|
+
--type) FILTER_TYPE="$2"; shift 2 ;;
|
|
476
|
+
--agent) FILTER_AGENT="$2"; shift 2 ;;
|
|
477
|
+
--team) FILTER_TEAM="$2"; shift 2 ;;
|
|
478
|
+
--stage) FILTER_STAGE="$2"; shift 2 ;;
|
|
479
|
+
*) break ;;
|
|
480
|
+
esac
|
|
481
|
+
done
|
|
482
|
+
|
|
483
|
+
case "$cmd" in
|
|
484
|
+
watch) cmd_watch "$@" ;;
|
|
485
|
+
snapshot) cmd_snapshot ;;
|
|
486
|
+
history) cmd_history "${1:-1h}" ;;
|
|
487
|
+
stats) cmd_stats ;;
|
|
488
|
+
agents) cmd_agents ;;
|
|
489
|
+
help|-h|--help) cmd_help ;;
|
|
490
|
+
*)
|
|
491
|
+
error "Unknown command: $cmd"
|
|
492
|
+
cmd_help
|
|
493
|
+
exit 1
|
|
494
|
+
;;
|
|
495
|
+
esac
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
|
|
499
|
+
main "$@"
|
|
500
|
+
fi
|