shipwright-cli 3.0.0 → 3.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.
- package/README.md +21 -7
- package/completions/_shipwright +247 -93
- package/completions/shipwright.bash +69 -15
- package/completions/shipwright.fish +309 -41
- package/config/decision-tiers.json +55 -0
- package/config/defaults.json +25 -2
- package/config/event-schema.json +142 -5
- package/config/policy.json +8 -0
- package/dashboard/public/index.html +6 -0
- package/dashboard/public/styles.css +76 -0
- package/dashboard/server.ts +51 -0
- package/dashboard/src/core/api.ts +5 -0
- package/dashboard/src/types/api.ts +10 -0
- package/dashboard/src/views/metrics.ts +69 -1
- package/package.json +3 -3
- package/scripts/lib/architecture.sh +2 -1
- package/scripts/lib/bootstrap.sh +0 -0
- package/scripts/lib/config.sh +0 -0
- package/scripts/lib/daemon-adaptive.sh +4 -2
- package/scripts/lib/daemon-dispatch.sh +24 -1
- package/scripts/lib/daemon-failure.sh +0 -0
- package/scripts/lib/daemon-health.sh +0 -0
- package/scripts/lib/daemon-patrol.sh +42 -7
- package/scripts/lib/daemon-poll.sh +17 -0
- package/scripts/lib/daemon-state.sh +17 -0
- package/scripts/lib/daemon-triage.sh +1 -1
- package/scripts/lib/decide-autonomy.sh +295 -0
- package/scripts/lib/decide-scoring.sh +228 -0
- package/scripts/lib/decide-signals.sh +462 -0
- package/scripts/lib/fleet-failover.sh +0 -0
- package/scripts/lib/helpers.sh +19 -18
- package/scripts/lib/pipeline-detection.sh +1 -1
- package/scripts/lib/pipeline-github.sh +0 -0
- package/scripts/lib/pipeline-intelligence.sh +23 -4
- package/scripts/lib/pipeline-quality-checks.sh +11 -6
- package/scripts/lib/pipeline-quality.sh +0 -0
- package/scripts/lib/pipeline-stages.sh +330 -33
- package/scripts/lib/pipeline-state.sh +14 -0
- package/scripts/lib/policy.sh +0 -0
- package/scripts/lib/test-helpers.sh +0 -0
- package/scripts/postinstall.mjs +75 -1
- package/scripts/signals/example-collector.sh +36 -0
- package/scripts/sw +8 -4
- package/scripts/sw-activity.sh +1 -7
- package/scripts/sw-adaptive.sh +7 -7
- package/scripts/sw-adversarial.sh +1 -1
- package/scripts/sw-architecture-enforcer.sh +1 -1
- package/scripts/sw-auth.sh +1 -1
- package/scripts/sw-autonomous.sh +1 -1
- package/scripts/sw-changelog.sh +1 -1
- package/scripts/sw-checkpoint.sh +1 -1
- package/scripts/sw-ci.sh +11 -6
- package/scripts/sw-cleanup.sh +1 -1
- package/scripts/sw-code-review.sh +36 -17
- package/scripts/sw-connect.sh +1 -1
- package/scripts/sw-context.sh +1 -1
- package/scripts/sw-cost.sh +71 -5
- package/scripts/sw-daemon.sh +6 -3
- package/scripts/sw-dashboard.sh +1 -1
- package/scripts/sw-db.sh +53 -38
- package/scripts/sw-decide.sh +685 -0
- package/scripts/sw-decompose.sh +1 -1
- package/scripts/sw-deps.sh +1 -1
- package/scripts/sw-developer-simulation.sh +1 -1
- package/scripts/sw-discovery.sh +80 -4
- package/scripts/sw-doc-fleet.sh +1 -1
- package/scripts/sw-docs-agent.sh +1 -1
- package/scripts/sw-docs.sh +1 -1
- package/scripts/sw-doctor.sh +1 -1
- package/scripts/sw-dora.sh +1 -1
- package/scripts/sw-durable.sh +9 -5
- package/scripts/sw-e2e-orchestrator.sh +1 -1
- package/scripts/sw-eventbus.sh +7 -4
- package/scripts/sw-evidence.sh +1 -1
- package/scripts/sw-feedback.sh +1 -1
- package/scripts/sw-fix.sh +1 -1
- package/scripts/sw-fleet-discover.sh +1 -1
- package/scripts/sw-fleet-viz.sh +6 -4
- package/scripts/sw-fleet.sh +1 -1
- package/scripts/sw-github-app.sh +3 -2
- 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 +1 -1
- package/scripts/sw-heartbeat.sh +1 -1
- package/scripts/sw-hygiene.sh +5 -3
- package/scripts/sw-incident.sh +9 -5
- package/scripts/sw-init.sh +1 -1
- package/scripts/sw-instrument.sh +1 -1
- package/scripts/sw-intelligence.sh +11 -6
- package/scripts/sw-jira.sh +1 -1
- package/scripts/sw-launchd.sh +1 -1
- package/scripts/sw-linear.sh +1 -1
- package/scripts/sw-logs.sh +1 -1
- package/scripts/sw-loop.sh +338 -32
- package/scripts/sw-memory.sh +23 -6
- package/scripts/sw-mission-control.sh +1 -1
- package/scripts/sw-model-router.sh +3 -2
- package/scripts/sw-otel.sh +8 -4
- package/scripts/sw-oversight.sh +1 -1
- package/scripts/sw-pipeline-composer.sh +3 -1
- package/scripts/sw-pipeline-vitals.sh +11 -6
- package/scripts/sw-pipeline.sh +92 -8
- package/scripts/sw-pm.sh +5 -4
- package/scripts/sw-pr-lifecycle.sh +7 -4
- package/scripts/sw-predictive.sh +11 -5
- package/scripts/sw-prep.sh +1 -1
- package/scripts/sw-ps.sh +1 -1
- package/scripts/sw-public-dashboard.sh +3 -2
- package/scripts/sw-quality.sh +21 -10
- package/scripts/sw-reaper.sh +1 -1
- package/scripts/sw-recruit.sh +1 -1
- package/scripts/sw-regression.sh +1 -1
- package/scripts/sw-release-manager.sh +1 -1
- package/scripts/sw-release.sh +1 -1
- package/scripts/sw-remote.sh +1 -1
- package/scripts/sw-replay.sh +1 -1
- package/scripts/sw-retro.sh +1 -1
- package/scripts/sw-review-rerun.sh +1 -1
- package/scripts/sw-scale.sh +69 -11
- package/scripts/sw-security-audit.sh +1 -1
- package/scripts/sw-self-optimize.sh +168 -4
- package/scripts/sw-session.sh +3 -3
- package/scripts/sw-setup.sh +1 -1
- package/scripts/sw-standup.sh +1 -1
- package/scripts/sw-status.sh +1 -1
- package/scripts/sw-strategic.sh +11 -6
- package/scripts/sw-stream.sh +7 -4
- package/scripts/sw-swarm.sh +3 -2
- package/scripts/sw-team-stages.sh +1 -1
- package/scripts/sw-templates.sh +3 -3
- package/scripts/sw-testgen.sh +11 -6
- package/scripts/sw-tmux-pipeline.sh +1 -1
- package/scripts/sw-tmux.sh +35 -1
- package/scripts/sw-trace.sh +1 -1
- package/scripts/sw-tracker.sh +1 -1
- package/scripts/sw-triage.sh +7 -7
- package/scripts/sw-upgrade.sh +1 -1
- package/scripts/sw-ux.sh +1 -1
- package/scripts/sw-webhook.sh +3 -2
- package/scripts/sw-widgets.sh +7 -4
- package/scripts/sw-worktree.sh +1 -1
- package/scripts/update-homebrew-sha.sh +21 -15
package/scripts/sw-quality.sh
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
9
|
-
VERSION="3.
|
|
9
|
+
VERSION="3.2.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
12
|
|
|
@@ -16,6 +16,8 @@ REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
16
16
|
# Canonical helpers (colors, output, events)
|
|
17
17
|
# shellcheck source=lib/helpers.sh
|
|
18
18
|
[[ -f "$SCRIPT_DIR/lib/helpers.sh" ]] && source "$SCRIPT_DIR/lib/helpers.sh"
|
|
19
|
+
# shellcheck source=lib/config.sh
|
|
20
|
+
[[ -f "$SCRIPT_DIR/lib/config.sh" ]] && source "$SCRIPT_DIR/lib/config.sh"
|
|
19
21
|
# Fallbacks when helpers not loaded (e.g. test env with overridden SCRIPT_DIR)
|
|
20
22
|
[[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
|
|
21
23
|
[[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
|
|
@@ -81,7 +83,8 @@ validate_quality() {
|
|
|
81
83
|
local uncommitted_pass=true
|
|
82
84
|
if [[ -d "$REPO_DIR/.git" ]]; then
|
|
83
85
|
local dirty_count
|
|
84
|
-
dirty_count=$(cd "$REPO_DIR" && git status --short 2>/dev/null | wc -l ||
|
|
86
|
+
dirty_count=$(cd "$REPO_DIR" && git status --short 2>/dev/null | wc -l || true)
|
|
87
|
+
dirty_count="${dirty_count:-0}"
|
|
85
88
|
if [[ "$dirty_count" -gt 0 ]]; then
|
|
86
89
|
uncommitted_pass=false
|
|
87
90
|
all_pass=false
|
|
@@ -93,7 +96,8 @@ validate_quality() {
|
|
|
93
96
|
local todos_pass=true
|
|
94
97
|
if [[ -d "$REPO_DIR/.git" ]]; then
|
|
95
98
|
local todo_count
|
|
96
|
-
todo_count=$(cd "$REPO_DIR" && git diff --cached 2>/dev/null | grep -cE '^\+.*(TODO|FIXME)' ||
|
|
99
|
+
todo_count=$(cd "$REPO_DIR" && git diff --cached 2>/dev/null | grep -cE '^\+.*(TODO|FIXME)' || true)
|
|
100
|
+
todo_count="${todo_count:-0}"
|
|
97
101
|
if [[ "$todo_count" -gt 0 ]]; then
|
|
98
102
|
todos_pass=false
|
|
99
103
|
all_pass=false
|
|
@@ -101,13 +105,16 @@ validate_quality() {
|
|
|
101
105
|
json_output=$(echo "$json_output" | jq --arg tp "$todos_pass" '.checks.todos=$tp' 2>/dev/null || true)
|
|
102
106
|
fi
|
|
103
107
|
|
|
104
|
-
# Check 5:
|
|
108
|
+
# Check 5: Secrets patterns in diff
|
|
105
109
|
local secrets_pass=true
|
|
106
110
|
local secret_patterns="(password|secret|token|api[_-]?key|aws_access|private_key)"
|
|
111
|
+
local secret_threshold
|
|
112
|
+
secret_threshold=$(_config_get_int "quality.secret_threshold" 3 2>/dev/null || echo 3)
|
|
107
113
|
if [[ -d "$REPO_DIR/.git" ]]; then
|
|
108
114
|
local secret_count
|
|
109
|
-
secret_count=$(cd "$REPO_DIR" && git diff --cached 2>/dev/null | grep -ciE "$secret_patterns" ||
|
|
110
|
-
|
|
115
|
+
secret_count=$(cd "$REPO_DIR" && git diff --cached 2>/dev/null | grep -ciE "$secret_patterns" || true)
|
|
116
|
+
secret_count="${secret_count:-0}"
|
|
117
|
+
if [[ "$secret_count" -gt "$secret_threshold" ]]; then
|
|
111
118
|
secrets_pass=false
|
|
112
119
|
all_pass=false
|
|
113
120
|
fi
|
|
@@ -322,7 +329,8 @@ completion_detection() {
|
|
|
322
329
|
# Check diminishing returns: < 10 lines changed in last 3 iterations
|
|
323
330
|
local recent_changes=0
|
|
324
331
|
if [[ -f "$ARTIFACTS_DIR/progress.md" ]]; then
|
|
325
|
-
recent_changes=$(grep -c "^### Iteration" "$ARTIFACTS_DIR/progress.md" ||
|
|
332
|
+
recent_changes=$(grep -c "^### Iteration" "$ARTIFACTS_DIR/progress.md" || true)
|
|
333
|
+
recent_changes="${recent_changes:-0}"
|
|
326
334
|
fi
|
|
327
335
|
|
|
328
336
|
# Check if tests went from failing to passing
|
|
@@ -339,7 +347,8 @@ completion_detection() {
|
|
|
339
347
|
local subtasks_done=true
|
|
340
348
|
if [[ -f ".claude/goal.md" ]]; then
|
|
341
349
|
local unchecked_count
|
|
342
|
-
unchecked_count=$(grep -c "^- \[ \]" ".claude/goal.md" 2>/dev/null ||
|
|
350
|
+
unchecked_count=$(grep -c "^- \[ \]" ".claude/goal.md" 2>/dev/null || true)
|
|
351
|
+
unchecked_count="${unchecked_count:-0}"
|
|
343
352
|
if [[ "$unchecked_count" -gt 0 ]]; then
|
|
344
353
|
subtasks_done=false
|
|
345
354
|
fi
|
|
@@ -399,14 +408,16 @@ calculate_quality_score() {
|
|
|
399
408
|
# Security audit (20%)
|
|
400
409
|
local security_files=0
|
|
401
410
|
if [[ -d "$REPO_DIR" ]]; then
|
|
402
|
-
security_files=$(find "$REPO_DIR" -type f \( -name "*.js" -o -name "*.py" -o -name "*.go" \) 2>/dev/null | wc -l ||
|
|
411
|
+
security_files=$(find "$REPO_DIR" -type f \( -name "*.js" -o -name "*.py" -o -name "*.go" \) 2>/dev/null | wc -l || true)
|
|
412
|
+
security_files="${security_files:-0}"
|
|
403
413
|
security_score=$((security_files > 0 ? 85 : 0))
|
|
404
414
|
fi
|
|
405
415
|
|
|
406
416
|
# Architecture audit (15%)
|
|
407
417
|
local architecture_files=0
|
|
408
418
|
if [[ -d "$REPO_DIR" ]]; then
|
|
409
|
-
architecture_files=$(find "$REPO_DIR" -type f \( -name "*.js" -o -name "*.py" \) 2>/dev/null | wc -l ||
|
|
419
|
+
architecture_files=$(find "$REPO_DIR" -type f \( -name "*.js" -o -name "*.py" \) 2>/dev/null | wc -l || true)
|
|
420
|
+
architecture_files="${architecture_files:-0}"
|
|
410
421
|
architecture_score=$((architecture_files > 0 ? 80 : 0))
|
|
411
422
|
fi
|
|
412
423
|
|
package/scripts/sw-reaper.sh
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
# ║ shipwright reaper --watch Continuous loop (default: 5s) ║
|
|
12
12
|
# ║ shipwright reaper --dry-run Preview what would be reaped ║
|
|
13
13
|
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
14
|
-
VERSION="3.
|
|
14
|
+
VERSION="3.2.0"
|
|
15
15
|
set -euo pipefail
|
|
16
16
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
17
17
|
|
package/scripts/sw-recruit.sh
CHANGED
|
@@ -2026,7 +2026,7 @@ cmd_evaluate() {
|
|
|
2026
2026
|
echo " Tasks Completed: $(echo "$profile" | jq -r '.tasks_completed // "0"')"
|
|
2027
2027
|
echo ""
|
|
2028
2028
|
|
|
2029
|
-
# Use population-aware thresholds
|
|
2029
|
+
# Use population-aware thresholds for performance evaluation
|
|
2030
2030
|
local pop_stats
|
|
2031
2031
|
pop_stats=$(_recruit_compute_population_stats)
|
|
2032
2032
|
local mean_success
|
package/scripts/sw-regression.sh
CHANGED
package/scripts/sw-release.sh
CHANGED
|
@@ -7,7 +7,7 @@ set -euo pipefail
|
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
trap 'rm -f "${tmp_file:-}" "${tmp_changelog:-}"' EXIT
|
|
9
9
|
|
|
10
|
-
VERSION="3.
|
|
10
|
+
VERSION="3.2.0"
|
|
11
11
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
12
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
13
13
|
|
package/scripts/sw-remote.sh
CHANGED
package/scripts/sw-replay.sh
CHANGED
package/scripts/sw-retro.sh
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
9
|
-
VERSION="3.
|
|
9
|
+
VERSION="3.2.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
|
|
12
12
|
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
package/scripts/sw-scale.sh
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
9
|
-
VERSION="3.
|
|
9
|
+
VERSION="3.2.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
|
|
12
12
|
# ─── Dependency check ─────────────────────────────────────────────────────────
|
|
@@ -357,7 +357,8 @@ cmd_status() {
|
|
|
357
357
|
fi
|
|
358
358
|
|
|
359
359
|
if [[ -f "$SCALE_EVENTS_FILE" ]]; then
|
|
360
|
-
event_count=$(wc -l < "$SCALE_EVENTS_FILE" ||
|
|
360
|
+
event_count=$(wc -l < "$SCALE_EVENTS_FILE" || true)
|
|
361
|
+
event_count="${event_count:-0}"
|
|
361
362
|
fi
|
|
362
363
|
|
|
363
364
|
local last_scale_time
|
|
@@ -424,16 +425,73 @@ cmd_recommend() {
|
|
|
424
425
|
echo -e " Modules changed: ${CYAN}${module_threshold}${RESET} (add reviewer above this)"
|
|
425
426
|
echo ""
|
|
426
427
|
|
|
427
|
-
#
|
|
428
|
-
|
|
429
|
-
|
|
428
|
+
# Read pipeline context from env or pipeline-state.md
|
|
429
|
+
local actual_iterations="${ACTUAL_ITERATIONS:-0}"
|
|
430
|
+
local test_coverage="${TEST_COVERAGE:-0}"
|
|
431
|
+
local module_count="${MODULE_COUNT:-0}"
|
|
432
|
+
|
|
433
|
+
# Try to extract from pipeline-state.md if env vars not set
|
|
434
|
+
local state_file=".claude/pipeline-state.md"
|
|
435
|
+
if [[ "$actual_iterations" == "0" && -f "$state_file" ]]; then
|
|
436
|
+
actual_iterations=$(grep -oE 'iterations?[: ]+([0-9]+)' "$state_file" 2>/dev/null | grep -oE '[0-9]+' | tail -1 || echo "0")
|
|
437
|
+
actual_iterations="${actual_iterations:-0}"
|
|
438
|
+
fi
|
|
439
|
+
if [[ "$test_coverage" == "0" && -f "$state_file" ]]; then
|
|
440
|
+
test_coverage=$(grep -oE 'coverage[: ]+([0-9]+)' "$state_file" 2>/dev/null | grep -oE '[0-9]+' | tail -1 || echo "0")
|
|
441
|
+
test_coverage="${test_coverage:-0}"
|
|
442
|
+
fi
|
|
443
|
+
|
|
444
|
+
# Count changed modules via git diff
|
|
445
|
+
if [[ "$module_count" == "0" ]] && command -v git >/dev/null 2>&1; then
|
|
446
|
+
local base_branch="${BASE_BRANCH:-main}"
|
|
447
|
+
if git rev-parse --verify "$base_branch" >/dev/null 2>&1; then
|
|
448
|
+
module_count=$(git diff --name-only "${base_branch}..HEAD" 2>/dev/null \
|
|
449
|
+
| sed 's|/[^/]*$||' | sort -u | wc -l || true)
|
|
450
|
+
module_count="${module_count:-0}"
|
|
451
|
+
fi
|
|
452
|
+
module_count="${module_count:-0}"
|
|
453
|
+
fi
|
|
454
|
+
|
|
455
|
+
local has_recommendations=false
|
|
456
|
+
|
|
457
|
+
# Check iterations against threshold
|
|
458
|
+
if [[ "$actual_iterations" -gt 0 && "$actual_iterations" -ge "$iteration_threshold" ]]; then
|
|
459
|
+
echo -e " ${YELLOW}⚠${RESET} Failed ${actual_iterations} iterations (threshold: ${iteration_threshold})"
|
|
460
|
+
echo -e " ${CYAN}→ Recommend adding: tester${RESET}"
|
|
461
|
+
echo ""
|
|
462
|
+
has_recommendations=true
|
|
463
|
+
fi
|
|
464
|
+
|
|
465
|
+
# Check coverage against threshold
|
|
466
|
+
if [[ "$test_coverage" -gt 0 && "$test_coverage" -lt "$coverage_threshold" ]]; then
|
|
467
|
+
echo -e " ${YELLOW}⚠${RESET} Coverage at ${test_coverage}% (threshold: ${coverage_threshold}%)"
|
|
468
|
+
echo -e " ${CYAN}→ Recommend adding: tester${RESET}"
|
|
469
|
+
echo ""
|
|
470
|
+
has_recommendations=true
|
|
471
|
+
fi
|
|
472
|
+
|
|
473
|
+
# Check module count against threshold
|
|
474
|
+
if [[ "$module_count" -gt 0 && "$module_count" -ge "$module_threshold" ]]; then
|
|
475
|
+
echo -e " ${YELLOW}⚠${RESET} ${module_count} modules changed (threshold: ${module_threshold})"
|
|
476
|
+
echo -e " ${CYAN}→ Recommend adding: reviewer${RESET}"
|
|
477
|
+
echo ""
|
|
478
|
+
has_recommendations=true
|
|
479
|
+
fi
|
|
480
|
+
|
|
481
|
+
if [[ "$has_recommendations" == "false" ]]; then
|
|
482
|
+
if [[ "$actual_iterations" == "0" && "$test_coverage" == "0" && "$module_count" == "0" ]]; then
|
|
483
|
+
echo -e " ${DIM}No pipeline context available — run during an active pipeline or set ACTUAL_ITERATIONS, TEST_COVERAGE, MODULE_COUNT${RESET}"
|
|
484
|
+
else
|
|
485
|
+
success "All metrics within thresholds — no scaling changes needed"
|
|
486
|
+
fi
|
|
487
|
+
echo ""
|
|
488
|
+
fi
|
|
430
489
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
# echo -e " ${CYAN}→ Recommend adding: tester${RESET}"
|
|
490
|
+
emit_event "scale.recommendation" \
|
|
491
|
+
"iterations=$actual_iterations" \
|
|
492
|
+
"coverage=$test_coverage" \
|
|
493
|
+
"modules=$module_count" \
|
|
494
|
+
"has_recommendations=$has_recommendations"
|
|
437
495
|
}
|
|
438
496
|
|
|
439
497
|
# ─── Help message ────────────────────────────────────────────────────────
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
9
|
-
VERSION="3.
|
|
9
|
+
VERSION="3.2.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
12
|
|
|
@@ -1181,6 +1181,162 @@ optimize_evolve_memory() {
|
|
|
1181
1181
|
success "Memory evolved: pruned=$pruned, strengthened=$strengthened, promoted=$promoted"
|
|
1182
1182
|
}
|
|
1183
1183
|
|
|
1184
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
1185
|
+
# CONTEXT EFFICIENCY CLOSED LOOP
|
|
1186
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
1187
|
+
|
|
1188
|
+
# optimize_tune_context_efficiency
|
|
1189
|
+
# Read loop.context_efficiency events and recommend context budget adjustments.
|
|
1190
|
+
# If avg budget_utilization > 90%, recommend increasing context_budget_chars.
|
|
1191
|
+
# If avg trim_ratio > 30%, recommend reducing verbose context sections.
|
|
1192
|
+
optimize_tune_context_efficiency() {
|
|
1193
|
+
local events_file="${EVENTS_FILE:-${HOME}/.shipwright/events.jsonl}"
|
|
1194
|
+
|
|
1195
|
+
if [[ ! -f "$events_file" ]]; then
|
|
1196
|
+
info "No events file found — skipping context efficiency tuning"
|
|
1197
|
+
return 0
|
|
1198
|
+
fi
|
|
1199
|
+
|
|
1200
|
+
ensure_optimization_dir
|
|
1201
|
+
|
|
1202
|
+
info "Analyzing context efficiency..."
|
|
1203
|
+
|
|
1204
|
+
# Extract recent loop.context_efficiency events (last 200)
|
|
1205
|
+
local ctx_events
|
|
1206
|
+
ctx_events=$(grep '"loop.context_efficiency"' "$events_file" 2>/dev/null | tail -200 || true)
|
|
1207
|
+
|
|
1208
|
+
if [[ -z "$ctx_events" ]]; then
|
|
1209
|
+
info "No context efficiency events found — skipping"
|
|
1210
|
+
return 0
|
|
1211
|
+
fi
|
|
1212
|
+
|
|
1213
|
+
local event_count
|
|
1214
|
+
event_count=$(echo "$ctx_events" | wc -l | tr -d ' ')
|
|
1215
|
+
event_count="${event_count:-0}"
|
|
1216
|
+
|
|
1217
|
+
if [[ "$event_count" -lt 3 ]]; then
|
|
1218
|
+
info "Insufficient context efficiency events ($event_count) — need at least 3"
|
|
1219
|
+
return 0
|
|
1220
|
+
fi
|
|
1221
|
+
|
|
1222
|
+
# Calculate averages using jq (handles both string and numeric JSON values)
|
|
1223
|
+
local stats
|
|
1224
|
+
stats=$(echo "$ctx_events" | jq -rs '
|
|
1225
|
+
if length == 0 then {u:0,r:0,raw:0,tr:0,b:0,n:0}
|
|
1226
|
+
else {
|
|
1227
|
+
u: ([.[] | .budget_utilization | tonumber] | add / length),
|
|
1228
|
+
r: ([.[] | .trim_ratio | tonumber] | add / length),
|
|
1229
|
+
raw: ([.[] | .raw_prompt_chars | tonumber] | add / length),
|
|
1230
|
+
tr: ([.[] | .trimmed_prompt_chars | tonumber] | add / length),
|
|
1231
|
+
b: ([.[] | .budget_chars | tonumber] | last // 0),
|
|
1232
|
+
n: length
|
|
1233
|
+
} end
|
|
1234
|
+
| "\(.u) \(.r) \(.raw) \(.tr) \(.b) \(.n)"
|
|
1235
|
+
' 2>/dev/null || echo "0 0 0 0 0 0")
|
|
1236
|
+
|
|
1237
|
+
local avg_utilization avg_trim_ratio avg_raw avg_trimmed current_budget sample_count
|
|
1238
|
+
avg_utilization=$(echo "$stats" | awk '{printf "%.1f", $1}')
|
|
1239
|
+
avg_trim_ratio=$(echo "$stats" | awk '{printf "%.1f", $2}')
|
|
1240
|
+
avg_raw=$(echo "$stats" | awk '{printf "%.0f", $3}')
|
|
1241
|
+
avg_trimmed=$(echo "$stats" | awk '{printf "%.0f", $4}')
|
|
1242
|
+
current_budget=$(echo "$stats" | awk '{printf "%.0f", $5}')
|
|
1243
|
+
sample_count=$(echo "$stats" | awk '{printf "%d", $6}')
|
|
1244
|
+
|
|
1245
|
+
info "Context efficiency: avg_utilization=${avg_utilization}%, avg_trim_ratio=${avg_trim_ratio}%, samples=${sample_count}"
|
|
1246
|
+
|
|
1247
|
+
local recommendations=""
|
|
1248
|
+
local rec_count=0
|
|
1249
|
+
|
|
1250
|
+
# Rule 1: If avg budget_utilization > 90%, recommend increasing context_budget_chars
|
|
1251
|
+
if awk -v u="$avg_utilization" 'BEGIN { exit !(u > 90) }' 2>/dev/null; then
|
|
1252
|
+
local new_budget
|
|
1253
|
+
new_budget=$(awk -v b="$current_budget" 'BEGIN { printf "%.0f", b * 1.2 }')
|
|
1254
|
+
# Cap at 300000
|
|
1255
|
+
if awk -v nb="$new_budget" 'BEGIN { exit !(nb > 300000) }' 2>/dev/null; then
|
|
1256
|
+
new_budget=300000
|
|
1257
|
+
fi
|
|
1258
|
+
recommendations="${recommendations}increase_budget:${new_budget} "
|
|
1259
|
+
rec_count=$((rec_count + 1))
|
|
1260
|
+
warn "Budget utilization high (${avg_utilization}%) — recommend increasing context_budget_chars from ${current_budget} to ${new_budget}"
|
|
1261
|
+
|
|
1262
|
+
emit_event "optimize.context_recommendation" \
|
|
1263
|
+
"action=increase_budget" \
|
|
1264
|
+
"current=${current_budget}" \
|
|
1265
|
+
"recommended=${new_budget}" \
|
|
1266
|
+
"avg_utilization=${avg_utilization}" \
|
|
1267
|
+
"samples=${sample_count}"
|
|
1268
|
+
fi
|
|
1269
|
+
|
|
1270
|
+
# Rule 2: If avg trim_ratio > 30%, recommend reducing verbose context sections
|
|
1271
|
+
if awk -v r="$avg_trim_ratio" 'BEGIN { exit !(r > 30) }' 2>/dev/null; then
|
|
1272
|
+
local avg_discarded
|
|
1273
|
+
avg_discarded=$(awk -v raw="$avg_raw" -v trimmed="$avg_trimmed" 'BEGIN { printf "%.0f", raw - trimmed }')
|
|
1274
|
+
recommendations="${recommendations}reduce_verbose:${avg_discarded} "
|
|
1275
|
+
rec_count=$((rec_count + 1))
|
|
1276
|
+
warn "Trim ratio high (${avg_trim_ratio}%) — avg ${avg_discarded} chars discarded per iteration"
|
|
1277
|
+
warn "Recommend reducing verbose context: lower context_trim_memory_chars, context_trim_git_entries, or context_trim_test_lines"
|
|
1278
|
+
|
|
1279
|
+
emit_event "optimize.context_recommendation" \
|
|
1280
|
+
"action=reduce_verbose" \
|
|
1281
|
+
"avg_trim_ratio=${avg_trim_ratio}" \
|
|
1282
|
+
"avg_discarded=${avg_discarded}" \
|
|
1283
|
+
"samples=${sample_count}"
|
|
1284
|
+
fi
|
|
1285
|
+
|
|
1286
|
+
# Rule 3: If budget utilization is very low (<50%), recommend decreasing budget to save tokens
|
|
1287
|
+
if awk -v u="$avg_utilization" 'BEGIN { exit !(u < 50) }' 2>/dev/null && [[ "$sample_count" -ge 10 ]]; then
|
|
1288
|
+
local smaller_budget
|
|
1289
|
+
smaller_budget=$(awk -v b="$current_budget" 'BEGIN { printf "%.0f", b * 0.85 }')
|
|
1290
|
+
# Floor at 100000
|
|
1291
|
+
if awk -v sb="$smaller_budget" 'BEGIN { exit !(sb < 100000) }' 2>/dev/null; then
|
|
1292
|
+
smaller_budget=100000
|
|
1293
|
+
fi
|
|
1294
|
+
recommendations="${recommendations}decrease_budget:${smaller_budget} "
|
|
1295
|
+
rec_count=$((rec_count + 1))
|
|
1296
|
+
info "Budget utilization low (${avg_utilization}%) — recommend decreasing context_budget_chars from ${current_budget} to ${smaller_budget} to save tokens"
|
|
1297
|
+
|
|
1298
|
+
emit_event "optimize.context_recommendation" \
|
|
1299
|
+
"action=decrease_budget" \
|
|
1300
|
+
"current=${current_budget}" \
|
|
1301
|
+
"recommended=${smaller_budget}" \
|
|
1302
|
+
"avg_utilization=${avg_utilization}" \
|
|
1303
|
+
"samples=${sample_count}"
|
|
1304
|
+
fi
|
|
1305
|
+
|
|
1306
|
+
# Write context efficiency summary to optimization dir
|
|
1307
|
+
local summary_file="${OPTIMIZATION_DIR}/context-efficiency.json"
|
|
1308
|
+
local tmp_summary
|
|
1309
|
+
tmp_summary=$(mktemp "${summary_file}.tmp.XXXXXX")
|
|
1310
|
+
trap "rm -f '$tmp_summary'" RETURN
|
|
1311
|
+
jq -n \
|
|
1312
|
+
--argjson avg_utilization "$avg_utilization" \
|
|
1313
|
+
--argjson avg_trim_ratio "$avg_trim_ratio" \
|
|
1314
|
+
--argjson avg_raw "$avg_raw" \
|
|
1315
|
+
--argjson avg_trimmed "$avg_trimmed" \
|
|
1316
|
+
--argjson current_budget "${current_budget:-0}" \
|
|
1317
|
+
--argjson sample_count "$sample_count" \
|
|
1318
|
+
--argjson rec_count "$rec_count" \
|
|
1319
|
+
--arg recommendations "${recommendations}" \
|
|
1320
|
+
--arg updated "$(now_iso)" \
|
|
1321
|
+
'{
|
|
1322
|
+
avg_budget_utilization: $avg_utilization,
|
|
1323
|
+
avg_trim_ratio: $avg_trim_ratio,
|
|
1324
|
+
avg_raw_chars: $avg_raw,
|
|
1325
|
+
avg_trimmed_chars: $avg_trimmed,
|
|
1326
|
+
current_budget_chars: $current_budget,
|
|
1327
|
+
sample_count: $sample_count,
|
|
1328
|
+
recommendation_count: $rec_count,
|
|
1329
|
+
recommendations: $recommendations,
|
|
1330
|
+
updated_at: $updated
|
|
1331
|
+
}' > "$tmp_summary" && mv "$tmp_summary" "$summary_file" || rm -f "$tmp_summary"
|
|
1332
|
+
|
|
1333
|
+
if [[ "$rec_count" -eq 0 ]]; then
|
|
1334
|
+
success "Context efficiency is healthy (utilization: ${avg_utilization}%, trim: ${avg_trim_ratio}%)"
|
|
1335
|
+
else
|
|
1336
|
+
success "Context efficiency analysis complete — $rec_count recommendation(s)"
|
|
1337
|
+
fi
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1184
1340
|
# ═════════════════════════════════════════════════════════════════════════════
|
|
1185
1341
|
# QUALITY INDEX (LONGITUDINAL TRACKING)
|
|
1186
1342
|
# ═════════════════════════════════════════════════════════════════════════════
|
|
@@ -1206,10 +1362,14 @@ optimize_track_quality_index() {
|
|
|
1206
1362
|
[[ -z "$recent" ]] && return 0
|
|
1207
1363
|
|
|
1208
1364
|
local total success_count
|
|
1209
|
-
total=$(echo "$recent" | wc -l
|
|
1365
|
+
total=$(echo "$recent" | wc -l || true)
|
|
1366
|
+
total="${total:-0}"
|
|
1367
|
+
total=$(echo "$total" | tr -d ' ')
|
|
1210
1368
|
[[ "$total" -lt 3 ]] && return 0
|
|
1211
1369
|
|
|
1212
|
-
success_count=$(echo "$recent" | jq -c 'select(.result == "success" or .result == "completed")' 2>/dev/null | wc -l
|
|
1370
|
+
success_count=$(echo "$recent" | jq -c 'select(.result == "success" or .result == "completed")' 2>/dev/null | wc -l || true)
|
|
1371
|
+
success_count="${success_count:-0}"
|
|
1372
|
+
success_count=$(echo "$success_count" | tr -d ' ')
|
|
1213
1373
|
success_count="${success_count:-0}"
|
|
1214
1374
|
|
|
1215
1375
|
local avg_iterations avg_quality
|
|
@@ -1235,7 +1395,8 @@ optimize_track_quality_index() {
|
|
|
1235
1395
|
# Detect trend
|
|
1236
1396
|
if [[ -f "$quality_file" ]]; then
|
|
1237
1397
|
local line_count
|
|
1238
|
-
line_count=$(wc -l < "$quality_file" 2>/dev/null | tr -d ' ' ||
|
|
1398
|
+
line_count=$(wc -l < "$quality_file" 2>/dev/null | tr -d ' ' || true)
|
|
1399
|
+
line_count="${line_count:-0}"
|
|
1239
1400
|
if [[ "$line_count" -ge 2 ]]; then
|
|
1240
1401
|
local prev_index
|
|
1241
1402
|
prev_index=$(tail -2 "$quality_file" | head -1 | jq -r '.quality_index // 0' 2>/dev/null || echo "0")
|
|
@@ -1295,6 +1456,7 @@ optimize_full_analysis() {
|
|
|
1295
1456
|
optimize_route_models
|
|
1296
1457
|
optimize_learn_risk_keywords
|
|
1297
1458
|
optimize_evolve_memory
|
|
1459
|
+
optimize_tune_context_efficiency 2>/dev/null || true
|
|
1298
1460
|
optimize_track_quality_index 2>/dev/null || true
|
|
1299
1461
|
optimize_report >> "${OPTIMIZATION_DIR}/last-report.txt" 2>/dev/null || true
|
|
1300
1462
|
optimize_adjust_audit_intensity 2>/dev/null || true
|
|
@@ -1489,6 +1651,7 @@ show_help() {
|
|
|
1489
1651
|
echo " quality-index Show quality trend (last 10 snapshots)"
|
|
1490
1652
|
echo " ingest-retro Ingest most recent retro into optimization loop"
|
|
1491
1653
|
echo " evolve-memory Prune/strengthen/promote memory patterns"
|
|
1654
|
+
echo " context-efficiency Analyze context budget usage and recommend tuning"
|
|
1492
1655
|
echo " help Show this help"
|
|
1493
1656
|
echo ""
|
|
1494
1657
|
echo -e "${CYAN}STORAGE${RESET}"
|
|
@@ -1513,6 +1676,7 @@ main() {
|
|
|
1513
1676
|
report) optimize_report ;;
|
|
1514
1677
|
quality-index) cmd_quality_index ;;
|
|
1515
1678
|
evolve-memory) optimize_evolve_memory ;;
|
|
1679
|
+
context-efficiency) optimize_tune_context_efficiency ;;
|
|
1516
1680
|
help|--help|-h) show_help ;;
|
|
1517
1681
|
*) error "Unknown command: $cmd"; exit 1 ;;
|
|
1518
1682
|
esac
|
package/scripts/sw-session.sh
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# ║ Supports --template to scaffold from a team template and --terminal ║
|
|
9
9
|
# ║ to select a terminal adapter (tmux, iterm2, wezterm). ║
|
|
10
10
|
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
11
|
-
VERSION="3.
|
|
11
|
+
VERSION="3.2.0"
|
|
12
12
|
set -euo pipefail
|
|
13
13
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
14
14
|
|
|
@@ -355,7 +355,7 @@ if [[ "$DRY_RUN" == true ]]; then
|
|
|
355
355
|
cat << EOF
|
|
356
356
|
#!/usr/bin/env bash
|
|
357
357
|
# Auto-generated by shipwright session — safe to delete
|
|
358
|
-
cd ${PROJECT_DIR} || exit 1
|
|
358
|
+
cd "${PROJECT_DIR}" || exit 1
|
|
359
359
|
printf '\\033]2;${TEAM_NAME}-lead\\033\\\\'
|
|
360
360
|
PROMPT=\$(cat <prompt-file>)
|
|
361
361
|
rm -f <prompt-file> "\$0"
|
|
@@ -367,7 +367,7 @@ EOF
|
|
|
367
367
|
else
|
|
368
368
|
cat << EOF
|
|
369
369
|
#!/usr/bin/env bash
|
|
370
|
-
cd ${PROJECT_DIR} || exit 1
|
|
370
|
+
cd "${PROJECT_DIR}" || exit 1
|
|
371
371
|
printf '\\033]2;${TEAM_NAME}-lead\\033\\\\'
|
|
372
372
|
rm -f "\$0"
|
|
373
373
|
claude${DRY_RUN_FLAGS}
|
package/scripts/sw-setup.sh
CHANGED
package/scripts/sw-standup.sh
CHANGED
package/scripts/sw-status.sh
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# ║ ║
|
|
5
5
|
# ║ Shows running teams, agent windows, and task progress. ║
|
|
6
6
|
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
7
|
-
VERSION="3.
|
|
7
|
+
VERSION="3.2.0"
|
|
8
8
|
set -euo pipefail
|
|
9
9
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
10
10
|
|
package/scripts/sw-strategic.sh
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# When sourced, do NOT add set -euo pipefail — the parent handles that.
|
|
8
8
|
# When run directly, main() sets up the error handling.
|
|
9
9
|
|
|
10
|
-
VERSION="3.
|
|
10
|
+
VERSION="3.2.0"
|
|
11
11
|
|
|
12
12
|
# ─── Paths (set defaults if not provided by parent) ──────────────────────────
|
|
13
13
|
SCRIPT_DIR="${SCRIPT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}"
|
|
@@ -821,12 +821,14 @@ strategic_status() {
|
|
|
821
821
|
|
|
822
822
|
# Total issues created
|
|
823
823
|
local total_created
|
|
824
|
-
total_created=$(grep '"strategic.issue_created"' "$events_file" 2>/dev/null | wc -l | tr -d ' ' ||
|
|
824
|
+
total_created=$(grep '"strategic.issue_created"' "$events_file" 2>/dev/null | wc -l | tr -d ' ' || true)
|
|
825
|
+
total_created="${total_created:-0}"
|
|
825
826
|
echo -e " Total created: ${total_created} issues (all time)"
|
|
826
827
|
|
|
827
828
|
# Total cycles
|
|
828
829
|
local total_cycles
|
|
829
|
-
total_cycles=$(grep '"strategic.cycle_complete"' "$events_file" 2>/dev/null | wc -l | tr -d ' ' ||
|
|
830
|
+
total_cycles=$(grep '"strategic.cycle_complete"' "$events_file" 2>/dev/null | wc -l | tr -d ' ' || true)
|
|
831
|
+
total_cycles="${total_cycles:-0}"
|
|
830
832
|
echo -e " Total cycles: ${total_cycles}"
|
|
831
833
|
|
|
832
834
|
echo ""
|
|
@@ -845,9 +847,12 @@ strategic_outcomes() {
|
|
|
845
847
|
fi
|
|
846
848
|
|
|
847
849
|
local shipped_count failed_count pending_count
|
|
848
|
-
shipped_count=$(grep -c '"outcome":"shipped"' "$outcomes_file" 2>/dev/null ||
|
|
849
|
-
|
|
850
|
-
|
|
850
|
+
shipped_count=$(grep -c '"outcome":"shipped"' "$outcomes_file" 2>/dev/null || true)
|
|
851
|
+
shipped_count="${shipped_count:-0}"
|
|
852
|
+
failed_count=$(grep -cE '"outcome":"closed_unshipped"' "$outcomes_file" 2>/dev/null || true)
|
|
853
|
+
failed_count="${failed_count:-0}"
|
|
854
|
+
pending_count=$(grep -c '"outcome":"pending"' "$outcomes_file" 2>/dev/null || true)
|
|
855
|
+
pending_count="${pending_count:-0}"
|
|
851
856
|
|
|
852
857
|
echo -e " ${GREEN}Shipped:${RESET} $shipped_count (closed with merged PR)"
|
|
853
858
|
echo -e " ${RED}Closed unshipped:${RESET} $failed_count (closed without merge)"
|