shipwright-cli 3.2.0 → 3.3.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/.claude/agents/code-reviewer.md +2 -0
- package/.claude/agents/devops-engineer.md +2 -0
- package/.claude/agents/doc-fleet-agent.md +2 -0
- package/.claude/agents/pipeline-agent.md +2 -0
- package/.claude/agents/shell-script-specialist.md +2 -0
- package/.claude/agents/test-specialist.md +2 -0
- package/.claude/hooks/agent-crash-capture.sh +32 -0
- package/.claude/hooks/post-tool-use.sh +3 -2
- package/.claude/hooks/pre-tool-use.sh +35 -3
- package/README.md +4 -4
- package/claude-code/hooks/config-change.sh +18 -0
- package/claude-code/hooks/instructions-reloaded.sh +7 -0
- package/claude-code/hooks/worktree-create.sh +25 -0
- package/claude-code/hooks/worktree-remove.sh +20 -0
- package/config/code-constitution.json +130 -0
- package/dashboard/middleware/auth.ts +134 -0
- package/dashboard/middleware/constants.ts +21 -0
- package/dashboard/public/index.html +2 -6
- package/dashboard/public/styles.css +100 -97
- package/dashboard/routes/auth.ts +38 -0
- package/dashboard/server.ts +66 -25
- package/dashboard/services/config.ts +26 -0
- package/dashboard/services/db.ts +118 -0
- package/dashboard/src/canvas/pixel-agent.ts +298 -0
- package/dashboard/src/canvas/pixel-sprites.ts +440 -0
- package/dashboard/src/canvas/shipyard-effects.ts +367 -0
- package/dashboard/src/canvas/shipyard-scene.ts +616 -0
- package/dashboard/src/canvas/submarine-layout.ts +267 -0
- package/dashboard/src/components/header.ts +8 -7
- package/dashboard/src/core/router.ts +1 -0
- package/dashboard/src/design/submarine-theme.ts +253 -0
- package/dashboard/src/main.ts +2 -0
- package/dashboard/src/types/api.ts +2 -1
- package/dashboard/src/views/activity.ts +2 -1
- package/dashboard/src/views/shipyard.ts +39 -0
- package/dashboard/types/index.ts +166 -0
- package/docs/plans/2026-02-28-compound-audit-and-shipyard-design.md +186 -0
- package/docs/plans/2026-02-28-skipper-shipwright-implementation-plan.md +1182 -0
- package/docs/plans/2026-02-28-skipper-shipwright-integration-design.md +531 -0
- package/docs/plans/2026-03-01-ai-powered-skill-injection-design.md +298 -0
- package/docs/plans/2026-03-01-ai-powered-skill-injection-plan.md +1109 -0
- package/docs/plans/2026-03-01-capabilities-cleanup-plan.md +658 -0
- package/docs/plans/2026-03-01-clean-architecture-plan.md +924 -0
- package/docs/plans/2026-03-01-compound-audit-cascade-design.md +191 -0
- package/docs/plans/2026-03-01-compound-audit-cascade-plan.md +921 -0
- package/docs/plans/2026-03-01-deep-integration-plan.md +851 -0
- package/docs/plans/2026-03-01-pipeline-audit-trail-design.md +145 -0
- package/docs/plans/2026-03-01-pipeline-audit-trail-plan.md +770 -0
- package/docs/plans/2026-03-01-refined-depths-brand-design.md +382 -0
- package/docs/plans/2026-03-01-refined-depths-implementation.md +599 -0
- package/docs/plans/2026-03-01-skipper-kernel-integration-design.md +203 -0
- package/docs/plans/2026-03-01-unified-platform-design.md +272 -0
- package/docs/plans/2026-03-07-claude-code-feature-integration-design.md +189 -0
- package/docs/plans/2026-03-07-claude-code-feature-integration-plan.md +1165 -0
- package/docs/research/BACKLOG_QUICK_REFERENCE.md +352 -0
- package/docs/research/CUTTING_EDGE_RESEARCH_2026.md +546 -0
- package/docs/research/RESEARCH_INDEX.md +439 -0
- package/docs/research/RESEARCH_SOURCES.md +440 -0
- package/docs/research/RESEARCH_SUMMARY.txt +275 -0
- package/docs/superpowers/specs/2026-03-10-pipeline-quality-revolution-design.md +341 -0
- package/package.json +2 -2
- package/scripts/lib/adaptive-model.sh +427 -0
- package/scripts/lib/adaptive-timeout.sh +316 -0
- package/scripts/lib/audit-trail.sh +309 -0
- package/scripts/lib/auto-recovery.sh +471 -0
- package/scripts/lib/bandit-selector.sh +431 -0
- package/scripts/lib/bootstrap.sh +104 -2
- package/scripts/lib/causal-graph.sh +455 -0
- package/scripts/lib/compat.sh +126 -0
- package/scripts/lib/compound-audit.sh +337 -0
- package/scripts/lib/constitutional.sh +454 -0
- package/scripts/lib/context-budget.sh +359 -0
- package/scripts/lib/convergence.sh +594 -0
- package/scripts/lib/cost-optimizer.sh +634 -0
- package/scripts/lib/daemon-adaptive.sh +10 -0
- package/scripts/lib/daemon-dispatch.sh +106 -17
- package/scripts/lib/daemon-failure.sh +34 -4
- package/scripts/lib/daemon-patrol.sh +23 -2
- package/scripts/lib/daemon-poll-github.sh +361 -0
- package/scripts/lib/daemon-poll-health.sh +299 -0
- package/scripts/lib/daemon-poll.sh +27 -611
- package/scripts/lib/daemon-state.sh +112 -66
- package/scripts/lib/daemon-triage.sh +10 -0
- package/scripts/lib/dod-scorecard.sh +442 -0
- package/scripts/lib/error-actionability.sh +300 -0
- package/scripts/lib/formal-spec.sh +461 -0
- package/scripts/lib/helpers.sh +177 -4
- package/scripts/lib/intent-analysis.sh +409 -0
- package/scripts/lib/loop-convergence.sh +350 -0
- package/scripts/lib/loop-iteration.sh +682 -0
- package/scripts/lib/loop-progress.sh +48 -0
- package/scripts/lib/loop-restart.sh +185 -0
- package/scripts/lib/memory-effectiveness.sh +506 -0
- package/scripts/lib/mutation-executor.sh +352 -0
- package/scripts/lib/outcome-feedback.sh +521 -0
- package/scripts/lib/pipeline-cli.sh +336 -0
- package/scripts/lib/pipeline-commands.sh +1216 -0
- package/scripts/lib/pipeline-detection.sh +100 -2
- package/scripts/lib/pipeline-execution.sh +897 -0
- package/scripts/lib/pipeline-github.sh +28 -3
- package/scripts/lib/pipeline-intelligence-compound.sh +431 -0
- package/scripts/lib/pipeline-intelligence-scoring.sh +407 -0
- package/scripts/lib/pipeline-intelligence-skip.sh +181 -0
- package/scripts/lib/pipeline-intelligence.sh +100 -1136
- package/scripts/lib/pipeline-quality-bash-compat.sh +182 -0
- package/scripts/lib/pipeline-quality-checks.sh +17 -715
- package/scripts/lib/pipeline-quality-gates.sh +563 -0
- package/scripts/lib/pipeline-stages-build.sh +730 -0
- package/scripts/lib/pipeline-stages-delivery.sh +965 -0
- package/scripts/lib/pipeline-stages-intake.sh +1133 -0
- package/scripts/lib/pipeline-stages-monitor.sh +407 -0
- package/scripts/lib/pipeline-stages-review.sh +1022 -0
- package/scripts/lib/pipeline-stages.sh +59 -2929
- package/scripts/lib/pipeline-state.sh +36 -5
- package/scripts/lib/pipeline-util.sh +487 -0
- package/scripts/lib/policy-learner.sh +438 -0
- package/scripts/lib/process-reward.sh +493 -0
- package/scripts/lib/project-detect.sh +649 -0
- package/scripts/lib/quality-profile.sh +334 -0
- package/scripts/lib/recruit-commands.sh +885 -0
- package/scripts/lib/recruit-learning.sh +739 -0
- package/scripts/lib/recruit-roles.sh +648 -0
- package/scripts/lib/reward-aggregator.sh +458 -0
- package/scripts/lib/rl-optimizer.sh +362 -0
- package/scripts/lib/root-cause.sh +427 -0
- package/scripts/lib/scope-enforcement.sh +445 -0
- package/scripts/lib/session-restart.sh +493 -0
- package/scripts/lib/skill-memory.sh +300 -0
- package/scripts/lib/skill-registry.sh +775 -0
- package/scripts/lib/spec-driven.sh +476 -0
- package/scripts/lib/test-helpers.sh +18 -7
- package/scripts/lib/test-holdout.sh +429 -0
- package/scripts/lib/test-optimizer.sh +511 -0
- package/scripts/shipwright-file-suggest.sh +45 -0
- package/scripts/skills/adversarial-quality.md +61 -0
- package/scripts/skills/api-design.md +44 -0
- package/scripts/skills/architecture-design.md +50 -0
- package/scripts/skills/brainstorming.md +43 -0
- package/scripts/skills/data-pipeline.md +44 -0
- package/scripts/skills/deploy-safety.md +64 -0
- package/scripts/skills/documentation.md +38 -0
- package/scripts/skills/frontend-design.md +45 -0
- package/scripts/skills/generated/.gitkeep +0 -0
- package/scripts/skills/generated/_refinements/.gitkeep +0 -0
- package/scripts/skills/generated/_refinements/adversarial-quality.patch.md +3 -0
- package/scripts/skills/generated/_refinements/architecture-design.patch.md +3 -0
- package/scripts/skills/generated/_refinements/brainstorming.patch.md +3 -0
- package/scripts/skills/generated/cli-version-management.md +29 -0
- package/scripts/skills/generated/collection-system-validation.md +99 -0
- package/scripts/skills/generated/large-scale-c-refactoring-coordination.md +97 -0
- package/scripts/skills/generated/pattern-matching-similarity-scoring.md +195 -0
- package/scripts/skills/generated/test-parallelization-detection.md +65 -0
- package/scripts/skills/observability.md +79 -0
- package/scripts/skills/performance.md +48 -0
- package/scripts/skills/pr-quality.md +49 -0
- package/scripts/skills/product-thinking.md +43 -0
- package/scripts/skills/security-audit.md +49 -0
- package/scripts/skills/systematic-debugging.md +40 -0
- package/scripts/skills/testing-strategy.md +47 -0
- package/scripts/skills/two-stage-review.md +52 -0
- package/scripts/skills/validation-thoroughness.md +55 -0
- package/scripts/sw +9 -3
- package/scripts/sw-activity.sh +9 -2
- package/scripts/sw-adaptive.sh +2 -1
- package/scripts/sw-adversarial.sh +2 -1
- package/scripts/sw-architecture-enforcer.sh +3 -1
- package/scripts/sw-auth.sh +12 -2
- package/scripts/sw-autonomous.sh +5 -1
- package/scripts/sw-changelog.sh +4 -1
- package/scripts/sw-checkpoint.sh +2 -1
- package/scripts/sw-ci.sh +5 -1
- package/scripts/sw-cleanup.sh +4 -26
- package/scripts/sw-code-review.sh +10 -4
- package/scripts/sw-connect.sh +2 -1
- package/scripts/sw-context.sh +2 -1
- package/scripts/sw-cost.sh +48 -3
- package/scripts/sw-daemon.sh +66 -9
- package/scripts/sw-dashboard.sh +3 -1
- package/scripts/sw-db.sh +59 -16
- package/scripts/sw-decide.sh +8 -2
- package/scripts/sw-decompose.sh +360 -17
- package/scripts/sw-deps.sh +4 -1
- package/scripts/sw-developer-simulation.sh +4 -1
- package/scripts/sw-discovery.sh +325 -2
- package/scripts/sw-doc-fleet.sh +4 -1
- package/scripts/sw-docs-agent.sh +3 -1
- package/scripts/sw-docs.sh +2 -1
- package/scripts/sw-doctor.sh +453 -2
- package/scripts/sw-dora.sh +4 -1
- package/scripts/sw-durable.sh +4 -3
- package/scripts/sw-e2e-orchestrator.sh +17 -16
- package/scripts/sw-eventbus.sh +7 -1
- package/scripts/sw-evidence.sh +364 -12
- package/scripts/sw-feedback.sh +550 -9
- package/scripts/sw-fix.sh +20 -1
- package/scripts/sw-fleet-discover.sh +6 -2
- package/scripts/sw-fleet-viz.sh +4 -1
- package/scripts/sw-fleet.sh +5 -1
- package/scripts/sw-github-app.sh +16 -3
- package/scripts/sw-github-checks.sh +3 -2
- package/scripts/sw-github-deploy.sh +3 -2
- package/scripts/sw-github-graphql.sh +18 -7
- package/scripts/sw-guild.sh +5 -1
- package/scripts/sw-heartbeat.sh +5 -30
- package/scripts/sw-hello.sh +67 -0
- package/scripts/sw-hygiene.sh +6 -1
- package/scripts/sw-incident.sh +265 -1
- package/scripts/sw-init.sh +18 -2
- package/scripts/sw-instrument.sh +10 -2
- package/scripts/sw-intelligence.sh +42 -6
- package/scripts/sw-jira.sh +5 -1
- package/scripts/sw-launchd.sh +2 -1
- package/scripts/sw-linear.sh +4 -1
- package/scripts/sw-logs.sh +4 -1
- package/scripts/sw-loop.sh +432 -1128
- package/scripts/sw-memory.sh +356 -2
- package/scripts/sw-mission-control.sh +6 -1
- package/scripts/sw-model-router.sh +481 -26
- package/scripts/sw-otel.sh +13 -4
- package/scripts/sw-oversight.sh +14 -5
- package/scripts/sw-patrol-meta.sh +334 -0
- package/scripts/sw-pipeline-composer.sh +5 -1
- package/scripts/sw-pipeline-vitals.sh +2 -1
- package/scripts/sw-pipeline.sh +53 -2664
- package/scripts/sw-pm.sh +12 -5
- package/scripts/sw-pr-lifecycle.sh +2 -1
- package/scripts/sw-predictive.sh +7 -1
- package/scripts/sw-prep.sh +185 -2
- package/scripts/sw-ps.sh +5 -25
- package/scripts/sw-public-dashboard.sh +15 -3
- package/scripts/sw-quality.sh +2 -1
- package/scripts/sw-reaper.sh +8 -25
- package/scripts/sw-recruit.sh +156 -2303
- package/scripts/sw-regression.sh +19 -12
- package/scripts/sw-release-manager.sh +3 -1
- package/scripts/sw-release.sh +4 -1
- package/scripts/sw-remote.sh +3 -1
- package/scripts/sw-replay.sh +7 -1
- package/scripts/sw-retro.sh +158 -1
- package/scripts/sw-review-rerun.sh +3 -1
- package/scripts/sw-scale.sh +10 -3
- package/scripts/sw-security-audit.sh +6 -1
- package/scripts/sw-self-optimize.sh +6 -3
- package/scripts/sw-session.sh +9 -3
- package/scripts/sw-setup.sh +3 -1
- package/scripts/sw-stall-detector.sh +406 -0
- package/scripts/sw-standup.sh +15 -7
- package/scripts/sw-status.sh +3 -1
- package/scripts/sw-strategic.sh +4 -1
- package/scripts/sw-stream.sh +7 -1
- package/scripts/sw-swarm.sh +18 -6
- package/scripts/sw-team-stages.sh +13 -6
- package/scripts/sw-templates.sh +5 -29
- package/scripts/sw-testgen.sh +7 -1
- package/scripts/sw-tmux-pipeline.sh +4 -1
- package/scripts/sw-tmux-role-color.sh +2 -0
- package/scripts/sw-tmux-status.sh +1 -1
- package/scripts/sw-tmux.sh +3 -1
- package/scripts/sw-trace.sh +3 -1
- package/scripts/sw-tracker-github.sh +3 -0
- package/scripts/sw-tracker-jira.sh +3 -0
- package/scripts/sw-tracker-linear.sh +3 -0
- package/scripts/sw-tracker.sh +3 -1
- package/scripts/sw-triage.sh +2 -1
- package/scripts/sw-upgrade.sh +3 -1
- package/scripts/sw-ux.sh +5 -2
- package/scripts/sw-webhook.sh +3 -1
- package/scripts/sw-widgets.sh +3 -1
- package/scripts/sw-worktree.sh +15 -3
- package/scripts/test-skill-injection.sh +1233 -0
- package/templates/pipelines/autonomous.json +27 -3
- package/templates/pipelines/cost-aware.json +34 -8
- package/templates/pipelines/deployed.json +12 -0
- package/templates/pipelines/enterprise.json +12 -0
- package/templates/pipelines/fast.json +6 -0
- package/templates/pipelines/full.json +27 -3
- package/templates/pipelines/hotfix.json +6 -0
- package/templates/pipelines/standard.json +12 -0
- package/templates/pipelines/tdd.json +12 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
# ║ shipwright stall-detector — Pipeline Stall & Deadlock Detection ║
|
|
4
|
+
# ║ Monitor running pipelines, detect stalls, classify issues, auto-abort ║
|
|
5
|
+
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
VERSION="3.3.0"
|
|
9
|
+
# shellcheck source=lib/bootstrap.sh
|
|
10
|
+
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib/bootstrap.sh"
|
|
11
|
+
|
|
12
|
+
# ─── Constants ──────────────────────────────────────────────────────────────
|
|
13
|
+
HEARTBEAT_DIR="$HOME/.shipwright/heartbeats"
|
|
14
|
+
PIPELINE_ARTIFACTS_DIR="${PIPELINE_ARTIFACTS_DIR:-.claude/pipeline-artifacts}"
|
|
15
|
+
STATE_FILE="${PIPELINE_ARTIFACTS_DIR:-pipeline-state.md}"
|
|
16
|
+
STALL_DETECTOR_STATE="${HOME}/.shipwright/stall-detector-state.json"
|
|
17
|
+
|
|
18
|
+
# Default configuration
|
|
19
|
+
STALL_TIMEOUT_SECONDS="${STALL_TIMEOUT_SECONDS:-300}"
|
|
20
|
+
MAX_STAGE_RETRIES="${MAX_STAGE_RETRIES:-5}"
|
|
21
|
+
HEARTBEAT_MAX_AGE_SECONDS="${HEARTBEAT_MAX_AGE_SECONDS:-120}"
|
|
22
|
+
AUTO_ABORT_ENABLED="${AUTO_ABORT_ENABLED:-true}"
|
|
23
|
+
CREATE_ISSUE_ON_ABORT="${CREATE_ISSUE_ON_ABORT:-false}"
|
|
24
|
+
STALL_DETECTOR_INTERVAL="${STALL_DETECTOR_INTERVAL:-30}"
|
|
25
|
+
|
|
26
|
+
# ─── Help ───────────────────────────────────────────────────────────────────
|
|
27
|
+
show_help() {
|
|
28
|
+
echo ""
|
|
29
|
+
echo -e "${CYAN}${BOLD} Shipwright Stall Detector${RESET} ${DIM}v${VERSION}${RESET}"
|
|
30
|
+
echo -e "${DIM} ════════════════════════════════════════════${RESET}"
|
|
31
|
+
echo ""
|
|
32
|
+
echo -e " ${BOLD}USAGE${RESET}"
|
|
33
|
+
echo -e " shipwright stall-detector <command> [options]"
|
|
34
|
+
echo ""
|
|
35
|
+
echo -e " ${BOLD}COMMANDS${RESET}"
|
|
36
|
+
echo -e " ${CYAN}check${RESET} Single-shot detection, return JSON"
|
|
37
|
+
echo -e " ${CYAN}watch${RESET} Background monitor (runs every N seconds)"
|
|
38
|
+
echo -e " ${CYAN}abort${RESET} Manually abort a stalled pipeline"
|
|
39
|
+
echo -e " ${CYAN}config${RESET} Show current configuration"
|
|
40
|
+
echo -e " ${CYAN}status${RESET} Show detection stats and recent stalls"
|
|
41
|
+
echo ""
|
|
42
|
+
echo -e " ${BOLD}CHECK OPTIONS${RESET}"
|
|
43
|
+
echo -e " (none)"
|
|
44
|
+
echo ""
|
|
45
|
+
echo -e " ${BOLD}WATCH OPTIONS${RESET}"
|
|
46
|
+
echo -e " --interval <secs> Check interval (default: 30)"
|
|
47
|
+
echo -e " --once Run once and exit"
|
|
48
|
+
echo ""
|
|
49
|
+
echo -e " ${BOLD}ABORT OPTIONS${RESET}"
|
|
50
|
+
echo -e " <pipeline-id> Pipeline ID or PID to abort"
|
|
51
|
+
echo -e " --reason <text> Abort reason (optional)"
|
|
52
|
+
echo ""
|
|
53
|
+
echo -e " ${BOLD}EXAMPLES${RESET}"
|
|
54
|
+
echo -e " ${DIM}# One-shot detection${RESET}"
|
|
55
|
+
echo -e " shipwright stall-detector check"
|
|
56
|
+
echo ""
|
|
57
|
+
echo -e " ${DIM}# Monitor in background${RESET}"
|
|
58
|
+
echo -e " shipwright stall-detector watch --interval 60"
|
|
59
|
+
echo ""
|
|
60
|
+
echo -e " ${DIM}# Manually abort a stalled pipeline${RESET}"
|
|
61
|
+
echo -e " shipwright stall-detector abort pipeline-12345 --reason \"build timeout\""
|
|
62
|
+
echo ""
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# ─── Ensure heartbeat and artifacts directories exist ──────────────────────
|
|
66
|
+
ensure_dirs() {
|
|
67
|
+
mkdir -p "$HEARTBEAT_DIR"
|
|
68
|
+
mkdir -p "${PIPELINE_ARTIFACTS_DIR%/*}" 2>/dev/null || true
|
|
69
|
+
mkdir -p "${STALL_DETECTOR_STATE%/*}" 2>/dev/null || true
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# ─── Load Configuration ──────────────────────────────────────────────────────
|
|
73
|
+
load_config() {
|
|
74
|
+
local config_file=".claude/daemon-config.json"
|
|
75
|
+
|
|
76
|
+
if [[ -f "$config_file" ]]; then
|
|
77
|
+
STALL_TIMEOUT_SECONDS=$(jq -r '.stall_detection.stall_timeout_seconds // 300' "$config_file" 2>/dev/null || echo "300")
|
|
78
|
+
MAX_STAGE_RETRIES=$(jq -r '.stall_detection.max_stage_retries // 5' "$config_file" 2>/dev/null || echo "5")
|
|
79
|
+
HEARTBEAT_MAX_AGE_SECONDS=$(jq -r '.stall_detection.heartbeat_max_age_seconds // 120' "$config_file" 2>/dev/null || echo "120")
|
|
80
|
+
AUTO_ABORT_ENABLED=$(jq -r '.stall_detection.auto_abort_enabled // true' "$config_file" 2>/dev/null || echo "true")
|
|
81
|
+
CREATE_ISSUE_ON_ABORT=$(jq -r '.stall_detection.create_issue_on_abort // false' "$config_file" 2>/dev/null || echo "false")
|
|
82
|
+
STALL_DETECTOR_INTERVAL=$(jq -r '.stall_detection.interval // 30' "$config_file" 2>/dev/null || echo "30")
|
|
83
|
+
fi
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# ─── Classify Stall Type ────────────────────────────────────────────────────
|
|
87
|
+
# Returns: {type, reason, severity}
|
|
88
|
+
stall_detector_classify() {
|
|
89
|
+
local heartbeat_age="$1"
|
|
90
|
+
local stage="$2"
|
|
91
|
+
local retry_count="${3:-0}"
|
|
92
|
+
|
|
93
|
+
local classification_json
|
|
94
|
+
|
|
95
|
+
# Determine stall type based on evidence
|
|
96
|
+
if [[ "$heartbeat_age" -gt "$STALL_TIMEOUT_SECONDS" ]]; then
|
|
97
|
+
classification_json=$(jq -n \
|
|
98
|
+
--arg type "heartbeat_stale" \
|
|
99
|
+
--arg reason "No heartbeat update for ${heartbeat_age}s (threshold: ${STALL_TIMEOUT_SECONDS}s)" \
|
|
100
|
+
--arg severity "critical" \
|
|
101
|
+
'{type: $type, reason: $reason, severity: $severity}')
|
|
102
|
+
elif [[ "$retry_count" -gt "$MAX_STAGE_RETRIES" ]]; then
|
|
103
|
+
classification_json=$(jq -n \
|
|
104
|
+
--arg type "loop_detected" \
|
|
105
|
+
--arg reason "Stage ${stage} restarted ${retry_count} times (max: ${MAX_STAGE_RETRIES})" \
|
|
106
|
+
--arg severity "warning" \
|
|
107
|
+
'{type: $type, reason: $reason, severity: $severity}')
|
|
108
|
+
else
|
|
109
|
+
classification_json=$(jq -n \
|
|
110
|
+
--arg type "timeout" \
|
|
111
|
+
--arg reason "Stage ${stage} exceeded max duration" \
|
|
112
|
+
--arg severity "warning" \
|
|
113
|
+
'{type: $type, reason: $reason, severity: $severity}')
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
echo "$classification_json"
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
# ─── Single-shot stall check ─────────────────────────────────────────────────
|
|
120
|
+
# Returns JSON: {stalled: [...], deadlocked: [...], looping: [...]}
|
|
121
|
+
cmd_check() {
|
|
122
|
+
ensure_dirs
|
|
123
|
+
load_config
|
|
124
|
+
|
|
125
|
+
# Build arrays using jq
|
|
126
|
+
local result_json
|
|
127
|
+
|
|
128
|
+
# Read all heartbeat files
|
|
129
|
+
if [[ ! -d "$HEARTBEAT_DIR" ]]; then
|
|
130
|
+
result_json=$(jq -n '{stalled: [], deadlocked: [], looping: [], checked_at: "'"$(now_iso)"'", pipeline_count: 0}')
|
|
131
|
+
echo "$result_json"
|
|
132
|
+
return 0
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
local now_epoch
|
|
136
|
+
now_epoch="$(now_epoch)"
|
|
137
|
+
|
|
138
|
+
# Build stalled list using jq
|
|
139
|
+
local stalled_items=""
|
|
140
|
+
for hb_file in "${HEARTBEAT_DIR}"/*.json; do
|
|
141
|
+
[[ ! -f "$hb_file" ]] && continue
|
|
142
|
+
|
|
143
|
+
local job_id updated_at stage
|
|
144
|
+
job_id="$(basename "$hb_file" .json)"
|
|
145
|
+
updated_at=$(jq -r '.updated_at // empty' "$hb_file" 2>/dev/null || true)
|
|
146
|
+
stage=$(jq -r '.stage // empty' "$hb_file" 2>/dev/null || true)
|
|
147
|
+
|
|
148
|
+
if [[ -z "$updated_at" ]]; then
|
|
149
|
+
continue
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
# Calculate heartbeat age
|
|
153
|
+
local hb_epoch
|
|
154
|
+
if TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%SZ" "$updated_at" +%s >/dev/null 2>&1; then
|
|
155
|
+
hb_epoch="$(TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%SZ" "$updated_at" +%s 2>/dev/null)"
|
|
156
|
+
else
|
|
157
|
+
hb_epoch="$(date -d "$updated_at" +%s 2>/dev/null || echo 0)"
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
local current_age=$((now_epoch - hb_epoch))
|
|
161
|
+
|
|
162
|
+
# Check for stale heartbeat
|
|
163
|
+
if [[ "$current_age" -gt "$HEARTBEAT_MAX_AGE_SECONDS" ]]; then
|
|
164
|
+
stalled_items="${stalled_items} \"$job_id\""
|
|
165
|
+
emit_event "stall_detector.stalled" "job_id=$job_id" "age_seconds=$current_age" "stage=$stage" 2>/dev/null || true
|
|
166
|
+
fi
|
|
167
|
+
done
|
|
168
|
+
|
|
169
|
+
# Build final JSON result
|
|
170
|
+
result_json=$(jq -n \
|
|
171
|
+
--arg checked_at "$(now_iso)" \
|
|
172
|
+
'{
|
|
173
|
+
stalled: ['"${stalled_items# }"'],
|
|
174
|
+
deadlocked: [],
|
|
175
|
+
looping: [],
|
|
176
|
+
checked_at: $checked_at,
|
|
177
|
+
pipeline_count: 0
|
|
178
|
+
}' 2>/dev/null || jq -n '{stalled: [], deadlocked: [], looping: [], checked_at: "'"$(now_iso)"'", pipeline_count: 0}')
|
|
179
|
+
|
|
180
|
+
# Fix pipeline_count
|
|
181
|
+
result_json=$(echo "$result_json" | jq '.pipeline_count = ((.stalled | length) + (.deadlocked | length) + (.looping | length))')
|
|
182
|
+
|
|
183
|
+
echo "$result_json" | jq '.'
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
# ─── Auto-abort a stalled pipeline ──────────────────────────────────────────
|
|
187
|
+
cmd_abort() {
|
|
188
|
+
local pipeline_id="${1:-}"
|
|
189
|
+
if [[ -z "$pipeline_id" ]]; then
|
|
190
|
+
error "Usage: shipwright stall-detector abort <pipeline-id> [--reason <text>]"
|
|
191
|
+
exit 1
|
|
192
|
+
fi
|
|
193
|
+
shift
|
|
194
|
+
|
|
195
|
+
local reason="User abort"
|
|
196
|
+
while [[ $# -gt 0 ]]; do
|
|
197
|
+
case "$1" in
|
|
198
|
+
--reason) reason="${2:-}"; shift 2 ;;
|
|
199
|
+
*) shift ;;
|
|
200
|
+
esac
|
|
201
|
+
done
|
|
202
|
+
|
|
203
|
+
ensure_dirs
|
|
204
|
+
load_config
|
|
205
|
+
|
|
206
|
+
local hb_file="${HEARTBEAT_DIR}/${pipeline_id}.json"
|
|
207
|
+
|
|
208
|
+
if [[ ! -f "$hb_file" ]]; then
|
|
209
|
+
error "No heartbeat found for pipeline: ${pipeline_id}"
|
|
210
|
+
return 1
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
local pid
|
|
214
|
+
pid=$(jq -r '.pid // empty' "$hb_file" 2>/dev/null || true)
|
|
215
|
+
|
|
216
|
+
if [[ -z "$pid" ]]; then
|
|
217
|
+
error "Invalid heartbeat file for pipeline: ${pipeline_id}"
|
|
218
|
+
return 1
|
|
219
|
+
fi
|
|
220
|
+
|
|
221
|
+
# Send SIGTERM to pipeline process
|
|
222
|
+
if kill -0 "$pid" 2>/dev/null; then
|
|
223
|
+
info "Sending SIGTERM to pipeline process $pid..."
|
|
224
|
+
kill -TERM "$pid" 2>/dev/null || true
|
|
225
|
+
sleep 2
|
|
226
|
+
|
|
227
|
+
# If still alive, send SIGKILL
|
|
228
|
+
if kill -0 "$pid" 2>/dev/null; then
|
|
229
|
+
warn "Process did not respond to SIGTERM, sending SIGKILL..."
|
|
230
|
+
kill -KILL "$pid" 2>/dev/null || true
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
success "Aborted pipeline: ${pipeline_id} (reason: ${reason})"
|
|
234
|
+
else
|
|
235
|
+
warn "Pipeline process $pid not running (already dead)"
|
|
236
|
+
fi
|
|
237
|
+
|
|
238
|
+
# Update heartbeat to mark as aborted
|
|
239
|
+
local tmp_file
|
|
240
|
+
tmp_file="$(mktemp "${HEARTBEAT_DIR}/.tmp.XXXXXX")" || { error "mktemp failed"; return 1; }
|
|
241
|
+
|
|
242
|
+
jq --arg reason "$reason" --arg aborted_at "$(now_iso)" \
|
|
243
|
+
'. + {status: "aborted", abort_reason: $reason, aborted_at: $aborted_at}' \
|
|
244
|
+
"$hb_file" > "$tmp_file" || { rm -f "$tmp_file"; return 1; }
|
|
245
|
+
|
|
246
|
+
mv "$tmp_file" "$hb_file" || { rm -f "$tmp_file"; return 1; }
|
|
247
|
+
|
|
248
|
+
# Emit abort event
|
|
249
|
+
emit_event "pipeline.stall_aborted" "pipeline_id=$pipeline_id" "reason=$reason" "pid=$pid" 2>/dev/null || true
|
|
250
|
+
|
|
251
|
+
# Clear heartbeat file (optional - mark as processed)
|
|
252
|
+
rm -f "$hb_file" 2>/dev/null || true
|
|
253
|
+
|
|
254
|
+
return 0
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
# ─── Background watch loop ──────────────────────────────────────────────────
|
|
258
|
+
cmd_watch() {
|
|
259
|
+
local interval="$STALL_DETECTOR_INTERVAL"
|
|
260
|
+
local once=false
|
|
261
|
+
|
|
262
|
+
while [[ $# -gt 0 ]]; do
|
|
263
|
+
case "$1" in
|
|
264
|
+
--interval) interval="${2:-30}"; shift 2 ;;
|
|
265
|
+
--once) once=true; shift ;;
|
|
266
|
+
*) shift ;;
|
|
267
|
+
esac
|
|
268
|
+
done
|
|
269
|
+
|
|
270
|
+
ensure_dirs
|
|
271
|
+
load_config
|
|
272
|
+
|
|
273
|
+
info "Starting stall detector (interval: ${interval}s, auto-abort: ${AUTO_ABORT_ENABLED})"
|
|
274
|
+
|
|
275
|
+
# Write PID to state file
|
|
276
|
+
local tmp_state
|
|
277
|
+
tmp_state="$(mktemp)" || { error "mktemp failed"; return 1; }
|
|
278
|
+
jq -n --arg pid "$$" --arg started_at "$(now_iso)" \
|
|
279
|
+
'{pid: ($pid | tonumber), started_at: $started_at, checks: 0, aborts: 0}' > "$tmp_state" || { rm -f "$tmp_state"; return 1; }
|
|
280
|
+
mv "$tmp_state" "$STALL_DETECTOR_STATE" 2>/dev/null || true
|
|
281
|
+
|
|
282
|
+
while true; do
|
|
283
|
+
local check_result
|
|
284
|
+
check_result=$(cmd_check)
|
|
285
|
+
|
|
286
|
+
local stalled_count deadlocked_count looping_count
|
|
287
|
+
stalled_count=$(echo "$check_result" | jq '.stalled | length' 2>/dev/null || echo "0")
|
|
288
|
+
deadlocked_count=$(echo "$check_result" | jq '.deadlocked | length' 2>/dev/null || echo "0")
|
|
289
|
+
looping_count=$(echo "$check_result" | jq '.looping | length' 2>/dev/null || echo "0")
|
|
290
|
+
|
|
291
|
+
# Log check result if any stalls detected
|
|
292
|
+
if [[ "$stalled_count" -gt 0 || "$deadlocked_count" -gt 0 || "$looping_count" -gt 0 ]]; then
|
|
293
|
+
warn "Detected stalls: $stalled_count stalled, $deadlocked_count deadlocked, $looping_count looping"
|
|
294
|
+
|
|
295
|
+
# Auto-abort if enabled
|
|
296
|
+
if [[ "$AUTO_ABORT_ENABLED" == "true" ]]; then
|
|
297
|
+
local stalled_pids
|
|
298
|
+
stalled_pids=$(echo "$check_result" | jq -r '.stalled[] // empty' 2>/dev/null || true)
|
|
299
|
+
while IFS= read -r pid_or_id; do
|
|
300
|
+
[[ -z "$pid_or_id" ]] && continue
|
|
301
|
+
info "Auto-aborting stalled pipeline: $pid_or_id"
|
|
302
|
+
cmd_abort "$pid_or_id" --reason "auto-abort: stall detected (no heartbeat update)"
|
|
303
|
+
done <<< "$stalled_pids"
|
|
304
|
+
fi
|
|
305
|
+
fi
|
|
306
|
+
|
|
307
|
+
[[ "$once" == "true" ]] && break
|
|
308
|
+
|
|
309
|
+
sleep "$interval"
|
|
310
|
+
done
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
# ─── Show configuration ─────────────────────────────────────────────────────
|
|
314
|
+
cmd_config() {
|
|
315
|
+
ensure_dirs
|
|
316
|
+
load_config
|
|
317
|
+
|
|
318
|
+
echo ""
|
|
319
|
+
echo -e "${BOLD}Stall Detection Configuration${RESET}"
|
|
320
|
+
echo -e "${DIM}═════════════════════════════════════${RESET}"
|
|
321
|
+
echo ""
|
|
322
|
+
echo -e " ${CYAN}stall_timeout_seconds${RESET} ${STALL_TIMEOUT_SECONDS}"
|
|
323
|
+
echo -e " ${CYAN}max_stage_retries${RESET} ${MAX_STAGE_RETRIES}"
|
|
324
|
+
echo -e " ${CYAN}heartbeat_max_age_seconds${RESET} ${HEARTBEAT_MAX_AGE_SECONDS}"
|
|
325
|
+
echo -e " ${CYAN}auto_abort_enabled${RESET} ${AUTO_ABORT_ENABLED}"
|
|
326
|
+
echo -e " ${CYAN}create_issue_on_abort${RESET} ${CREATE_ISSUE_ON_ABORT}"
|
|
327
|
+
echo -e " ${CYAN}detector_interval${RESET} ${STALL_DETECTOR_INTERVAL}s"
|
|
328
|
+
echo ""
|
|
329
|
+
echo -e "${DIM}Configure via: .claude/daemon-config.json${RESET}"
|
|
330
|
+
echo -e "${DIM}Example:${RESET}"
|
|
331
|
+
echo ' {
|
|
332
|
+
"stall_detection": {
|
|
333
|
+
"stall_timeout_seconds": 300,
|
|
334
|
+
"max_stage_retries": 5,
|
|
335
|
+
"heartbeat_max_age_seconds": 120,
|
|
336
|
+
"auto_abort_enabled": true,
|
|
337
|
+
"create_issue_on_abort": false,
|
|
338
|
+
"interval": 30
|
|
339
|
+
}
|
|
340
|
+
}'
|
|
341
|
+
echo ""
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
# ─── Show detection statistics ──────────────────────────────────────────────
|
|
345
|
+
cmd_status() {
|
|
346
|
+
ensure_dirs
|
|
347
|
+
|
|
348
|
+
echo ""
|
|
349
|
+
echo -e "${BOLD}Stall Detection Status${RESET}"
|
|
350
|
+
echo -e "${DIM}══════════════════════${RESET}"
|
|
351
|
+
echo ""
|
|
352
|
+
|
|
353
|
+
# Show detector process info if running
|
|
354
|
+
if [[ -f "$STALL_DETECTOR_STATE" ]]; then
|
|
355
|
+
local pid started_at checks aborts
|
|
356
|
+
pid=$(jq -r '.pid // empty' "$STALL_DETECTOR_STATE" 2>/dev/null || true)
|
|
357
|
+
started_at=$(jq -r '.started_at // empty' "$STALL_DETECTOR_STATE" 2>/dev/null || true)
|
|
358
|
+
checks=$(jq -r '.checks // 0' "$STALL_DETECTOR_STATE" 2>/dev/null || true)
|
|
359
|
+
aborts=$(jq -r '.aborts // 0' "$STALL_DETECTOR_STATE" 2>/dev/null || true)
|
|
360
|
+
|
|
361
|
+
if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
|
|
362
|
+
echo -e " ${GREEN}✓${RESET} Detector running (PID: $pid)"
|
|
363
|
+
echo -e " Started: $started_at"
|
|
364
|
+
echo -e " Checks performed: $checks"
|
|
365
|
+
echo -e " Pipelines aborted: $aborts"
|
|
366
|
+
else
|
|
367
|
+
echo -e " ${YELLOW}✗${RESET} Detector not running"
|
|
368
|
+
fi
|
|
369
|
+
else
|
|
370
|
+
echo -e " ${YELLOW}✗${RESET} Detector not running"
|
|
371
|
+
fi
|
|
372
|
+
|
|
373
|
+
echo ""
|
|
374
|
+
|
|
375
|
+
# Show recent stalls
|
|
376
|
+
if [[ -d "$HEARTBEAT_DIR" ]]; then
|
|
377
|
+
local stall_count
|
|
378
|
+
stall_count=$(ls -1 "$HEARTBEAT_DIR"/*.json 2>/dev/null | wc -l)
|
|
379
|
+
echo -e " ${CYAN}Active heartbeats: $stall_count${RESET}"
|
|
380
|
+
fi
|
|
381
|
+
|
|
382
|
+
echo ""
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
# ─── Command Router ────────────────────────────────────────────────────────
|
|
386
|
+
main() {
|
|
387
|
+
local cmd="${1:-help}"
|
|
388
|
+
shift 2>/dev/null || true
|
|
389
|
+
|
|
390
|
+
case "$cmd" in
|
|
391
|
+
check) cmd_check "$@" ;;
|
|
392
|
+
watch) cmd_watch "$@" ;;
|
|
393
|
+
abort) cmd_abort "$@" ;;
|
|
394
|
+
config) cmd_config "$@" ;;
|
|
395
|
+
status) cmd_status "$@" ;;
|
|
396
|
+
help|--help|-h) show_help ;;
|
|
397
|
+
*)
|
|
398
|
+
error "Unknown command: ${cmd}"
|
|
399
|
+
echo ""
|
|
400
|
+
show_help
|
|
401
|
+
exit 1
|
|
402
|
+
;;
|
|
403
|
+
esac
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
main "$@"
|
package/scripts/sw-standup.sh
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
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.3.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
# shellcheck disable=SC2034
|
|
11
12
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
13
|
|
|
13
14
|
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
|
@@ -29,7 +30,8 @@ fi
|
|
|
29
30
|
if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
30
31
|
emit_event() {
|
|
31
32
|
local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
|
|
32
|
-
local payload
|
|
33
|
+
local payload
|
|
34
|
+
payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
|
|
33
35
|
while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
|
|
34
36
|
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
35
37
|
}
|
|
@@ -68,7 +70,8 @@ cmd_yesterday() {
|
|
|
68
70
|
now_epoch="$(now_epoch)"
|
|
69
71
|
local cutoff=$((now_epoch - SECONDS_24H))
|
|
70
72
|
|
|
71
|
-
local report_file
|
|
73
|
+
local report_file
|
|
74
|
+
report_file="${STANDUP_DIR}/yesterday-$(date +%Y-%m-%d).txt"
|
|
72
75
|
|
|
73
76
|
{
|
|
74
77
|
echo "╔════════════════════════════════════════════════════════════════════╗"
|
|
@@ -143,7 +146,8 @@ cmd_yesterday() {
|
|
|
143
146
|
cmd_today() {
|
|
144
147
|
ensure_dirs
|
|
145
148
|
|
|
146
|
-
local report_file
|
|
149
|
+
local report_file
|
|
150
|
+
report_file="${STANDUP_DIR}/today-$(date +%Y-%m-%d).txt"
|
|
147
151
|
|
|
148
152
|
{
|
|
149
153
|
echo "╔════════════════════════════════════════════════════════════════════╗"
|
|
@@ -193,7 +197,8 @@ cmd_today() {
|
|
|
193
197
|
cmd_blockers() {
|
|
194
198
|
ensure_dirs
|
|
195
199
|
|
|
196
|
-
local report_file
|
|
200
|
+
local report_file
|
|
201
|
+
report_file="${STANDUP_DIR}/blockers-$(date +%Y-%m-%d).txt"
|
|
197
202
|
|
|
198
203
|
{
|
|
199
204
|
echo "╔════════════════════════════════════════════════════════════════════╗"
|
|
@@ -261,7 +266,8 @@ cmd_blockers() {
|
|
|
261
266
|
cmd_velocity() {
|
|
262
267
|
ensure_dirs
|
|
263
268
|
|
|
264
|
-
local report_file
|
|
269
|
+
local report_file
|
|
270
|
+
report_file="${STANDUP_DIR}/velocity-$(date +%Y-%m-%d).txt"
|
|
265
271
|
|
|
266
272
|
{
|
|
267
273
|
echo "╔════════════════════════════════════════════════════════════════════╗"
|
|
@@ -335,7 +341,8 @@ cmd_velocity() {
|
|
|
335
341
|
cmd_digest() {
|
|
336
342
|
ensure_dirs
|
|
337
343
|
|
|
338
|
-
local report_file
|
|
344
|
+
local report_file
|
|
345
|
+
report_file="${STANDUP_DIR}/digest-$(date +%Y-%m-%d-%H%M%S).txt"
|
|
339
346
|
|
|
340
347
|
{
|
|
341
348
|
echo ""
|
|
@@ -493,6 +500,7 @@ cmd_notify() {
|
|
|
493
500
|
if [[ -z "$message_file" ]]; then
|
|
494
501
|
# Generate a digest
|
|
495
502
|
message_file=$(mktemp)
|
|
503
|
+
# shellcheck disable=SC2064
|
|
496
504
|
trap "rm -f '$message_file'" RETURN
|
|
497
505
|
cmd_digest > "$message_file" 2>&1 || true
|
|
498
506
|
fi
|
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.3.0"
|
|
8
8
|
set -euo pipefail
|
|
9
9
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
10
10
|
|
|
@@ -47,6 +47,7 @@ fi
|
|
|
47
47
|
if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
48
48
|
emit_event() {
|
|
49
49
|
local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
|
|
50
|
+
# shellcheck disable=SC2155
|
|
50
51
|
local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
|
|
51
52
|
while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
|
|
52
53
|
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
@@ -572,6 +573,7 @@ if [[ -f "$STATE_FILE" ]]; then
|
|
|
572
573
|
if [[ "$completed_count" -gt 0 ]]; then
|
|
573
574
|
echo ""
|
|
574
575
|
echo -e " ${BOLD}Recent Completions${RESET}"
|
|
576
|
+
# shellcheck disable=SC2034
|
|
575
577
|
while IFS=$'\t' read -r c_num c_result c_dur c_at; do
|
|
576
578
|
[[ -z "$c_num" ]] && continue
|
|
577
579
|
if [[ "$c_result" == "success" ]]; then
|
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.3.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)}"
|
|
@@ -25,6 +25,7 @@ EVENTS_FILE="${EVENTS_FILE:-${HOME}/.shipwright/events.jsonl}"
|
|
|
25
25
|
if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
26
26
|
emit_event() {
|
|
27
27
|
local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
|
|
28
|
+
# shellcheck disable=SC2155
|
|
28
29
|
local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
|
|
29
30
|
while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
|
|
30
31
|
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
@@ -46,6 +47,7 @@ fi
|
|
|
46
47
|
STRATEGIC_MAX_ISSUES=5
|
|
47
48
|
STRATEGIC_COOLDOWN_SECONDS=14400 # 4 hours
|
|
48
49
|
STRATEGIC_MODEL="claude-sonnet-4-5-20250929"
|
|
50
|
+
# shellcheck disable=SC2034
|
|
49
51
|
STRATEGIC_MAX_TOKENS=4096
|
|
50
52
|
STRATEGIC_STRATEGY_LINES=200
|
|
51
53
|
STRATEGIC_LABELS="auto-patrol,ready-to-build,strategic,shipwright"
|
|
@@ -494,6 +496,7 @@ strategic_call_api() {
|
|
|
494
496
|
|
|
495
497
|
local tmp_prompt
|
|
496
498
|
tmp_prompt=$(mktemp)
|
|
499
|
+
# shellcheck disable=SC2064
|
|
497
500
|
trap "rm -f '$tmp_prompt'" RETURN
|
|
498
501
|
printf '%s' "$prompt" > "$tmp_prompt"
|
|
499
502
|
|
package/scripts/sw-stream.sh
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
# ║ Streams tmux pane output in real-time to the dashboard or CLI. ║
|
|
6
6
|
# ║ Captures output periodically, tags by agent/team, supports replay. ║
|
|
7
7
|
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
8
|
-
|
|
8
|
+
# shellcheck disable=SC2034
|
|
9
|
+
VERSION="3.3.0"
|
|
9
10
|
set -euo pipefail
|
|
10
11
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
11
12
|
|
|
@@ -30,6 +31,7 @@ fi
|
|
|
30
31
|
if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
31
32
|
emit_event() {
|
|
32
33
|
local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
|
|
34
|
+
# shellcheck disable=SC2155
|
|
33
35
|
local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
|
|
34
36
|
while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
|
|
35
37
|
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
@@ -48,6 +50,7 @@ load_config() {
|
|
|
48
50
|
if [[ -f "$STREAM_CONFIG" ]]; then
|
|
49
51
|
CAPTURE_INTERVAL=$(jq -r '.capture_interval_seconds // 1' "$STREAM_CONFIG" 2>/dev/null || echo 1)
|
|
50
52
|
BUFFER_LINES=$(jq -r '.buffer_lines // 500' "$STREAM_CONFIG" 2>/dev/null || echo 500)
|
|
53
|
+
# shellcheck disable=SC2034
|
|
51
54
|
OUTPUT_FORMAT=$(jq -r '.output_format // "jsonl"' "$STREAM_CONFIG" 2>/dev/null || echo "jsonl")
|
|
52
55
|
fi
|
|
53
56
|
}
|
|
@@ -97,6 +100,7 @@ capture_pane_output() {
|
|
|
97
100
|
# Write JSONL entry with timestamp, pane_id, agent, team, content
|
|
98
101
|
local tmp_file
|
|
99
102
|
tmp_file=$(mktemp)
|
|
103
|
+
# shellcheck disable=SC2064
|
|
100
104
|
trap "rm -f '$tmp_file'" RETURN
|
|
101
105
|
|
|
102
106
|
{
|
|
@@ -116,6 +120,7 @@ capture_pane_output() {
|
|
|
116
120
|
line_count=$(wc -l < "$pane_file" 2>/dev/null || true)
|
|
117
121
|
line_count="${line_count:-0}"
|
|
118
122
|
if [[ "$line_count" -gt "$BUFFER_LINES" ]]; then
|
|
123
|
+
# shellcheck disable=SC2034
|
|
119
124
|
local skip=$((line_count - BUFFER_LINES))
|
|
120
125
|
tail -n "$BUFFER_LINES" "$pane_file" > "${pane_file}.tmp"
|
|
121
126
|
mv "${pane_file}.tmp" "$pane_file"
|
|
@@ -349,6 +354,7 @@ stream_config() {
|
|
|
349
354
|
# Create tmp file for atomic write
|
|
350
355
|
local tmp_file
|
|
351
356
|
tmp_file=$(mktemp)
|
|
357
|
+
# shellcheck disable=SC2064
|
|
352
358
|
trap "rm -f '$tmp_file'" RETURN
|
|
353
359
|
|
|
354
360
|
case "$key" in
|