shipwright-cli 2.4.0 → 3.1.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 +248 -94
- package/completions/shipwright.bash +68 -19
- package/completions/shipwright.fish +310 -42
- package/config/decision-tiers.json +55 -0
- package/config/defaults.json +111 -0
- package/config/event-schema.json +218 -0
- package/config/policy.json +21 -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 +7 -9
- 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 +127 -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 +63 -17
- package/scripts/lib/daemon-failure.sh +0 -0
- package/scripts/lib/daemon-health.sh +1 -1
- package/scripts/lib/daemon-patrol.sh +64 -17
- package/scripts/lib/daemon-poll.sh +54 -25
- package/scripts/lib/daemon-state.sh +125 -23
- package/scripts/lib/daemon-triage.sh +31 -9
- 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 +63 -0
- package/scripts/lib/helpers.sh +29 -6
- package/scripts/lib/pipeline-detection.sh +2 -2
- package/scripts/lib/pipeline-github.sh +9 -9
- package/scripts/lib/pipeline-intelligence.sh +105 -38
- package/scripts/lib/pipeline-quality-checks.sh +17 -16
- package/scripts/lib/pipeline-quality.sh +1 -1
- package/scripts/lib/pipeline-stages.sh +440 -59
- package/scripts/lib/pipeline-state.sh +54 -4
- package/scripts/lib/policy.sh +0 -0
- package/scripts/lib/test-helpers.sh +247 -0
- package/scripts/postinstall.mjs +78 -12
- package/scripts/signals/example-collector.sh +36 -0
- package/scripts/sw +17 -7
- 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 +17 -18
- package/scripts/sw-daemon.sh +76 -71
- package/scripts/sw-dashboard.sh +57 -17
- package/scripts/sw-db.sh +524 -26
- package/scripts/sw-decide.sh +685 -0
- 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 +138 -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 +368 -53
- 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 +905 -104
- package/scripts/sw-memory.sh +263 -20
- package/scripts/sw-mission-control.sh +2 -12
- package/scripts/sw-model-router.sh +73 -34
- package/scripts/sw-otel.sh +15 -23
- 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 +550 -122
- package/scripts/sw-pm.sh +2 -12
- package/scripts/sw-pr-lifecycle.sh +33 -28
- 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 +85 -14
- 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 +174 -41
- package/scripts/sw-security-audit.sh +12 -22
- package/scripts/sw-self-optimize.sh +239 -23
- package/scripts/sw-session.sh +5 -15
- 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 +29 -39
- 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 +73 -0
- package/templates/pipelines/tdd.json +72 -0
- package/scripts/sw-pipeline.sh.mock +0 -7
|
@@ -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
|
-
|
|
234
|
+
if [[ "$correctness_count" -gt 0 ]]; then
|
|
235
|
+
priority_findings="${priority_findings:+${priority_findings},}correctness:${correctness_count}"
|
|
236
|
+
fi
|
|
237
|
+
|
|
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}"
|
|
201
243
|
fi
|
|
202
|
-
priority_findings="${priority_findings:+${priority_findings},}performance:${performance_count}"
|
|
203
|
-
fi
|
|
204
244
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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}"
|
|
208
250
|
fi
|
|
209
|
-
|
|
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}" \
|
|
@@ -1098,7 +1148,16 @@ stage_compound_quality() {
|
|
|
1098
1148
|
_cq_real_changes=$(git diff --name-only "origin/${BASE_BRANCH:-main}...HEAD" \
|
|
1099
1149
|
-- . ':!.claude/loop-state.md' ':!.claude/pipeline-state.md' \
|
|
1100
1150
|
':!.claude/pipeline-artifacts/*' ':!**/progress.md' \
|
|
1101
|
-
':!**/error-summary.json' 2>/dev/null | wc -l
|
|
1151
|
+
':!**/error-summary.json' 2>/dev/null | wc -l || echo "0")
|
|
1152
|
+
_cq_real_changes=$(echo "$_cq_real_changes" | tr -d '[:space:]')
|
|
1153
|
+
[[ -z "$_cq_real_changes" ]] && _cq_real_changes=0
|
|
1154
|
+
# Fallback: if no remote, compare against first commit
|
|
1155
|
+
if [[ "$_cq_real_changes" -eq 0 ]] 2>/dev/null; then
|
|
1156
|
+
_cq_real_changes=$(git diff --name-only "$(git rev-list --max-parents=0 HEAD 2>/dev/null)...HEAD" \
|
|
1157
|
+
-- . ':!.claude/*' ':!**/progress.md' ':!**/error-summary.json' 2>/dev/null | wc -l || echo "0")
|
|
1158
|
+
_cq_real_changes=$(echo "$_cq_real_changes" | tr -d '[:space:]')
|
|
1159
|
+
[[ -z "$_cq_real_changes" ]] && _cq_real_changes=0
|
|
1160
|
+
fi
|
|
1102
1161
|
if [[ "${_cq_real_changes:-0}" -eq 0 ]]; then
|
|
1103
1162
|
error "Compound quality: no meaningful code changes found — failing quality gate"
|
|
1104
1163
|
return 1
|
|
@@ -1117,7 +1176,7 @@ stage_compound_quality() {
|
|
|
1117
1176
|
|
|
1118
1177
|
# Intelligent audit selection
|
|
1119
1178
|
local audit_plan='{"adversarial":"targeted","architecture":"targeted","simulation":"targeted","security":"targeted","dod":"targeted"}'
|
|
1120
|
-
if type pipeline_select_audits
|
|
1179
|
+
if type pipeline_select_audits >/dev/null 2>&1; then
|
|
1121
1180
|
local _selected
|
|
1122
1181
|
_selected=$(pipeline_select_audits 2>/dev/null) || true
|
|
1123
1182
|
if [[ -n "$_selected" && "$_selected" != "null" ]]; then
|
|
@@ -1157,8 +1216,11 @@ stage_compound_quality() {
|
|
|
1157
1216
|
|
|
1158
1217
|
# 2. Test coverage check
|
|
1159
1218
|
local coverage_pct=0
|
|
1160
|
-
coverage_pct=$(run_test_coverage_check 2>/dev/null) || coverage_pct=0
|
|
1219
|
+
coverage_pct=$(run_test_coverage_check 2>/dev/null | tr -d '[:space:][:cntrl:]') || coverage_pct=0
|
|
1161
1220
|
coverage_pct="${coverage_pct:-0}"
|
|
1221
|
+
# Sanitize: strip anything non-numeric (ANSI codes, whitespace, etc.)
|
|
1222
|
+
coverage_pct=$(echo "$coverage_pct" | sed 's/[^0-9]//g')
|
|
1223
|
+
[[ -z "$coverage_pct" ]] && coverage_pct=0
|
|
1162
1224
|
|
|
1163
1225
|
if [[ "$coverage_pct" != "skip" ]]; then
|
|
1164
1226
|
if [[ "$coverage_pct" -lt "${PIPELINE_COVERAGE_THRESHOLD:-60}" ]]; then
|
|
@@ -1204,16 +1266,21 @@ stage_compound_quality() {
|
|
|
1204
1266
|
fi
|
|
1205
1267
|
|
|
1206
1268
|
# Vitals-driven adaptive cycle limit (preferred)
|
|
1269
|
+
# Respect the template's max_cycles as a ceiling — vitals can only reduce, not inflate
|
|
1207
1270
|
local base_max_cycles="$max_cycles"
|
|
1208
|
-
|
|
1271
|
+
local template_max_cycles="$max_cycles"
|
|
1272
|
+
if type pipeline_adaptive_limit >/dev/null 2>&1; then
|
|
1209
1273
|
local _cq_vitals=""
|
|
1210
|
-
if type pipeline_compute_vitals
|
|
1274
|
+
if type pipeline_compute_vitals >/dev/null 2>&1; then
|
|
1211
1275
|
_cq_vitals=$(pipeline_compute_vitals "$STATE_FILE" "$ARTIFACTS_DIR" "${ISSUE_NUMBER:-}" 2>/dev/null) || true
|
|
1212
1276
|
fi
|
|
1213
1277
|
local vitals_cq_limit
|
|
1214
1278
|
vitals_cq_limit=$(pipeline_adaptive_limit "compound_quality" "$_cq_vitals" 2>/dev/null) || true
|
|
1215
1279
|
if [[ -n "$vitals_cq_limit" && "$vitals_cq_limit" =~ ^[0-9]+$ && "$vitals_cq_limit" -gt 0 ]]; then
|
|
1216
|
-
|
|
1280
|
+
# Cap at template max — don't let vitals override the pipeline template's intent
|
|
1281
|
+
if [[ "$vitals_cq_limit" -le "$template_max_cycles" ]]; then
|
|
1282
|
+
max_cycles="$vitals_cq_limit"
|
|
1283
|
+
fi
|
|
1217
1284
|
if [[ "$max_cycles" != "$base_max_cycles" ]]; then
|
|
1218
1285
|
info "Vitals-driven cycles: ${base_max_cycles} → ${max_cycles} (compound_quality)"
|
|
1219
1286
|
fi
|
|
@@ -1270,7 +1337,7 @@ stage_compound_quality() {
|
|
|
1270
1337
|
fi
|
|
1271
1338
|
|
|
1272
1339
|
# 3. Developer Simulation (intelligence module)
|
|
1273
|
-
if type simulation_review
|
|
1340
|
+
if type simulation_review >/dev/null 2>&1; then
|
|
1274
1341
|
local sim_enabled
|
|
1275
1342
|
sim_enabled=$(jq -r '.intelligence.simulation_enabled // false' "$PIPELINE_CONFIG" 2>/dev/null || echo "false")
|
|
1276
1343
|
local daemon_cfg="${PROJECT_ROOT}/.claude/daemon-config.json"
|
|
@@ -1310,7 +1377,7 @@ stage_compound_quality() {
|
|
|
1310
1377
|
fi
|
|
1311
1378
|
|
|
1312
1379
|
# 4. Architecture Enforcer (intelligence module)
|
|
1313
|
-
if type architecture_validate_changes
|
|
1380
|
+
if type architecture_validate_changes >/dev/null 2>&1; then
|
|
1314
1381
|
local arch_enabled
|
|
1315
1382
|
arch_enabled=$(jq -r '.intelligence.architecture_enabled // false' "$PIPELINE_CONFIG" 2>/dev/null || echo "false")
|
|
1316
1383
|
local daemon_cfg="${PROJECT_ROOT}/.claude/daemon-config.json"
|
|
@@ -1474,7 +1541,7 @@ All quality checks clean:
|
|
|
1474
1541
|
|
|
1475
1542
|
# DoD verification on successful pass
|
|
1476
1543
|
local _dod_pass_rate=100
|
|
1477
|
-
if type pipeline_verify_dod
|
|
1544
|
+
if type pipeline_verify_dod >/dev/null 2>&1; then
|
|
1478
1545
|
pipeline_verify_dod "$ARTIFACTS_DIR" 2>/dev/null || true
|
|
1479
1546
|
if [[ -f "$ARTIFACTS_DIR/dod-verification.json" ]]; then
|
|
1480
1547
|
_dod_pass_rate=$(jq -r '.pass_rate // 100' "$ARTIFACTS_DIR/dod-verification.json" 2>/dev/null || echo "100")
|
|
@@ -1496,7 +1563,7 @@ All quality checks clean:
|
|
|
1496
1563
|
|
|
1497
1564
|
# DoD verification on successful pass
|
|
1498
1565
|
local _dod_pass_rate=100
|
|
1499
|
-
if type pipeline_verify_dod
|
|
1566
|
+
if type pipeline_verify_dod >/dev/null 2>&1; then
|
|
1500
1567
|
pipeline_verify_dod "$ARTIFACTS_DIR" 2>/dev/null || true
|
|
1501
1568
|
if [[ -f "$ARTIFACTS_DIR/dod-verification.json" ]]; then
|
|
1502
1569
|
_dod_pass_rate=$(jq -r '.pass_rate // 100' "$ARTIFACTS_DIR/dod-verification.json" 2>/dev/null || echo "100")
|
|
@@ -1590,7 +1657,7 @@ All quality checks clean:
|
|
|
1590
1657
|
|
|
1591
1658
|
# DoD verification
|
|
1592
1659
|
local _dod_pass_rate=0
|
|
1593
|
-
if type pipeline_verify_dod
|
|
1660
|
+
if type pipeline_verify_dod >/dev/null 2>&1; then
|
|
1594
1661
|
pipeline_verify_dod "$ARTIFACTS_DIR" 2>/dev/null || true
|
|
1595
1662
|
if [[ -f "$ARTIFACTS_DIR/dod-verification.json" ]]; then
|
|
1596
1663
|
_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 '.' || true)))
|
|
444
|
+
[[ -n "$param_changes" ]] && issue_count=$((issue_count + $(echo "$param_changes" | grep -c '.' 2>/dev/null || true)))
|
|
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,8 @@ 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 || true)
|
|
994
|
+
bad_writes="${bad_writes:-0}"
|
|
994
995
|
|
|
995
996
|
if [[ "$bad_writes" -gt 0 ]]; then
|
|
996
997
|
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")
|