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,444 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
# ║ shipwright scale — Dynamic agent team scaling during pipeline execution ║
|
|
4
|
+
# ║ Scale up/down, manage rules, track history, recommend actions ║
|
|
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
|
+
# ─── Constants ──────────────────────────────────────────────────────────────
|
|
34
|
+
SCALE_RULES_FILE="${HOME}/.shipwright/scale-rules.json"
|
|
35
|
+
SCALE_EVENTS_FILE="${HOME}/.shipwright/scale-events.jsonl"
|
|
36
|
+
SCALE_STATE_FILE="${HOME}/.shipwright/scale-state.json"
|
|
37
|
+
|
|
38
|
+
now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
|
|
39
|
+
now_unix() { date +%s; }
|
|
40
|
+
|
|
41
|
+
# ─── Ensure directories exist ──────────────────────────────────────────────
|
|
42
|
+
ensure_dirs() {
|
|
43
|
+
mkdir -p "$HOME/.shipwright"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# ─── Initialize rules file ────────────────────────────────────────────────
|
|
47
|
+
init_rules() {
|
|
48
|
+
if [[ ! -f "$SCALE_RULES_FILE" ]]; then
|
|
49
|
+
local tmp_file
|
|
50
|
+
tmp_file=$(mktemp)
|
|
51
|
+
cat > "$tmp_file" << 'JSON'
|
|
52
|
+
{
|
|
53
|
+
"iteration_threshold": 3,
|
|
54
|
+
"coverage_threshold": 60,
|
|
55
|
+
"module_threshold": 5,
|
|
56
|
+
"budget_check": true,
|
|
57
|
+
"cooldown_seconds": 120,
|
|
58
|
+
"max_team_size": 8,
|
|
59
|
+
"roles": ["builder", "reviewer", "tester", "security"]
|
|
60
|
+
}
|
|
61
|
+
JSON
|
|
62
|
+
mv "$tmp_file" "$SCALE_RULES_FILE"
|
|
63
|
+
success "Initialized scaling rules"
|
|
64
|
+
fi
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# ─── Get current unix timestamp ───────────────────────────────────────────
|
|
68
|
+
get_last_scale_time() {
|
|
69
|
+
if [[ -f "$SCALE_STATE_FILE" ]]; then
|
|
70
|
+
jq -r '.last_scale_time // 0' "$SCALE_STATE_FILE" 2>/dev/null || echo "0"
|
|
71
|
+
else
|
|
72
|
+
echo "0"
|
|
73
|
+
fi
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# ─── Check if we're within cooldown period ───────────────────────────────
|
|
77
|
+
in_cooldown() {
|
|
78
|
+
local cooldown
|
|
79
|
+
cooldown=$(jq -r '.cooldown_seconds // 120' "$SCALE_RULES_FILE" 2>/dev/null || echo "120")
|
|
80
|
+
local last_time
|
|
81
|
+
last_time=$(get_last_scale_time)
|
|
82
|
+
local now
|
|
83
|
+
now=$(now_unix)
|
|
84
|
+
local elapsed=$((now - last_time))
|
|
85
|
+
[[ $elapsed -lt $cooldown ]]
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# ─── Update scale state ───────────────────────────────────────────────────
|
|
89
|
+
update_scale_state() {
|
|
90
|
+
local tmp_file
|
|
91
|
+
tmp_file=$(mktemp)
|
|
92
|
+
|
|
93
|
+
if [[ -f "$SCALE_STATE_FILE" ]]; then
|
|
94
|
+
# Update existing state
|
|
95
|
+
jq --arg now "$(now_unix)" '.last_scale_time = ($now | tonumber)' "$SCALE_STATE_FILE" > "$tmp_file"
|
|
96
|
+
else
|
|
97
|
+
# Create new state
|
|
98
|
+
cat > "$tmp_file" << JSON
|
|
99
|
+
{
|
|
100
|
+
"last_scale_time": $(now_unix),
|
|
101
|
+
"team_size": 0,
|
|
102
|
+
"events_count": 0
|
|
103
|
+
}
|
|
104
|
+
JSON
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
mv "$tmp_file" "$SCALE_STATE_FILE"
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# ─── Emit scaling event ───────────────────────────────────────────────────
|
|
111
|
+
emit_scale_event() {
|
|
112
|
+
local action="$1" # up, down, auto
|
|
113
|
+
local role="$2" # builder, reviewer, tester, security
|
|
114
|
+
local reason="$3" # iteration_threshold, coverage_threshold, etc
|
|
115
|
+
local context="${4:-}" # additional context
|
|
116
|
+
|
|
117
|
+
ensure_dirs
|
|
118
|
+
|
|
119
|
+
local event
|
|
120
|
+
event=$(jq -c -n \
|
|
121
|
+
--arg ts "$(now_iso)" \
|
|
122
|
+
--arg action "$action" \
|
|
123
|
+
--arg role "$role" \
|
|
124
|
+
--arg reason "$reason" \
|
|
125
|
+
--arg context "$context" \
|
|
126
|
+
'{ts: $ts, action: $action, role: $role, reason: $reason, context: $context}')
|
|
127
|
+
|
|
128
|
+
echo "$event" >> "$SCALE_EVENTS_FILE"
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
# ─── Scale Up: spawn new agent ───────────────────────────────────────────
|
|
132
|
+
cmd_up() {
|
|
133
|
+
local role="${1:-builder}"
|
|
134
|
+
shift 2>/dev/null || true
|
|
135
|
+
|
|
136
|
+
ensure_dirs
|
|
137
|
+
init_rules
|
|
138
|
+
|
|
139
|
+
# Validate role
|
|
140
|
+
local valid_roles="builder reviewer tester security"
|
|
141
|
+
if ! echo "$valid_roles" | grep -q "$role"; then
|
|
142
|
+
error "Invalid role: $role. Valid roles: $valid_roles"
|
|
143
|
+
return 1
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
# Check cooldown
|
|
147
|
+
if in_cooldown; then
|
|
148
|
+
local cooldown
|
|
149
|
+
cooldown=$(jq -r '.cooldown_seconds // 120' "$SCALE_RULES_FILE")
|
|
150
|
+
warn "Scaling cooldown active. Wait ${cooldown}s before next scale event."
|
|
151
|
+
return 1
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
# Check max team size
|
|
155
|
+
local max_size
|
|
156
|
+
max_size=$(jq -r '.max_team_size // 8' "$SCALE_RULES_FILE")
|
|
157
|
+
|
|
158
|
+
info "Scaling up team with ${role} agent"
|
|
159
|
+
echo -e " Max team size: ${CYAN}${max_size}${RESET}"
|
|
160
|
+
echo -e " Role: ${CYAN}${role}${RESET}"
|
|
161
|
+
echo ""
|
|
162
|
+
|
|
163
|
+
# TODO: Integrate with tmux/SendMessage to spawn agent
|
|
164
|
+
# For now, emit event and log
|
|
165
|
+
emit_scale_event "up" "$role" "manual" "$*"
|
|
166
|
+
update_scale_state
|
|
167
|
+
|
|
168
|
+
success "Scale-up event recorded (role: ${role})"
|
|
169
|
+
echo ""
|
|
170
|
+
echo -e " ${DIM}Note: Actual agent spawn requires tmux/claude integration${RESET}"
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
# ─── Scale Down: send shutdown to agent ──────────────────────────────────
|
|
174
|
+
cmd_down() {
|
|
175
|
+
local agent_id="${1:-}"
|
|
176
|
+
shift 2>/dev/null || true
|
|
177
|
+
|
|
178
|
+
if [[ -z "$agent_id" ]]; then
|
|
179
|
+
error "Usage: shipwright scale down <agent-id>"
|
|
180
|
+
return 1
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
ensure_dirs
|
|
184
|
+
init_rules
|
|
185
|
+
|
|
186
|
+
info "Scaling down agent: ${agent_id}"
|
|
187
|
+
echo ""
|
|
188
|
+
|
|
189
|
+
# TODO: Integrate with SendMessage to shut down agent
|
|
190
|
+
emit_scale_event "down" "unknown" "manual" "agent_id=$agent_id"
|
|
191
|
+
update_scale_state
|
|
192
|
+
|
|
193
|
+
success "Scale-down event recorded (agent: ${agent_id})"
|
|
194
|
+
echo -e " ${DIM}Note: Agent shutdown requires SendMessage integration${RESET}"
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
# ─── Manage scaling rules ────────────────────────────────────────────────
|
|
198
|
+
cmd_rules() {
|
|
199
|
+
local subcmd="${1:-show}"
|
|
200
|
+
shift 2>/dev/null || true
|
|
201
|
+
|
|
202
|
+
ensure_dirs
|
|
203
|
+
init_rules
|
|
204
|
+
|
|
205
|
+
case "$subcmd" in
|
|
206
|
+
show)
|
|
207
|
+
info "Scaling Rules"
|
|
208
|
+
echo ""
|
|
209
|
+
cat "$SCALE_RULES_FILE" | jq '.' | sed 's/^/ /'
|
|
210
|
+
echo ""
|
|
211
|
+
;;
|
|
212
|
+
set)
|
|
213
|
+
local key="${1:-}"
|
|
214
|
+
local value="${2:-}"
|
|
215
|
+
|
|
216
|
+
if [[ -z "$key" || -z "$value" ]]; then
|
|
217
|
+
error "Usage: shipwright scale rules set <key> <value>"
|
|
218
|
+
return 1
|
|
219
|
+
fi
|
|
220
|
+
|
|
221
|
+
local tmp_file
|
|
222
|
+
tmp_file=$(mktemp)
|
|
223
|
+
|
|
224
|
+
jq --arg key "$key" --arg value "$value" \
|
|
225
|
+
'if ($value | test("^[0-9]+$")) then
|
|
226
|
+
.[$key] = ($value | tonumber)
|
|
227
|
+
else
|
|
228
|
+
.[$key] = $value
|
|
229
|
+
end' "$SCALE_RULES_FILE" > "$tmp_file"
|
|
230
|
+
|
|
231
|
+
mv "$tmp_file" "$SCALE_RULES_FILE"
|
|
232
|
+
success "Updated: ${key} = ${value}"
|
|
233
|
+
;;
|
|
234
|
+
reset)
|
|
235
|
+
rm -f "$SCALE_RULES_FILE"
|
|
236
|
+
init_rules
|
|
237
|
+
success "Rules reset to defaults"
|
|
238
|
+
;;
|
|
239
|
+
*)
|
|
240
|
+
error "Unknown subcommand: $subcmd"
|
|
241
|
+
echo -e " Valid: ${CYAN}show${RESET}, ${CYAN}set${RESET}, ${CYAN}reset${RESET}"
|
|
242
|
+
return 1
|
|
243
|
+
;;
|
|
244
|
+
esac
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
# ─── Show current status ──────────────────────────────────────────────────
|
|
248
|
+
cmd_status() {
|
|
249
|
+
ensure_dirs
|
|
250
|
+
init_rules
|
|
251
|
+
|
|
252
|
+
local team_size=0
|
|
253
|
+
local event_count=0
|
|
254
|
+
|
|
255
|
+
if [[ -f "$SCALE_STATE_FILE" ]]; then
|
|
256
|
+
team_size=$(jq -r '.team_size // 0' "$SCALE_STATE_FILE" 2>/dev/null || echo "0")
|
|
257
|
+
fi
|
|
258
|
+
|
|
259
|
+
if [[ -f "$SCALE_EVENTS_FILE" ]]; then
|
|
260
|
+
event_count=$(wc -l < "$SCALE_EVENTS_FILE" || echo "0")
|
|
261
|
+
fi
|
|
262
|
+
|
|
263
|
+
local last_scale_time
|
|
264
|
+
last_scale_time=$(get_last_scale_time)
|
|
265
|
+
|
|
266
|
+
info "Scaling Status"
|
|
267
|
+
echo ""
|
|
268
|
+
echo -e " Team size: ${CYAN}${team_size}${RESET}"
|
|
269
|
+
echo -e " Scale events: ${CYAN}${event_count}${RESET}"
|
|
270
|
+
echo -e " Last scale: ${CYAN}$(date -u -d @"$last_scale_time" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo "never")${RESET}"
|
|
271
|
+
|
|
272
|
+
local max_size
|
|
273
|
+
max_size=$(jq -r '.max_team_size // 8' "$SCALE_RULES_FILE")
|
|
274
|
+
echo -e " Max team size: ${CYAN}${max_size}${RESET}"
|
|
275
|
+
echo ""
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
# ─── Show scaling history ────────────────────────────────────────────────
|
|
279
|
+
cmd_history() {
|
|
280
|
+
local limit="${1:-20}"
|
|
281
|
+
shift 2>/dev/null || true
|
|
282
|
+
|
|
283
|
+
ensure_dirs
|
|
284
|
+
|
|
285
|
+
if [[ ! -f "$SCALE_EVENTS_FILE" ]]; then
|
|
286
|
+
warn "No scaling events recorded"
|
|
287
|
+
return 0
|
|
288
|
+
fi
|
|
289
|
+
|
|
290
|
+
info "Scaling History (last ${limit} events)"
|
|
291
|
+
echo ""
|
|
292
|
+
|
|
293
|
+
tail -n "$limit" "$SCALE_EVENTS_FILE" | while IFS= read -r line; do
|
|
294
|
+
local ts action role reason
|
|
295
|
+
ts=$(echo "$line" | jq -r '.ts // "unknown"' 2>/dev/null || echo "unknown")
|
|
296
|
+
action=$(echo "$line" | jq -r '.action // "unknown"' 2>/dev/null || echo "unknown")
|
|
297
|
+
role=$(echo "$line" | jq -r '.role // "unknown"' 2>/dev/null || echo "unknown")
|
|
298
|
+
reason=$(echo "$line" | jq -r '.reason // "unknown"' 2>/dev/null || echo "unknown")
|
|
299
|
+
printf " %s ${CYAN}%-6s${RESET} %-10s %s\n" "$ts" "$action" "$role" "$reason"
|
|
300
|
+
done
|
|
301
|
+
|
|
302
|
+
echo ""
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
# ─── Recommend scaling actions ───────────────────────────────────────────
|
|
306
|
+
cmd_recommend() {
|
|
307
|
+
ensure_dirs
|
|
308
|
+
init_rules
|
|
309
|
+
|
|
310
|
+
local iteration_threshold
|
|
311
|
+
iteration_threshold=$(jq -r '.iteration_threshold // 3' "$SCALE_RULES_FILE")
|
|
312
|
+
|
|
313
|
+
local coverage_threshold
|
|
314
|
+
coverage_threshold=$(jq -r '.coverage_threshold // 60' "$SCALE_RULES_FILE")
|
|
315
|
+
|
|
316
|
+
local module_threshold
|
|
317
|
+
module_threshold=$(jq -r '.module_threshold // 5' "$SCALE_RULES_FILE")
|
|
318
|
+
|
|
319
|
+
info "Scaling Recommendations"
|
|
320
|
+
echo ""
|
|
321
|
+
echo -e " Thresholds:"
|
|
322
|
+
echo -e " Failed iterations: ${CYAN}${iteration_threshold}${RESET} (add tester on failure)"
|
|
323
|
+
echo -e " Test coverage: ${CYAN}${coverage_threshold}%${RESET} (add tester below this)"
|
|
324
|
+
echo -e " Modules changed: ${CYAN}${module_threshold}${RESET} (add reviewer above this)"
|
|
325
|
+
echo ""
|
|
326
|
+
|
|
327
|
+
# TODO: Parse pipeline context to generate actual recommendations
|
|
328
|
+
echo -e " ${DIM}Recommendations require active pipeline context (passed via environment)${RESET}"
|
|
329
|
+
echo ""
|
|
330
|
+
|
|
331
|
+
# Example output when context is available:
|
|
332
|
+
# echo -e " ${YELLOW}⚠${RESET} Failed 4 iterations (threshold: 3)"
|
|
333
|
+
# echo -e " ${CYAN}→ Recommend adding: tester${RESET}"
|
|
334
|
+
# echo ""
|
|
335
|
+
# echo -e " ${YELLOW}⚠${RESET} Coverage at 45% (threshold: 60%)"
|
|
336
|
+
# echo -e " ${CYAN}→ Recommend adding: tester${RESET}"
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
# ─── Help message ────────────────────────────────────────────────────────
|
|
340
|
+
cmd_help() {
|
|
341
|
+
cat << 'EOF'
|
|
342
|
+
shipwright scale — Dynamic agent team scaling during pipeline execution
|
|
343
|
+
|
|
344
|
+
USAGE
|
|
345
|
+
shipwright scale <command> [options]
|
|
346
|
+
|
|
347
|
+
COMMANDS
|
|
348
|
+
up [role] Spawn new agent with specific role (builder/reviewer/tester/security)
|
|
349
|
+
down <agent-id> Gracefully shutdown an agent (waits for task completion)
|
|
350
|
+
rules Manage scaling rules (iteration_threshold, coverage_threshold, etc)
|
|
351
|
+
status Show current team size, scaling history, budget impact
|
|
352
|
+
history [N] Show last N scaling events (default: 20)
|
|
353
|
+
recommend Analyze pipeline state and suggest scaling actions
|
|
354
|
+
help Show this help message
|
|
355
|
+
|
|
356
|
+
RULES SUBCOMMANDS
|
|
357
|
+
rules show Display current scaling rules
|
|
358
|
+
rules set <k> <v> Update a rule (e.g., rules set max_team_size 10)
|
|
359
|
+
rules reset Reset to default rules
|
|
360
|
+
|
|
361
|
+
OPTIONS
|
|
362
|
+
--cooldown <secs> Minimum seconds between scale events (default: 120)
|
|
363
|
+
--max-size <n> Maximum team size (default: 8)
|
|
364
|
+
|
|
365
|
+
RULES (stored in ~/.shipwright/scale-rules.json)
|
|
366
|
+
iteration_threshold Scale up after N failed iterations (default: 3)
|
|
367
|
+
coverage_threshold Add tester when coverage < N% (default: 60)
|
|
368
|
+
module_threshold Split builders when touching > N modules (default: 5)
|
|
369
|
+
budget_check Factor in remaining budget before scaling (default: true)
|
|
370
|
+
cooldown_seconds Minimum time between scale events (default: 120)
|
|
371
|
+
max_team_size Maximum agents per team (default: 8)
|
|
372
|
+
|
|
373
|
+
EXAMPLES
|
|
374
|
+
# Add a tester to the current team
|
|
375
|
+
shipwright scale up tester
|
|
376
|
+
|
|
377
|
+
# Remove an agent gracefully
|
|
378
|
+
shipwright scale down agent-42
|
|
379
|
+
|
|
380
|
+
# View current scaling rules
|
|
381
|
+
shipwright scale rules show
|
|
382
|
+
|
|
383
|
+
# Update max team size
|
|
384
|
+
shipwright scale rules set max_team_size 10
|
|
385
|
+
|
|
386
|
+
# Show recent scaling events
|
|
387
|
+
shipwright scale history
|
|
388
|
+
|
|
389
|
+
# Get scaling recommendations for current pipeline
|
|
390
|
+
shipwright scale recommend --issue 46
|
|
391
|
+
|
|
392
|
+
INTEGRATION
|
|
393
|
+
Scaling events are recorded in ~/.shipwright/scale-events.jsonl
|
|
394
|
+
State is persisted in ~/.shipwright/scale-state.json
|
|
395
|
+
Rules are stored in ~/.shipwright/scale-rules.json
|
|
396
|
+
|
|
397
|
+
Actual agent spawning/shutdown integrates with:
|
|
398
|
+
- tmux (for new pane creation)
|
|
399
|
+
- SendMessage tool (for agent communication)
|
|
400
|
+
- Pipeline context (from sw-pipeline.sh environment)
|
|
401
|
+
|
|
402
|
+
EOF
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
# ─── Main router ──────────────────────────────────────────────────────────
|
|
406
|
+
main() {
|
|
407
|
+
local cmd="${1:-help}"
|
|
408
|
+
shift 2>/dev/null || true
|
|
409
|
+
|
|
410
|
+
case "$cmd" in
|
|
411
|
+
up)
|
|
412
|
+
cmd_up "$@"
|
|
413
|
+
;;
|
|
414
|
+
down)
|
|
415
|
+
cmd_down "$@"
|
|
416
|
+
;;
|
|
417
|
+
rules)
|
|
418
|
+
cmd_rules "$@"
|
|
419
|
+
;;
|
|
420
|
+
status)
|
|
421
|
+
cmd_status "$@"
|
|
422
|
+
;;
|
|
423
|
+
history)
|
|
424
|
+
cmd_history "$@"
|
|
425
|
+
;;
|
|
426
|
+
recommend)
|
|
427
|
+
cmd_recommend "$@"
|
|
428
|
+
;;
|
|
429
|
+
help|--help|-h)
|
|
430
|
+
cmd_help
|
|
431
|
+
;;
|
|
432
|
+
*)
|
|
433
|
+
error "Unknown command: $cmd"
|
|
434
|
+
echo ""
|
|
435
|
+
cmd_help
|
|
436
|
+
exit 1
|
|
437
|
+
;;
|
|
438
|
+
esac
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
# Only run main if this script is executed directly (not sourced)
|
|
442
|
+
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
|
443
|
+
main "$@"
|
|
444
|
+
fi
|