shipwright-cli 2.4.0 → 3.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 +16 -11
- package/completions/_shipwright +1 -1
- package/completions/shipwright.bash +3 -8
- package/completions/shipwright.fish +1 -1
- package/config/defaults.json +111 -0
- package/config/event-schema.json +81 -0
- package/config/policy.json +13 -18
- package/dashboard/coverage/coverage-summary.json +14 -0
- package/dashboard/public/index.html +1 -1
- package/dashboard/server.ts +306 -17
- package/dashboard/src/components/charts/bar.test.ts +79 -0
- package/dashboard/src/components/charts/donut.test.ts +68 -0
- package/dashboard/src/components/charts/pipeline-rail.test.ts +117 -0
- package/dashboard/src/components/charts/sparkline.test.ts +125 -0
- package/dashboard/src/core/api.test.ts +309 -0
- package/dashboard/src/core/helpers.test.ts +301 -0
- package/dashboard/src/core/router.test.ts +307 -0
- package/dashboard/src/core/router.ts +7 -0
- package/dashboard/src/core/sse.test.ts +144 -0
- package/dashboard/src/views/metrics.test.ts +186 -0
- package/dashboard/src/views/overview.test.ts +173 -0
- package/dashboard/src/views/pipelines.test.ts +183 -0
- package/dashboard/src/views/team.test.ts +253 -0
- package/dashboard/vitest.config.ts +14 -5
- package/docs/TIPS.md +1 -1
- package/docs/patterns/README.md +1 -1
- package/package.json +5 -7
- package/scripts/adapters/docker-deploy.sh +1 -1
- package/scripts/adapters/tmux-adapter.sh +11 -1
- package/scripts/adapters/wezterm-adapter.sh +1 -1
- package/scripts/check-version-consistency.sh +1 -1
- package/scripts/lib/architecture.sh +126 -0
- package/scripts/lib/bootstrap.sh +75 -0
- package/scripts/lib/compat.sh +89 -6
- package/scripts/lib/config.sh +91 -0
- package/scripts/lib/daemon-adaptive.sh +3 -3
- package/scripts/lib/daemon-dispatch.sh +39 -16
- package/scripts/lib/daemon-health.sh +1 -1
- package/scripts/lib/daemon-patrol.sh +24 -12
- package/scripts/lib/daemon-poll.sh +37 -25
- package/scripts/lib/daemon-state.sh +115 -23
- package/scripts/lib/daemon-triage.sh +30 -8
- package/scripts/lib/fleet-failover.sh +63 -0
- package/scripts/lib/helpers.sh +30 -6
- package/scripts/lib/pipeline-detection.sh +2 -2
- package/scripts/lib/pipeline-github.sh +9 -9
- package/scripts/lib/pipeline-intelligence.sh +85 -35
- package/scripts/lib/pipeline-quality-checks.sh +16 -16
- package/scripts/lib/pipeline-quality.sh +1 -1
- package/scripts/lib/pipeline-stages.sh +242 -28
- package/scripts/lib/pipeline-state.sh +40 -4
- package/scripts/lib/test-helpers.sh +247 -0
- package/scripts/postinstall.mjs +3 -11
- package/scripts/sw +10 -4
- package/scripts/sw-activity.sh +1 -11
- package/scripts/sw-adaptive.sh +109 -85
- package/scripts/sw-adversarial.sh +4 -14
- package/scripts/sw-architecture-enforcer.sh +1 -11
- package/scripts/sw-auth.sh +8 -17
- package/scripts/sw-autonomous.sh +111 -49
- package/scripts/sw-changelog.sh +1 -11
- package/scripts/sw-checkpoint.sh +144 -20
- package/scripts/sw-ci.sh +2 -12
- package/scripts/sw-cleanup.sh +13 -17
- package/scripts/sw-code-review.sh +16 -36
- package/scripts/sw-connect.sh +5 -12
- package/scripts/sw-context.sh +9 -26
- package/scripts/sw-cost.sh +6 -16
- package/scripts/sw-daemon.sh +75 -70
- package/scripts/sw-dashboard.sh +57 -17
- package/scripts/sw-db.sh +506 -15
- package/scripts/sw-decompose.sh +1 -11
- package/scripts/sw-deps.sh +15 -25
- package/scripts/sw-developer-simulation.sh +1 -11
- package/scripts/sw-discovery.sh +112 -30
- package/scripts/sw-doc-fleet.sh +7 -17
- package/scripts/sw-docs-agent.sh +6 -16
- package/scripts/sw-docs.sh +4 -12
- package/scripts/sw-doctor.sh +134 -43
- package/scripts/sw-dora.sh +11 -19
- package/scripts/sw-durable.sh +35 -52
- package/scripts/sw-e2e-orchestrator.sh +11 -27
- package/scripts/sw-eventbus.sh +115 -115
- package/scripts/sw-evidence.sh +114 -30
- package/scripts/sw-feedback.sh +3 -13
- package/scripts/sw-fix.sh +2 -20
- package/scripts/sw-fleet-discover.sh +1 -11
- package/scripts/sw-fleet-viz.sh +10 -18
- package/scripts/sw-fleet.sh +13 -17
- package/scripts/sw-github-app.sh +6 -16
- package/scripts/sw-github-checks.sh +1 -11
- package/scripts/sw-github-deploy.sh +1 -11
- package/scripts/sw-github-graphql.sh +2 -12
- package/scripts/sw-guild.sh +1 -11
- package/scripts/sw-heartbeat.sh +49 -12
- package/scripts/sw-hygiene.sh +45 -43
- package/scripts/sw-incident.sh +48 -74
- package/scripts/sw-init.sh +35 -37
- package/scripts/sw-instrument.sh +1 -11
- package/scripts/sw-intelligence.sh +362 -51
- package/scripts/sw-jira.sh +5 -14
- package/scripts/sw-launchd.sh +2 -12
- package/scripts/sw-linear.sh +8 -17
- package/scripts/sw-logs.sh +4 -12
- package/scripts/sw-loop.sh +641 -90
- package/scripts/sw-memory.sh +243 -17
- package/scripts/sw-mission-control.sh +2 -12
- package/scripts/sw-model-router.sh +73 -34
- package/scripts/sw-otel.sh +11 -21
- package/scripts/sw-oversight.sh +1 -11
- package/scripts/sw-patrol-meta.sh +5 -11
- package/scripts/sw-pipeline-composer.sh +7 -17
- package/scripts/sw-pipeline-vitals.sh +1 -11
- package/scripts/sw-pipeline.sh +478 -122
- package/scripts/sw-pm.sh +2 -12
- package/scripts/sw-pr-lifecycle.sh +27 -25
- package/scripts/sw-predictive.sh +16 -22
- package/scripts/sw-prep.sh +6 -16
- package/scripts/sw-ps.sh +1 -11
- package/scripts/sw-public-dashboard.sh +2 -12
- package/scripts/sw-quality.sh +77 -10
- package/scripts/sw-reaper.sh +1 -11
- package/scripts/sw-recruit.sh +15 -25
- package/scripts/sw-regression.sh +11 -21
- package/scripts/sw-release-manager.sh +19 -28
- package/scripts/sw-release.sh +8 -16
- package/scripts/sw-remote.sh +1 -11
- package/scripts/sw-replay.sh +48 -44
- package/scripts/sw-retro.sh +70 -92
- package/scripts/sw-review-rerun.sh +1 -1
- package/scripts/sw-scale.sh +109 -32
- package/scripts/sw-security-audit.sh +12 -22
- package/scripts/sw-self-optimize.sh +239 -23
- package/scripts/sw-session.sh +3 -13
- package/scripts/sw-setup.sh +8 -18
- package/scripts/sw-standup.sh +5 -15
- package/scripts/sw-status.sh +32 -23
- package/scripts/sw-strategic.sh +129 -13
- package/scripts/sw-stream.sh +1 -11
- package/scripts/sw-swarm.sh +76 -36
- package/scripts/sw-team-stages.sh +10 -20
- package/scripts/sw-templates.sh +4 -14
- package/scripts/sw-testgen.sh +3 -13
- package/scripts/sw-tmux-pipeline.sh +1 -19
- package/scripts/sw-tmux-role-color.sh +0 -10
- package/scripts/sw-tmux-status.sh +3 -11
- package/scripts/sw-tmux.sh +2 -20
- package/scripts/sw-trace.sh +1 -19
- package/scripts/sw-tracker-github.sh +0 -10
- package/scripts/sw-tracker-jira.sh +1 -11
- package/scripts/sw-tracker-linear.sh +1 -11
- package/scripts/sw-tracker.sh +7 -24
- package/scripts/sw-triage.sh +24 -34
- package/scripts/sw-upgrade.sh +5 -23
- package/scripts/sw-ux.sh +1 -19
- package/scripts/sw-webhook.sh +18 -32
- package/scripts/sw-widgets.sh +3 -21
- package/scripts/sw-worktree.sh +11 -27
- package/scripts/update-homebrew-sha.sh +67 -0
- package/templates/pipelines/tdd.json +72 -0
- package/scripts/sw-pipeline.sh.mock +0 -7
package/scripts/lib/helpers.sh
CHANGED
|
@@ -64,7 +64,7 @@ emit_event() {
|
|
|
64
64
|
shift
|
|
65
65
|
|
|
66
66
|
# Try SQLite first (via sw-db.sh's db_add_event)
|
|
67
|
-
if type db_add_event
|
|
67
|
+
if type db_add_event >/dev/null 2>&1; then
|
|
68
68
|
db_add_event "$event_type" "$@" 2>/dev/null || true
|
|
69
69
|
fi
|
|
70
70
|
|
|
@@ -76,7 +76,10 @@ emit_event() {
|
|
|
76
76
|
if [[ "$val" =~ ^-?[0-9]+\.?[0-9]*$ ]]; then
|
|
77
77
|
json_fields="${json_fields},\"${key}\":${val}"
|
|
78
78
|
else
|
|
79
|
-
val="${val
|
|
79
|
+
val="${val//\\/\\\\}" # escape backslashes first
|
|
80
|
+
val="${val//\"/\\\"}" # then quotes
|
|
81
|
+
val="${val//$'\n'/\\n}" # then newlines
|
|
82
|
+
val="${val//$'\t'/\\t}" # then tabs
|
|
80
83
|
json_fields="${json_fields},\"${key}\":\"${val}\""
|
|
81
84
|
fi
|
|
82
85
|
done
|
|
@@ -85,11 +88,20 @@ emit_event() {
|
|
|
85
88
|
# Use flock to prevent concurrent write corruption
|
|
86
89
|
local _lock_file="${EVENTS_FILE}.lock"
|
|
87
90
|
(
|
|
88
|
-
if command -v flock
|
|
91
|
+
if command -v flock >/dev/null 2>&1; then
|
|
89
92
|
flock -w 2 200 2>/dev/null || true
|
|
90
93
|
fi
|
|
91
94
|
echo "$_event_line" >> "$EVENTS_FILE"
|
|
92
95
|
) 200>"$_lock_file"
|
|
96
|
+
|
|
97
|
+
# Optional schema validation (dev mode only)
|
|
98
|
+
if [[ -n "${SHIPWRIGHT_DEV:-}" && -n "${_CONFIG_REPO_DIR:-}" && -f "${_CONFIG_REPO_DIR}/config/event-schema.json" ]]; then
|
|
99
|
+
local known_types
|
|
100
|
+
known_types=$(jq -r '.event_types | keys[]' "${_CONFIG_REPO_DIR}/config/event-schema.json" 2>/dev/null || true)
|
|
101
|
+
if [[ -n "$known_types" ]] && ! echo "$known_types" | grep -qx "$event_type"; then
|
|
102
|
+
echo "WARN: Unknown event type '$event_type'" >&2
|
|
103
|
+
fi
|
|
104
|
+
fi
|
|
93
105
|
}
|
|
94
106
|
|
|
95
107
|
# Rotate a JSONL file to keep it within max_lines.
|
|
@@ -103,9 +115,7 @@ with_retry() {
|
|
|
103
115
|
local attempt=1
|
|
104
116
|
local delay=1
|
|
105
117
|
while [[ "$attempt" -le "$max_attempts" ]]; do
|
|
106
|
-
|
|
107
|
-
return 0
|
|
108
|
-
fi
|
|
118
|
+
"$@" && return 0
|
|
109
119
|
local exit_code=$?
|
|
110
120
|
if [[ "$attempt" -lt "$max_attempts" ]]; then
|
|
111
121
|
warn "Attempt $attempt/$max_attempts failed (exit $exit_code), retrying in ${delay}s..."
|
|
@@ -187,3 +197,17 @@ _sw_github_url() {
|
|
|
187
197
|
repo="$(_sw_github_repo)"
|
|
188
198
|
echo "https://github.com/${repo}"
|
|
189
199
|
}
|
|
200
|
+
|
|
201
|
+
# ─── Network Safe Wrappers (config-aware timeouts) ─────────────────────────────
|
|
202
|
+
# Use SHIPWRIGHT_* env vars if set; otherwise _config_get_int when config.sh is loaded
|
|
203
|
+
# Usage: _curl_safe [curl args...] | _gh_safe [gh args...]
|
|
204
|
+
_curl_safe() {
|
|
205
|
+
local ct="${SHIPWRIGHT_CONNECT_TIMEOUT:-$(_config_get_int "network.connect_timeout" 10 2>/dev/null || echo 10)}"
|
|
206
|
+
local mt="${SHIPWRIGHT_MAX_TIME:-$(_config_get_int "network.max_time" 60 2>/dev/null || echo 60)}"
|
|
207
|
+
curl --connect-timeout "$ct" --max-time "$mt" "$@"
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
_gh_safe() {
|
|
211
|
+
local gh_timeout="${SHIPWRIGHT_GH_TIMEOUT:-$(_config_get_int "network.gh_timeout" 30 2>/dev/null || echo 30)}"
|
|
212
|
+
GH_HTTP_TIMEOUT="$gh_timeout" _timeout "$gh_timeout" gh "$@"
|
|
213
|
+
}
|
|
@@ -101,7 +101,7 @@ detect_project_lang() {
|
|
|
101
101
|
fi
|
|
102
102
|
|
|
103
103
|
# Intelligence: holistic analysis for polyglot/monorepo detection
|
|
104
|
-
if [[ "$detected" == "unknown" ]] && type intelligence_search_memory
|
|
104
|
+
if [[ "$detected" == "unknown" ]] && type intelligence_search_memory >/dev/null 2>&1 && command -v claude >/dev/null 2>&1; then
|
|
105
105
|
local config_files
|
|
106
106
|
config_files=$(ls "$root" 2>/dev/null | grep -E '\.(json|toml|yaml|yml|xml|gradle|lock|mod)$' | head -15)
|
|
107
107
|
if [[ -n "$config_files" ]]; then
|
|
@@ -221,7 +221,7 @@ detect_task_type() {
|
|
|
221
221
|
local goal="$1"
|
|
222
222
|
|
|
223
223
|
# Intelligence: Claude classification with confidence score
|
|
224
|
-
if type intelligence_search_memory
|
|
224
|
+
if type intelligence_search_memory >/dev/null 2>&1 && command -v claude >/dev/null 2>&1; then
|
|
225
225
|
local ai_result
|
|
226
226
|
ai_result=$(claude --print --output-format text -p "Classify this task into exactly ONE category. Reply in format: CATEGORY|CONFIDENCE (0-100)
|
|
227
227
|
|
|
@@ -9,14 +9,14 @@ gh_init() {
|
|
|
9
9
|
return
|
|
10
10
|
fi
|
|
11
11
|
|
|
12
|
-
if ! command -v gh
|
|
12
|
+
if ! command -v gh >/dev/null 2>&1; then
|
|
13
13
|
GH_AVAILABLE=false
|
|
14
14
|
warn "gh CLI not found — GitHub integration disabled"
|
|
15
15
|
return
|
|
16
16
|
fi
|
|
17
17
|
|
|
18
18
|
# Check if authenticated
|
|
19
|
-
if ! gh auth status
|
|
19
|
+
if ! gh auth status >/dev/null 2>&1; then
|
|
20
20
|
GH_AVAILABLE=false
|
|
21
21
|
warn "gh not authenticated — GitHub integration disabled"
|
|
22
22
|
return
|
|
@@ -46,7 +46,7 @@ gh_init() {
|
|
|
46
46
|
gh_comment_issue() {
|
|
47
47
|
[[ "$GH_AVAILABLE" != "true" ]] && return 0
|
|
48
48
|
local issue_num="$1" body="$2"
|
|
49
|
-
gh issue comment "$issue_num" --body "$body" 2>/dev/null || true
|
|
49
|
+
_timeout 30 gh issue comment "$issue_num" --body "$body" 2>/dev/null || true
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
# Post a progress-tracking comment and save its ID for later updates
|
|
@@ -56,7 +56,7 @@ gh_post_progress() {
|
|
|
56
56
|
local issue_num="$1" body="$2"
|
|
57
57
|
local result
|
|
58
58
|
result=$(gh api "repos/${REPO_OWNER}/${REPO_NAME}/issues/${issue_num}/comments" \
|
|
59
|
-
-f body="$body" --jq '.id' 2>/dev/null) || true
|
|
59
|
+
-f body="$body" --jq '.id' --timeout 30 2>/dev/null) || true
|
|
60
60
|
if [[ -n "$result" && "$result" != "null" ]]; then
|
|
61
61
|
PROGRESS_COMMENT_ID="$result"
|
|
62
62
|
fi
|
|
@@ -68,7 +68,7 @@ gh_update_progress() {
|
|
|
68
68
|
[[ "$GH_AVAILABLE" != "true" || -z "$PROGRESS_COMMENT_ID" ]] && return 0
|
|
69
69
|
local body="$1"
|
|
70
70
|
gh api "repos/${REPO_OWNER}/${REPO_NAME}/issues/comments/${PROGRESS_COMMENT_ID}" \
|
|
71
|
-
-X PATCH -f body="$body" 2>/dev/null || true
|
|
71
|
+
-X PATCH -f body="$body" --timeout 30 2>/dev/null || true
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
# Add labels to an issue or PR
|
|
@@ -77,7 +77,7 @@ gh_add_labels() {
|
|
|
77
77
|
[[ "$GH_AVAILABLE" != "true" ]] && return 0
|
|
78
78
|
local issue_num="$1" labels="$2"
|
|
79
79
|
[[ -z "$labels" ]] && return 0
|
|
80
|
-
gh issue edit "$issue_num" --add-label "$labels" 2>/dev/null || true
|
|
80
|
+
_timeout 30 gh issue edit "$issue_num" --add-label "$labels" 2>/dev/null || true
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
# Remove a label from an issue
|
|
@@ -85,7 +85,7 @@ gh_add_labels() {
|
|
|
85
85
|
gh_remove_label() {
|
|
86
86
|
[[ "$GH_AVAILABLE" != "true" ]] && return 0
|
|
87
87
|
local issue_num="$1" label="$2"
|
|
88
|
-
gh issue edit "$issue_num" --remove-label "$label" 2>/dev/null || true
|
|
88
|
+
_timeout 30 gh issue edit "$issue_num" --remove-label "$label" 2>/dev/null || true
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
# Self-assign an issue
|
|
@@ -93,7 +93,7 @@ gh_remove_label() {
|
|
|
93
93
|
gh_assign_self() {
|
|
94
94
|
[[ "$GH_AVAILABLE" != "true" ]] && return 0
|
|
95
95
|
local issue_num="$1"
|
|
96
|
-
gh issue edit "$issue_num" --add-assignee "@me" 2>/dev/null || true
|
|
96
|
+
_timeout 30 gh issue edit "$issue_num" --add-assignee "@me" 2>/dev/null || true
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
# Get full issue metadata as JSON
|
|
@@ -101,7 +101,7 @@ gh_assign_self() {
|
|
|
101
101
|
gh_get_issue_meta() {
|
|
102
102
|
[[ "$GH_AVAILABLE" != "true" ]] && return 0
|
|
103
103
|
local issue_num="$1"
|
|
104
|
-
gh issue view "$issue_num" --json title,body,labels,milestone,assignees,comments,number,state 2>/dev/null || true
|
|
104
|
+
_timeout 30 gh issue view "$issue_num" --json title,body,labels,milestone,assignees,comments,number,state 2>/dev/null || true
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
# Build a progress table for GitHub comment
|
|
@@ -106,8 +106,45 @@ pipeline_should_skip_stage() {
|
|
|
106
106
|
# Returns JSON with classified findings and routing recommendations.
|
|
107
107
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
108
108
|
classify_quality_findings() {
|
|
109
|
-
local findings_dir="$ARTIFACTS_DIR"
|
|
110
|
-
local result_file="$
|
|
109
|
+
local findings_dir="${1:-$ARTIFACTS_DIR}"
|
|
110
|
+
local result_file="$findings_dir/classified-findings.json"
|
|
111
|
+
|
|
112
|
+
# Build combined content for semantic classification
|
|
113
|
+
local content=""
|
|
114
|
+
if [[ -f "$findings_dir/adversarial-review.md" ]]; then
|
|
115
|
+
content="${content}
|
|
116
|
+
--- adversarial-review.md ---
|
|
117
|
+
$(head -500 "$findings_dir/adversarial-review.md" 2>/dev/null)"
|
|
118
|
+
fi
|
|
119
|
+
if [[ -f "$findings_dir/negative-review.md" ]]; then
|
|
120
|
+
content="${content}
|
|
121
|
+
--- negative-review.md ---
|
|
122
|
+
$(head -300 "$findings_dir/negative-review.md" 2>/dev/null)"
|
|
123
|
+
fi
|
|
124
|
+
if [[ -f "$findings_dir/security-audit.log" ]]; then
|
|
125
|
+
content="${content}
|
|
126
|
+
--- security-audit.log ---
|
|
127
|
+
$(cat "$findings_dir/security-audit.log" 2>/dev/null)"
|
|
128
|
+
fi
|
|
129
|
+
if [[ -f "$findings_dir/compound-architecture-validation.json" ]]; then
|
|
130
|
+
content="${content}
|
|
131
|
+
--- compound-architecture-validation.json ---
|
|
132
|
+
$(jq -r '.[] | "\(.severity): \(.message // .description // .)"' "$findings_dir/compound-architecture-validation.json" 2>/dev/null | head -50)"
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
# Try semantic classification first when Claude is available
|
|
136
|
+
local route=""
|
|
137
|
+
if command -v claude &>/dev/null && [[ "${INTELLIGENCE_ENABLED:-false}" != "false" ]] && [[ -n "$content" ]]; then
|
|
138
|
+
local prompt="Classify these code review findings into exactly ONE primary category. Return ONLY a single word: security, architecture, correctness, performance, testing, documentation, style.
|
|
139
|
+
|
|
140
|
+
Findings:
|
|
141
|
+
$content"
|
|
142
|
+
local category
|
|
143
|
+
category=$(echo "$prompt" | timeout 30 claude -p --model sonnet 2>/dev/null | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]')
|
|
144
|
+
if [[ "$category" =~ ^(security|architecture|correctness|performance|testing|documentation|style)$ ]]; then
|
|
145
|
+
route="$category"
|
|
146
|
+
fi
|
|
147
|
+
fi
|
|
111
148
|
|
|
112
149
|
# Initialize counters
|
|
113
150
|
local arch_count=0 security_count=0 correctness_count=0 performance_count=0 testing_count=0 style_count=0
|
|
@@ -173,40 +210,53 @@ classify_quality_findings() {
|
|
|
173
210
|
fi
|
|
174
211
|
|
|
175
212
|
# ── Determine routing ──
|
|
176
|
-
#
|
|
177
|
-
local route="correctness" # default
|
|
213
|
+
# Use semantic classification when available; else fall back to grep-derived priority
|
|
178
214
|
local needs_backtrack=false
|
|
179
215
|
local priority_findings=""
|
|
180
216
|
|
|
181
|
-
if [[ "$
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
fi
|
|
217
|
+
if [[ -z "$route" ]]; then
|
|
218
|
+
# Fallback: grep-based priority order: security > architecture > correctness > performance > testing > style
|
|
219
|
+
route="correctness"
|
|
185
220
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
needs_backtrack=true
|
|
221
|
+
if [[ "$security_count" -gt 0 ]]; then
|
|
222
|
+
route="security"
|
|
223
|
+
priority_findings="security:${security_count}"
|
|
190
224
|
fi
|
|
191
|
-
priority_findings="${priority_findings:+${priority_findings},}architecture:${arch_count}"
|
|
192
|
-
fi
|
|
193
225
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
226
|
+
if [[ "$arch_count" -gt 0 ]]; then
|
|
227
|
+
if [[ "$route" == "correctness" ]]; then
|
|
228
|
+
route="architecture"
|
|
229
|
+
needs_backtrack=true
|
|
230
|
+
fi
|
|
231
|
+
priority_findings="${priority_findings:+${priority_findings},}architecture:${arch_count}"
|
|
232
|
+
fi
|
|
197
233
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
route="performance"
|
|
234
|
+
if [[ "$correctness_count" -gt 0 ]]; then
|
|
235
|
+
priority_findings="${priority_findings:+${priority_findings},}correctness:${correctness_count}"
|
|
201
236
|
fi
|
|
202
|
-
priority_findings="${priority_findings:+${priority_findings},}performance:${performance_count}"
|
|
203
|
-
fi
|
|
204
237
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
238
|
+
if [[ "$performance_count" -gt 0 ]]; then
|
|
239
|
+
if [[ "$route" == "correctness" && "$correctness_count" -eq 0 ]]; then
|
|
240
|
+
route="performance"
|
|
241
|
+
fi
|
|
242
|
+
priority_findings="${priority_findings:+${priority_findings},}performance:${performance_count}"
|
|
208
243
|
fi
|
|
209
|
-
|
|
244
|
+
|
|
245
|
+
if [[ "$testing_count" -gt 0 ]]; then
|
|
246
|
+
if [[ "$route" == "correctness" && "$correctness_count" -eq 0 && "$performance_count" -eq 0 ]]; then
|
|
247
|
+
route="testing"
|
|
248
|
+
fi
|
|
249
|
+
priority_findings="${priority_findings:+${priority_findings},}testing:${testing_count}"
|
|
250
|
+
fi
|
|
251
|
+
else
|
|
252
|
+
# Semantic route: build priority_findings from counts, set needs_backtrack for architecture
|
|
253
|
+
[[ "$route" == "architecture" ]] && needs_backtrack=true
|
|
254
|
+
[[ "$arch_count" -gt 0 ]] && priority_findings="architecture:${arch_count}"
|
|
255
|
+
[[ "$security_count" -gt 0 ]] && priority_findings="${priority_findings:+${priority_findings},}security:${security_count}"
|
|
256
|
+
[[ "$correctness_count" -gt 0 ]] && priority_findings="${priority_findings:+${priority_findings},}correctness:${correctness_count}"
|
|
257
|
+
[[ "$performance_count" -gt 0 ]] && priority_findings="${priority_findings:+${priority_findings},}performance:${performance_count}"
|
|
258
|
+
[[ "$testing_count" -gt 0 ]] && priority_findings="${priority_findings:+${priority_findings},}testing:${testing_count}"
|
|
259
|
+
[[ -z "$priority_findings" ]] && priority_findings="${route}:1"
|
|
210
260
|
fi
|
|
211
261
|
|
|
212
262
|
# Style findings don't affect routing or count toward failure threshold
|
|
@@ -749,7 +799,7 @@ pipeline_record_quality_score() {
|
|
|
749
799
|
rm -f "$tmp_score"
|
|
750
800
|
|
|
751
801
|
# Rotate quality scores file to prevent unbounded growth
|
|
752
|
-
type rotate_jsonl
|
|
802
|
+
type rotate_jsonl >/dev/null 2>&1 && rotate_jsonl "$scores_file" 5000
|
|
753
803
|
|
|
754
804
|
emit_event "pipeline.quality_score_recorded" \
|
|
755
805
|
"issue=${ISSUE_NUMBER:-0}" \
|
|
@@ -1117,7 +1167,7 @@ stage_compound_quality() {
|
|
|
1117
1167
|
|
|
1118
1168
|
# Intelligent audit selection
|
|
1119
1169
|
local audit_plan='{"adversarial":"targeted","architecture":"targeted","simulation":"targeted","security":"targeted","dod":"targeted"}'
|
|
1120
|
-
if type pipeline_select_audits
|
|
1170
|
+
if type pipeline_select_audits >/dev/null 2>&1; then
|
|
1121
1171
|
local _selected
|
|
1122
1172
|
_selected=$(pipeline_select_audits 2>/dev/null) || true
|
|
1123
1173
|
if [[ -n "$_selected" && "$_selected" != "null" ]]; then
|
|
@@ -1205,9 +1255,9 @@ stage_compound_quality() {
|
|
|
1205
1255
|
|
|
1206
1256
|
# Vitals-driven adaptive cycle limit (preferred)
|
|
1207
1257
|
local base_max_cycles="$max_cycles"
|
|
1208
|
-
if type pipeline_adaptive_limit
|
|
1258
|
+
if type pipeline_adaptive_limit >/dev/null 2>&1; then
|
|
1209
1259
|
local _cq_vitals=""
|
|
1210
|
-
if type pipeline_compute_vitals
|
|
1260
|
+
if type pipeline_compute_vitals >/dev/null 2>&1; then
|
|
1211
1261
|
_cq_vitals=$(pipeline_compute_vitals "$STATE_FILE" "$ARTIFACTS_DIR" "${ISSUE_NUMBER:-}" 2>/dev/null) || true
|
|
1212
1262
|
fi
|
|
1213
1263
|
local vitals_cq_limit
|
|
@@ -1270,7 +1320,7 @@ stage_compound_quality() {
|
|
|
1270
1320
|
fi
|
|
1271
1321
|
|
|
1272
1322
|
# 3. Developer Simulation (intelligence module)
|
|
1273
|
-
if type simulation_review
|
|
1323
|
+
if type simulation_review >/dev/null 2>&1; then
|
|
1274
1324
|
local sim_enabled
|
|
1275
1325
|
sim_enabled=$(jq -r '.intelligence.simulation_enabled // false' "$PIPELINE_CONFIG" 2>/dev/null || echo "false")
|
|
1276
1326
|
local daemon_cfg="${PROJECT_ROOT}/.claude/daemon-config.json"
|
|
@@ -1310,7 +1360,7 @@ stage_compound_quality() {
|
|
|
1310
1360
|
fi
|
|
1311
1361
|
|
|
1312
1362
|
# 4. Architecture Enforcer (intelligence module)
|
|
1313
|
-
if type architecture_validate_changes
|
|
1363
|
+
if type architecture_validate_changes >/dev/null 2>&1; then
|
|
1314
1364
|
local arch_enabled
|
|
1315
1365
|
arch_enabled=$(jq -r '.intelligence.architecture_enabled // false' "$PIPELINE_CONFIG" 2>/dev/null || echo "false")
|
|
1316
1366
|
local daemon_cfg="${PROJECT_ROOT}/.claude/daemon-config.json"
|
|
@@ -1474,7 +1524,7 @@ All quality checks clean:
|
|
|
1474
1524
|
|
|
1475
1525
|
# DoD verification on successful pass
|
|
1476
1526
|
local _dod_pass_rate=100
|
|
1477
|
-
if type pipeline_verify_dod
|
|
1527
|
+
if type pipeline_verify_dod >/dev/null 2>&1; then
|
|
1478
1528
|
pipeline_verify_dod "$ARTIFACTS_DIR" 2>/dev/null || true
|
|
1479
1529
|
if [[ -f "$ARTIFACTS_DIR/dod-verification.json" ]]; then
|
|
1480
1530
|
_dod_pass_rate=$(jq -r '.pass_rate // 100' "$ARTIFACTS_DIR/dod-verification.json" 2>/dev/null || echo "100")
|
|
@@ -1496,7 +1546,7 @@ All quality checks clean:
|
|
|
1496
1546
|
|
|
1497
1547
|
# DoD verification on successful pass
|
|
1498
1548
|
local _dod_pass_rate=100
|
|
1499
|
-
if type pipeline_verify_dod
|
|
1549
|
+
if type pipeline_verify_dod >/dev/null 2>&1; then
|
|
1500
1550
|
pipeline_verify_dod "$ARTIFACTS_DIR" 2>/dev/null || true
|
|
1501
1551
|
if [[ -f "$ARTIFACTS_DIR/dod-verification.json" ]]; then
|
|
1502
1552
|
_dod_pass_rate=$(jq -r '.pass_rate // 100' "$ARTIFACTS_DIR/dod-verification.json" 2>/dev/null || echo "100")
|
|
@@ -1590,7 +1640,7 @@ All quality checks clean:
|
|
|
1590
1640
|
|
|
1591
1641
|
# DoD verification
|
|
1592
1642
|
local _dod_pass_rate=0
|
|
1593
|
-
if type pipeline_verify_dod
|
|
1643
|
+
if type pipeline_verify_dod >/dev/null 2>&1; then
|
|
1594
1644
|
pipeline_verify_dod "$ARTIFACTS_DIR" 2>/dev/null || true
|
|
1595
1645
|
if [[ -f "$ARTIFACTS_DIR/dod-verification.json" ]]; then
|
|
1596
1646
|
_dod_pass_rate=$(jq -r '.pass_rate // 0' "$ARTIFACTS_DIR/dod-verification.json" 2>/dev/null || echo "0")
|
|
@@ -10,15 +10,15 @@ quality_check_security() {
|
|
|
10
10
|
local tool_found=false
|
|
11
11
|
|
|
12
12
|
# Try npm audit
|
|
13
|
-
if [[ -f "package.json" ]] && command -v npm
|
|
13
|
+
if [[ -f "package.json" ]] && command -v npm >/dev/null 2>&1; then
|
|
14
14
|
tool_found=true
|
|
15
15
|
npm audit --production 2>&1 | tee "$audit_log" || audit_exit=$?
|
|
16
16
|
# Try pip-audit
|
|
17
|
-
elif [[ -f "requirements.txt" || -f "pyproject.toml" ]] && command -v pip-audit
|
|
17
|
+
elif [[ -f "requirements.txt" || -f "pyproject.toml" ]] && command -v pip-audit >/dev/null 2>&1; then
|
|
18
18
|
tool_found=true
|
|
19
19
|
pip-audit 2>&1 | tee "$audit_log" || audit_exit=$?
|
|
20
20
|
# Try cargo audit
|
|
21
|
-
elif [[ -f "Cargo.toml" ]] && command -v cargo-audit
|
|
21
|
+
elif [[ -f "Cargo.toml" ]] && command -v cargo-audit >/dev/null 2>&1; then
|
|
22
22
|
tool_found=true
|
|
23
23
|
cargo audit 2>&1 | tee "$audit_log" || audit_exit=$?
|
|
24
24
|
fi
|
|
@@ -170,7 +170,7 @@ quality_check_bundle_size() {
|
|
|
170
170
|
mv "$tmp_bundle_hist" "$bundle_history_file" 2>/dev/null || true
|
|
171
171
|
|
|
172
172
|
# Intelligence: identify top dependency bloaters
|
|
173
|
-
if type intelligence_search_memory
|
|
173
|
+
if type intelligence_search_memory >/dev/null 2>&1 && [[ -f "package.json" ]] && command -v jq >/dev/null 2>&1; then
|
|
174
174
|
local dep_sizes=""
|
|
175
175
|
local deps
|
|
176
176
|
deps=$(jq -r '.dependencies // {} | keys[]' package.json 2>/dev/null || true)
|
|
@@ -237,7 +237,7 @@ quality_check_perf_regression() {
|
|
|
237
237
|
if [[ -f "$daemon_cfg" ]]; then
|
|
238
238
|
intel_enabled=$(jq -r '.intelligence.enabled // false' "$daemon_cfg" 2>/dev/null || echo "false")
|
|
239
239
|
fi
|
|
240
|
-
if [[ "$intel_enabled" == "true" ]] && command -v claude
|
|
240
|
+
if [[ "$intel_enabled" == "true" ]] && command -v claude >/dev/null 2>&1; then
|
|
241
241
|
local tail_output
|
|
242
242
|
tail_output=$(tail -30 "$test_log" 2>/dev/null || true)
|
|
243
243
|
if [[ -n "$tail_output" ]]; then
|
|
@@ -378,7 +378,7 @@ quality_check_api_compat() {
|
|
|
378
378
|
|
|
379
379
|
# Check for breaking changes: removed endpoints, changed methods
|
|
380
380
|
local removed_endpoints=""
|
|
381
|
-
if command -v jq
|
|
381
|
+
if command -v jq >/dev/null 2>&1 && [[ "$spec_file" == *.json ]]; then
|
|
382
382
|
local old_paths new_paths
|
|
383
383
|
old_paths=$(echo "$old_spec" | jq -r '.paths | keys[]' 2>/dev/null | sort || true)
|
|
384
384
|
new_paths=$(jq -r '.paths | keys[]' "$spec_file" 2>/dev/null | sort || true)
|
|
@@ -387,7 +387,7 @@ quality_check_api_compat() {
|
|
|
387
387
|
|
|
388
388
|
# Enhanced schema diff: parameter changes, response schema, auth changes
|
|
389
389
|
local param_changes="" schema_changes=""
|
|
390
|
-
if command -v jq
|
|
390
|
+
if command -v jq >/dev/null 2>&1 && [[ "$spec_file" == *.json ]]; then
|
|
391
391
|
# Detect parameter changes on existing endpoints
|
|
392
392
|
local common_paths
|
|
393
393
|
common_paths=$(comm -12 <(echo "$old_spec" | jq -r '.paths | keys[]' 2>/dev/null | sort) <(jq -r '.paths | keys[]' "$spec_file" 2>/dev/null | sort) 2>/dev/null || true)
|
|
@@ -407,7 +407,7 @@ quality_check_api_compat() {
|
|
|
407
407
|
|
|
408
408
|
# Intelligence: semantic API diff for complex changes
|
|
409
409
|
local semantic_diff=""
|
|
410
|
-
if type intelligence_search_memory
|
|
410
|
+
if type intelligence_search_memory >/dev/null 2>&1 && command -v claude >/dev/null 2>&1; then
|
|
411
411
|
local spec_git_diff
|
|
412
412
|
spec_git_diff=$(git diff "${BASE_BRANCH}...HEAD" -- "$spec_file" 2>/dev/null | head -200 || true)
|
|
413
413
|
if [[ -n "$spec_git_diff" ]]; then
|
|
@@ -441,7 +441,7 @@ ${spec_git_diff}" --model haiku < /dev/null 2>/dev/null || true)
|
|
|
441
441
|
if [[ -n "$removed_endpoints" || -n "$param_changes" ]]; then
|
|
442
442
|
local issue_count=0
|
|
443
443
|
[[ -n "$removed_endpoints" ]] && issue_count=$((issue_count + $(echo "$removed_endpoints" | wc -l | xargs)))
|
|
444
|
-
[[ -n "$param_changes" ]] && issue_count=$((issue_count + $(echo "$param_changes" | grep -c '.' ||
|
|
444
|
+
[[ -n "$param_changes" ]] && issue_count=$((issue_count + $(echo "$param_changes" | grep -c '.' 2>/dev/null || echo "0")))
|
|
445
445
|
warn "API breaking changes: ${issue_count} issue(s) found"
|
|
446
446
|
return 1
|
|
447
447
|
fi
|
|
@@ -470,7 +470,7 @@ quality_check_coverage() {
|
|
|
470
470
|
if [[ -f "$daemon_cfg_cov" ]]; then
|
|
471
471
|
intel_enabled_cov=$(jq -r '.intelligence.enabled // false' "$daemon_cfg_cov" 2>/dev/null || echo "false")
|
|
472
472
|
fi
|
|
473
|
-
if [[ "$intel_enabled_cov" == "true" ]] && command -v claude
|
|
473
|
+
if [[ "$intel_enabled_cov" == "true" ]] && command -v claude >/dev/null 2>&1; then
|
|
474
474
|
local tail_cov_output
|
|
475
475
|
tail_cov_output=$(tail -40 "$test_log" 2>/dev/null || true)
|
|
476
476
|
if [[ -n "$tail_cov_output" ]]; then
|
|
@@ -559,7 +559,7 @@ run_adversarial_review() {
|
|
|
559
559
|
fi
|
|
560
560
|
|
|
561
561
|
# Delegate to sw-adversarial.sh module when available (uses intelligence cache)
|
|
562
|
-
if type adversarial_review
|
|
562
|
+
if type adversarial_review >/dev/null 2>&1; then
|
|
563
563
|
info "Using intelligence-backed adversarial review..."
|
|
564
564
|
local json_result
|
|
565
565
|
json_result=$(adversarial_review "$diff_content" "${GOAL:-}" 2>/dev/null || echo "[]")
|
|
@@ -605,7 +605,7 @@ run_adversarial_review() {
|
|
|
605
605
|
|
|
606
606
|
# Inject previous adversarial findings from memory
|
|
607
607
|
local adv_memory=""
|
|
608
|
-
if type intelligence_search_memory
|
|
608
|
+
if type intelligence_search_memory >/dev/null 2>&1; then
|
|
609
609
|
adv_memory=$(intelligence_search_memory "adversarial review security findings for: ${GOAL:-}" "${HOME}/.shipwright/memory" 5 2>/dev/null) || true
|
|
610
610
|
fi
|
|
611
611
|
|
|
@@ -676,7 +676,7 @@ $(head -200 "$file" 2>/dev/null || true)
|
|
|
676
676
|
|
|
677
677
|
# Inject previous negative prompting findings from memory
|
|
678
678
|
local neg_memory=""
|
|
679
|
-
if type intelligence_search_memory
|
|
679
|
+
if type intelligence_search_memory >/dev/null 2>&1; then
|
|
680
680
|
neg_memory=$(intelligence_search_memory "negative prompting findings common concerns for: ${GOAL:-}" "${HOME}/.shipwright/memory" 5 2>/dev/null) || true
|
|
681
681
|
fi
|
|
682
682
|
|
|
@@ -853,9 +853,9 @@ run_bash_compat_check() {
|
|
|
853
853
|
while IFS= read -r filepath; do
|
|
854
854
|
[[ -z "$filepath" ]] && continue
|
|
855
855
|
|
|
856
|
-
# declare -A (associative arrays)
|
|
856
|
+
# declare -A (associative arrays; declare -a is bash 3.2 compatible)
|
|
857
857
|
local declare_a_count
|
|
858
|
-
declare_a_count=$(grep -c 'declare[[:space:]]*-
|
|
858
|
+
declare_a_count=$(grep -c 'declare[[:space:]]*-A' "$filepath" 2>/dev/null || true)
|
|
859
859
|
if [[ "$declare_a_count" -gt 0 ]]; then
|
|
860
860
|
violations=$((violations + declare_a_count))
|
|
861
861
|
violation_details="${violation_details}${filepath}: declare -A (${declare_a_count} occurrences)
|
|
@@ -990,7 +990,7 @@ run_atomic_write_check() {
|
|
|
990
990
|
|
|
991
991
|
# Check for direct redirection writes (> file) in state/config paths
|
|
992
992
|
local bad_writes
|
|
993
|
-
bad_writes=$(git show "HEAD:$filepath" 2>/dev/null | grep -c 'echo.*>'
|
|
993
|
+
bad_writes=$(git show "HEAD:$filepath" 2>/dev/null | grep -c 'echo.*>' 2>/dev/null || echo "0")
|
|
994
994
|
|
|
995
995
|
if [[ "$bad_writes" -gt 0 ]]; then
|
|
996
996
|
violations=$((violations + bad_writes))
|
|
@@ -5,7 +5,7 @@ _PIPELINE_QUALITY_LOADED=1
|
|
|
5
5
|
|
|
6
6
|
# Policy overrides when config/policy.json exists
|
|
7
7
|
[[ -f "${SCRIPT_DIR:-}/lib/policy.sh" ]] && source "${SCRIPT_DIR:-}/lib/policy.sh"
|
|
8
|
-
if type policy_get
|
|
8
|
+
if type policy_get >/dev/null 2>&1; then
|
|
9
9
|
PIPELINE_COVERAGE_THRESHOLD=$(policy_get ".pipeline.coverage_threshold_percent" "60")
|
|
10
10
|
PIPELINE_QUALITY_GATE_THRESHOLD=$(policy_get ".pipeline.quality_gate_score_threshold" "70")
|
|
11
11
|
QUALITY_COVERAGE_THRESHOLD=$(policy_get ".quality.coverage_threshold" "70")
|