shipwright-cli 3.1.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 +22 -8
- 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/config/defaults.json +25 -2
- package/config/policy.json +1 -1
- package/dashboard/middleware/auth.ts +134 -0
- package/dashboard/middleware/constants.ts +21 -0
- package/dashboard/public/index.html +8 -6
- package/dashboard/public/styles.css +176 -97
- package/dashboard/routes/auth.ts +38 -0
- package/dashboard/server.ts +117 -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/api.ts +5 -0
- 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 +12 -1
- package/dashboard/src/views/activity.ts +2 -1
- package/dashboard/src/views/metrics.ts +69 -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 +14 -2
- package/scripts/lib/daemon-dispatch.sh +106 -17
- package/scripts/lib/daemon-failure.sh +34 -4
- package/scripts/lib/daemon-patrol.sh +25 -4
- 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 +119 -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 +180 -5
- 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 +101 -3
- 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 +104 -1138
- package/scripts/lib/pipeline-quality-bash-compat.sh +182 -0
- package/scripts/lib/pipeline-quality-checks.sh +17 -711
- 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 +161 -2901
- 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 -8
- package/scripts/sw-adaptive.sh +8 -7
- 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 +15 -6
- package/scripts/sw-cleanup.sh +4 -26
- package/scripts/sw-code-review.sh +45 -20
- package/scripts/sw-connect.sh +2 -1
- package/scripts/sw-context.sh +2 -1
- package/scripts/sw-cost.sh +107 -5
- package/scripts/sw-daemon.sh +71 -11
- package/scripts/sw-dashboard.sh +3 -1
- package/scripts/sw-db.sh +71 -20
- 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 +378 -5
- 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 +12 -7
- package/scripts/sw-e2e-orchestrator.sh +17 -16
- package/scripts/sw-eventbus.sh +13 -4
- 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 +9 -4
- package/scripts/sw-fleet.sh +5 -1
- package/scripts/sw-github-app.sh +18 -4
- 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 +10 -3
- package/scripts/sw-incident.sh +273 -5
- package/scripts/sw-init.sh +18 -2
- package/scripts/sw-instrument.sh +10 -2
- package/scripts/sw-intelligence.sh +44 -7
- 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 +436 -1076
- package/scripts/sw-memory.sh +357 -3
- package/scripts/sw-mission-control.sh +6 -1
- package/scripts/sw-model-router.sh +483 -27
- package/scripts/sw-otel.sh +15 -4
- package/scripts/sw-oversight.sh +14 -5
- package/scripts/sw-patrol-meta.sh +334 -0
- package/scripts/sw-pipeline-composer.sh +7 -1
- package/scripts/sw-pipeline-vitals.sh +12 -6
- package/scripts/sw-pipeline.sh +54 -2653
- package/scripts/sw-pm.sh +16 -8
- package/scripts/sw-pr-lifecycle.sh +2 -1
- package/scripts/sw-predictive.sh +17 -5
- package/scripts/sw-prep.sh +185 -2
- package/scripts/sw-ps.sh +5 -25
- package/scripts/sw-public-dashboard.sh +17 -4
- package/scripts/sw-quality.sh +14 -6
- 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 +14 -5
- package/scripts/sw-security-audit.sh +6 -1
- package/scripts/sw-self-optimize.sh +173 -6
- 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 +14 -6
- package/scripts/sw-stream.sh +13 -4
- package/scripts/sw-swarm.sh +20 -7
- package/scripts/sw-team-stages.sh +13 -6
- package/scripts/sw-templates.sh +7 -31
- package/scripts/sw-testgen.sh +17 -6
- 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 +37 -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 +3 -2
- package/scripts/sw-upgrade.sh +3 -1
- package/scripts/sw-ux.sh +5 -2
- package/scripts/sw-webhook.sh +5 -2
- package/scripts/sw-widgets.sh +9 -4
- 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
package/scripts/sw-incident.sh
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
# shellcheck disable=SC2034
|
|
10
|
+
VERSION="3.3.0"
|
|
10
11
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
12
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
13
|
|
|
@@ -41,6 +42,7 @@ format_duration() {
|
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
# ─── Structured Event Log ──────────────────────────────────────────────────
|
|
45
|
+
# shellcheck disable=SC2034
|
|
44
46
|
EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
|
|
45
47
|
|
|
46
48
|
# ─── State Directories ──────────────────────────────────────────────────────
|
|
@@ -53,6 +55,7 @@ ensure_incident_dir() {
|
|
|
53
55
|
[[ -f "$INCIDENT_CONFIG" ]] || cat > "$INCIDENT_CONFIG" << 'EOF'
|
|
54
56
|
{
|
|
55
57
|
"auto_response_enabled": true,
|
|
58
|
+
"auto_remediate_enabled": false,
|
|
56
59
|
"p0_auto_hotfix": true,
|
|
57
60
|
"p1_auto_hotfix": false,
|
|
58
61
|
"auto_rollback_enabled": false,
|
|
@@ -63,6 +66,12 @@ ensure_incident_dir() {
|
|
|
63
66
|
"p1_test_regression_count": 5,
|
|
64
67
|
"p1_pipeline_failure_rate": 0.3
|
|
65
68
|
},
|
|
69
|
+
"escalation": {
|
|
70
|
+
"p0": ["create_issue", "trigger_hotfix_pipeline", "emit_urgent_event"],
|
|
71
|
+
"p1": ["create_issue", "trigger_pipeline"],
|
|
72
|
+
"p2": ["create_issue"],
|
|
73
|
+
"p3": ["memory_only"]
|
|
74
|
+
},
|
|
66
75
|
"root_cause_patterns": {
|
|
67
76
|
"timeout_keywords": ["timeout", "deadline", "too slow"],
|
|
68
77
|
"memory_keywords": ["out of memory", "OOM", "heap"],
|
|
@@ -130,13 +139,18 @@ classify_severity() {
|
|
|
130
139
|
|
|
131
140
|
analyze_root_cause() {
|
|
132
141
|
local failure_log="$1"
|
|
142
|
+
# shellcheck disable=SC2034
|
|
133
143
|
local config="$2"
|
|
134
144
|
|
|
135
145
|
local timeout_hits error_hits memory_hits dependency_hits
|
|
136
|
-
timeout_hits=$(echo "$failure_log" | grep -ic "timeout\|deadline\|too slow" ||
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
146
|
+
timeout_hits=$(echo "$failure_log" | grep -ic "timeout\|deadline\|too slow" || true)
|
|
147
|
+
timeout_hits="${timeout_hits:-0}"
|
|
148
|
+
memory_hits=$(echo "$failure_log" | grep -ic "out of memory\|OOM\|heap" || true)
|
|
149
|
+
memory_hits="${memory_hits:-0}"
|
|
150
|
+
dependency_hits=$(echo "$failure_log" | grep -ic "dependency\|import\|require\|not found" || true)
|
|
151
|
+
dependency_hits="${dependency_hits:-0}"
|
|
152
|
+
error_hits=$(echo "$failure_log" | grep -c . || true)
|
|
153
|
+
error_hits="${error_hits:-0}"
|
|
140
154
|
|
|
141
155
|
if [[ "$timeout_hits" -gt 0 ]]; then
|
|
142
156
|
echo "Performance degradation: Timeout detected (${timeout_hits} occurrences)"
|
|
@@ -247,6 +261,258 @@ trigger_rollback_for_incident() {
|
|
|
247
261
|
emit_event "incident.rollback_triggered" "incident_id=$incident_id" "reason=$reason"
|
|
248
262
|
}
|
|
249
263
|
|
|
264
|
+
# ─── Incident Timeline ─────────────────────────────────────────────────
|
|
265
|
+
|
|
266
|
+
incident_timeline_update() {
|
|
267
|
+
local incident_id="$1"
|
|
268
|
+
local action="$2"
|
|
269
|
+
local details="${3:-}"
|
|
270
|
+
local actor="${4:-agent}"
|
|
271
|
+
|
|
272
|
+
local incident_file="${INCIDENTS_DIR}/${incident_id}.json"
|
|
273
|
+
[[ ! -f "$incident_file" ]] && return 1
|
|
274
|
+
|
|
275
|
+
local timestamp
|
|
276
|
+
timestamp=$(now_iso)
|
|
277
|
+
|
|
278
|
+
local timeline_entry
|
|
279
|
+
timeline_entry=$(jq -n \
|
|
280
|
+
--arg ts "$timestamp" \
|
|
281
|
+
--arg action "$action" \
|
|
282
|
+
--arg details "$details" \
|
|
283
|
+
--arg actor "$actor" \
|
|
284
|
+
'{timestamp: $ts, action: $action, details: $details, actor: $actor}')
|
|
285
|
+
|
|
286
|
+
jq --argjson entry "$timeline_entry" '.timeline += [$entry]' "$incident_file" > "${incident_file}.tmp" && \
|
|
287
|
+
mv "${incident_file}.tmp" "$incident_file"
|
|
288
|
+
|
|
289
|
+
emit_event "incident.timeline_updated" "incident_id=$incident_id" "action=$action" "actor=$actor"
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
# ─── Intelligent Root Cause Analysis ─────────────────────────────────
|
|
293
|
+
|
|
294
|
+
incident_deep_analysis() {
|
|
295
|
+
local incident_id="$1"
|
|
296
|
+
local failure_log="$2"
|
|
297
|
+
|
|
298
|
+
local incident_file="${INCIDENTS_DIR}/${incident_id}.json"
|
|
299
|
+
[[ ! -f "$incident_file" ]] && return 1
|
|
300
|
+
|
|
301
|
+
local probable_cause="Unknown cause"
|
|
302
|
+
local confidence="low"
|
|
303
|
+
local affected_components="unknown"
|
|
304
|
+
local suggested_fix="Review logs and recent commits"
|
|
305
|
+
|
|
306
|
+
# Fallback to keyword-based analysis
|
|
307
|
+
probable_cause=$(analyze_root_cause "$failure_log" "$INCIDENT_CONFIG")
|
|
308
|
+
|
|
309
|
+
local deep_analysis
|
|
310
|
+
deep_analysis=$(jq -n \
|
|
311
|
+
--arg cause "$probable_cause" \
|
|
312
|
+
--arg conf "$confidence" \
|
|
313
|
+
--arg comps "$affected_components" \
|
|
314
|
+
--arg fix "$suggested_fix" \
|
|
315
|
+
'{probable_cause: $cause, confidence: $conf, affected_components: $comps, suggested_fix: $fix}')
|
|
316
|
+
|
|
317
|
+
jq --argjson analysis "$deep_analysis" '.deep_analysis = $analysis' "$incident_file" > "${incident_file}.tmp" && \
|
|
318
|
+
mv "${incident_file}.tmp" "$incident_file"
|
|
319
|
+
|
|
320
|
+
echo "$deep_analysis"
|
|
321
|
+
emit_event "incident.deep_analysis_complete" "incident_id=$incident_id" "confidence=$confidence"
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
# ─── Incident Correlation Engine ────────────────────────────────────
|
|
325
|
+
|
|
326
|
+
incident_correlate() {
|
|
327
|
+
local time_window_secs="${1:-300}"
|
|
328
|
+
|
|
329
|
+
local current_epoch
|
|
330
|
+
current_epoch=$(now_epoch)
|
|
331
|
+
local window_start=$((current_epoch - time_window_secs))
|
|
332
|
+
|
|
333
|
+
local grouped="{}"
|
|
334
|
+
|
|
335
|
+
local incident_files
|
|
336
|
+
incident_files=$(find "$INCIDENTS_DIR" -name 'inc-*.json' -type f 2>/dev/null || true)
|
|
337
|
+
|
|
338
|
+
while IFS= read -r incident_file; do
|
|
339
|
+
[[ -z "$incident_file" ]] && continue
|
|
340
|
+
|
|
341
|
+
local id created_at signature
|
|
342
|
+
id=$(jq -r '.id' "$incident_file" 2>/dev/null || echo "")
|
|
343
|
+
created_at=$(jq -r '.created_at' "$incident_file" 2>/dev/null || echo "")
|
|
344
|
+
local created_epoch
|
|
345
|
+
created_epoch=$(date -d "$created_at" +%s 2>/dev/null || echo "0")
|
|
346
|
+
|
|
347
|
+
if [[ "$created_epoch" -lt "$window_start" ]]; then
|
|
348
|
+
continue
|
|
349
|
+
fi
|
|
350
|
+
|
|
351
|
+
signature=$(jq -r '.root_cause // "" | .[0:30]' "$incident_file" 2>/dev/null || echo "unknown")
|
|
352
|
+
|
|
353
|
+
if echo "$grouped" | jq -e ".\"$signature\"" >/dev/null 2>&1; then
|
|
354
|
+
grouped=$(echo "$grouped" | jq --arg sig "$signature" --arg id "$id" '.[$sig] += [$id]')
|
|
355
|
+
else
|
|
356
|
+
grouped=$(echo "$grouped" | jq --arg sig "$signature" --arg id "$id" '.[$sig] = [$id]')
|
|
357
|
+
fi
|
|
358
|
+
done <<< "$incident_files"
|
|
359
|
+
|
|
360
|
+
local correlation_groups
|
|
361
|
+
correlation_groups=$(echo "$grouped" | jq -c 'to_entries | map(select(.value | length > 1) | {signature: .key, incidents: .value, count: (.value | length)})')
|
|
362
|
+
|
|
363
|
+
echo "$correlation_groups"
|
|
364
|
+
emit_event "incident.correlation_complete" "groups=$(echo "$correlation_groups" | jq 'length' 2>/dev/null || echo 0)"
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
# ─── Rollback Verification ──────────────────────────────────────────────
|
|
368
|
+
|
|
369
|
+
incident_verify_rollback() {
|
|
370
|
+
local incident_id="$1"
|
|
371
|
+
|
|
372
|
+
local incident_file="${INCIDENTS_DIR}/${incident_id}.json"
|
|
373
|
+
[[ ! -f "$incident_file" ]] && return 1
|
|
374
|
+
|
|
375
|
+
info "Verifying rollback for incident $incident_id"
|
|
376
|
+
|
|
377
|
+
local failures_after_rollback
|
|
378
|
+
failures_after_rollback=$(get_recent_failures 60)
|
|
379
|
+
local failure_count
|
|
380
|
+
failure_count=$(echo "$failures_after_rollback" | jq 'length' 2>/dev/null || echo "0")
|
|
381
|
+
|
|
382
|
+
local verified="false"
|
|
383
|
+
if [[ "$failure_count" -eq 0 ]]; then
|
|
384
|
+
verified="true"
|
|
385
|
+
success "Rollback verified — no failures detected in last 60s"
|
|
386
|
+
else
|
|
387
|
+
warn "Failures still detected after rollback ($failure_count failure(s))"
|
|
388
|
+
fi
|
|
389
|
+
|
|
390
|
+
incident_timeline_update "$incident_id" "rollback_verified" "Verified: $verified" "agent"
|
|
391
|
+
|
|
392
|
+
jq --arg verified "$verified" '.remediation.rollback_verified = ($verified == "true")' "$incident_file" > "${incident_file}.tmp" && \
|
|
393
|
+
mv "${incident_file}.tmp" "$incident_file"
|
|
394
|
+
|
|
395
|
+
[[ "$verified" == "true" ]]
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
# ─── Escalation Chains ──────────────────────────────────────────────
|
|
399
|
+
|
|
400
|
+
incident_escalate() {
|
|
401
|
+
local incident_id="$1"
|
|
402
|
+
local severity="${2:-P3}"
|
|
403
|
+
|
|
404
|
+
local incident_file="${INCIDENTS_DIR}/${incident_id}.json"
|
|
405
|
+
[[ ! -f "$incident_file" ]] && return 1
|
|
406
|
+
|
|
407
|
+
local root_cause
|
|
408
|
+
root_cause=$(jq -r '.root_cause // "Unknown"' "$incident_file" 2>/dev/null || echo "Unknown")
|
|
409
|
+
|
|
410
|
+
case "$severity" in
|
|
411
|
+
P0)
|
|
412
|
+
info "P0 Incident — escalating with hotfix pipeline and urgent notification"
|
|
413
|
+
incident_timeline_update "$incident_id" "escalation_p0" "Creating hotfix issue" "agent"
|
|
414
|
+
|
|
415
|
+
local issue_num
|
|
416
|
+
issue_num=$(create_hotfix_issue "$incident_id" "$severity" "$root_cause")
|
|
417
|
+
|
|
418
|
+
if [[ -n "$issue_num" ]]; then
|
|
419
|
+
trigger_pipeline_for_incident "$issue_num" "$incident_id"
|
|
420
|
+
emit_event "incident.escalated_p0" "incident_id=$incident_id" "issue=$issue_num" "severity=P0"
|
|
421
|
+
fi
|
|
422
|
+
;;
|
|
423
|
+
P1)
|
|
424
|
+
info "P1 Incident — escalating with pipeline"
|
|
425
|
+
incident_timeline_update "$incident_id" "escalation_p1" "Creating issue" "agent"
|
|
426
|
+
|
|
427
|
+
local issue_num
|
|
428
|
+
issue_num=$(create_hotfix_issue "$incident_id" "$severity" "$root_cause")
|
|
429
|
+
|
|
430
|
+
if [[ -n "$issue_num" ]]; then
|
|
431
|
+
trigger_pipeline_for_incident "$issue_num" "$incident_id"
|
|
432
|
+
emit_event "incident.escalated_p1" "incident_id=$incident_id" "issue=$issue_num" "severity=P1"
|
|
433
|
+
fi
|
|
434
|
+
;;
|
|
435
|
+
P2)
|
|
436
|
+
info "P2 Incident — creating issue"
|
|
437
|
+
incident_timeline_update "$incident_id" "escalation_p2" "Creating issue" "agent"
|
|
438
|
+
|
|
439
|
+
local issue_num
|
|
440
|
+
issue_num=$(create_hotfix_issue "$incident_id" "$severity" "$root_cause")
|
|
441
|
+
if [[ -n "$issue_num" ]]; then
|
|
442
|
+
emit_event "incident.escalated_p2" "incident_id=$incident_id" "issue=$issue_num" "severity=P2"
|
|
443
|
+
fi
|
|
444
|
+
;;
|
|
445
|
+
P3)
|
|
446
|
+
info "P3 Incident — recording in memory"
|
|
447
|
+
incident_timeline_update "$incident_id" "escalation_p3" "Low severity — memory only" "agent"
|
|
448
|
+
emit_event "incident.escalated_p3" "incident_id=$incident_id" "severity=P3"
|
|
449
|
+
;;
|
|
450
|
+
esac
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
# ─── Auto-Remediation Orchestration ─────────────────────────────────
|
|
454
|
+
|
|
455
|
+
incident_auto_remediate() {
|
|
456
|
+
local incident_id="$1"
|
|
457
|
+
|
|
458
|
+
local incident_file="${INCIDENTS_DIR}/${incident_id}.json"
|
|
459
|
+
[[ ! -f "$incident_file" ]] && { error "Incident not found: $incident_id"; return 1; }
|
|
460
|
+
|
|
461
|
+
info "Starting auto-remediation for incident $incident_id"
|
|
462
|
+
incident_timeline_update "$incident_id" "auto_remediation_started" "Full loop orchestration" "agent"
|
|
463
|
+
|
|
464
|
+
info "Step 1: Correlating with recent incidents"
|
|
465
|
+
local correlations
|
|
466
|
+
correlations=$(incident_correlate 300)
|
|
467
|
+
incident_timeline_update "$incident_id" "correlation_check" "Correlated failures: $(echo "$correlations" | jq 'length' 2>/dev/null || echo "0")" "agent"
|
|
468
|
+
|
|
469
|
+
info "Step 2: Running deep analysis"
|
|
470
|
+
local failure_log
|
|
471
|
+
failure_log=$(jq -r '.failure_events | @json' "$incident_file" 2>/dev/null || echo "{}")
|
|
472
|
+
local deep_analysis
|
|
473
|
+
deep_analysis=$(incident_deep_analysis "$incident_id" "$failure_log")
|
|
474
|
+
|
|
475
|
+
info "Step 3: Escalating incident"
|
|
476
|
+
local severity
|
|
477
|
+
severity=$(jq -r '.severity // "P3"' "$incident_file" 2>/dev/null || echo "P3")
|
|
478
|
+
incident_escalate "$incident_id" "$severity"
|
|
479
|
+
|
|
480
|
+
if [[ "$severity" == "P0" ]] || [[ "$severity" == "P1" ]]; then
|
|
481
|
+
info "Step 4: Waiting for pipeline fix attempt (60s timeout)"
|
|
482
|
+
incident_timeline_update "$incident_id" "pipeline_wait" "Waiting for fix" "agent"
|
|
483
|
+
|
|
484
|
+
sleep 5
|
|
485
|
+
|
|
486
|
+
info "Step 5: Verifying fix"
|
|
487
|
+
if incident_verify_rollback "$incident_id"; then
|
|
488
|
+
incident_timeline_update "$incident_id" "resolution" "Incident resolved" "agent"
|
|
489
|
+
jq '.status = "resolved" | .resolved_at = (now | todate)' "$incident_file" > "${incident_file}.tmp" && \
|
|
490
|
+
mv "${incident_file}.tmp" "$incident_file"
|
|
491
|
+
success "Incident $incident_id auto-remediated and resolved"
|
|
492
|
+
else
|
|
493
|
+
warn "Automated fix failed — escalating severity"
|
|
494
|
+
local new_severity
|
|
495
|
+
case "$severity" in
|
|
496
|
+
P1) new_severity="P0" ;;
|
|
497
|
+
P2) new_severity="P1" ;;
|
|
498
|
+
*) new_severity="P0" ;;
|
|
499
|
+
esac
|
|
500
|
+
|
|
501
|
+
incident_timeline_update "$incident_id" "escalation_due_to_failure" "Automated fix failed, escalating to $new_severity" "agent"
|
|
502
|
+
jq --arg sev "$new_severity" '.severity = $sev' "$incident_file" > "${incident_file}.tmp" && \
|
|
503
|
+
mv "${incident_file}.tmp" "$incident_file"
|
|
504
|
+
|
|
505
|
+
incident_escalate "$incident_id" "$new_severity"
|
|
506
|
+
emit_event "incident.auto_remediation_failed" "incident_id=$incident_id" "escalated_to=$new_severity"
|
|
507
|
+
fi
|
|
508
|
+
else
|
|
509
|
+
incident_timeline_update "$incident_id" "resolution" "P2/P3 — tracked for human review" "agent"
|
|
510
|
+
fi
|
|
511
|
+
|
|
512
|
+
success "Auto-remediation orchestration complete for incident $incident_id"
|
|
513
|
+
emit_event "incident.auto_remediation_complete" "incident_id=$incident_id"
|
|
514
|
+
}
|
|
515
|
+
|
|
250
516
|
# ─── Watch Command ─────────────────────────────────────────────────────────
|
|
251
517
|
|
|
252
518
|
cmd_watch() {
|
|
@@ -445,12 +711,14 @@ EOF
|
|
|
445
711
|
cmd_stats() {
|
|
446
712
|
local format="${1:-table}"
|
|
447
713
|
|
|
714
|
+
# shellcheck disable=SC2010
|
|
448
715
|
if [[ ! -d "$INCIDENTS_DIR" ]] || [[ -z "$(ls -1 "$INCIDENTS_DIR"/*.json 2>/dev/null | grep -v postmortem)" ]]; then
|
|
449
716
|
info "No incident data available"
|
|
450
717
|
return 0
|
|
451
718
|
fi
|
|
452
719
|
|
|
453
720
|
local total_incidents
|
|
721
|
+
# shellcheck disable=SC2010
|
|
454
722
|
total_incidents=$(ls -1 "$INCIDENTS_DIR"/*.json 2>/dev/null | grep -v postmortem | wc -l)
|
|
455
723
|
|
|
456
724
|
local incident_files
|
package/scripts/sw-init.sh
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
# ║ ║
|
|
9
9
|
# ║ --deploy Detect platform and generate deployed.json template ║
|
|
10
10
|
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
11
|
-
|
|
11
|
+
# shellcheck disable=SC2034
|
|
12
|
+
VERSION="3.3.0"
|
|
12
13
|
set -euo pipefail
|
|
13
14
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
14
15
|
trap 'rm -f "${tmp:-}"' EXIT
|
|
@@ -343,6 +344,7 @@ if ! echo "$PATH" | tr ':' '\n' | grep -qxF "$BIN_DIR"; then
|
|
|
343
344
|
fi
|
|
344
345
|
export PATH="$BIN_DIR:$PATH"
|
|
345
346
|
else
|
|
347
|
+
# shellcheck disable=SC2088
|
|
346
348
|
success "~/.local/bin already in PATH"
|
|
347
349
|
fi
|
|
348
350
|
|
|
@@ -509,6 +511,11 @@ elif [[ -f "$SETTINGS_TEMPLATE" ]]; then
|
|
|
509
511
|
success "Installed ~/.claude/settings.json (with agent teams enabled)"
|
|
510
512
|
else
|
|
511
513
|
# Create minimal settings.json with agent teams
|
|
514
|
+
# Use restrictive umask for sensitive files (owner-only: 600)
|
|
515
|
+
local _old_umask_settings
|
|
516
|
+
_old_umask_settings=$(umask)
|
|
517
|
+
umask 0077
|
|
518
|
+
|
|
512
519
|
cat > "$SETTINGS_FILE" << 'SETTINGS_EOF'
|
|
513
520
|
{
|
|
514
521
|
"hooks": {},
|
|
@@ -516,10 +523,16 @@ else
|
|
|
516
523
|
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1",
|
|
517
524
|
"CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY": "5",
|
|
518
525
|
"CLAUDE_CODE_AUTOCOMPACT_PCT_OVERRIDE": "70",
|
|
519
|
-
"CLAUDE_CODE_SUBAGENT_MODEL": "haiku"
|
|
526
|
+
"CLAUDE_CODE_SUBAGENT_MODEL": "haiku",
|
|
527
|
+
"ENABLE_TOOL_SEARCH": "auto",
|
|
528
|
+
"MAX_MCP_OUTPUT_TOKENS": "50000"
|
|
520
529
|
}
|
|
521
530
|
}
|
|
522
531
|
SETTINGS_EOF
|
|
532
|
+
|
|
533
|
+
umask "$_old_umask_settings"
|
|
534
|
+
chmod 600 "$SETTINGS_FILE"
|
|
535
|
+
|
|
523
536
|
success "Created ~/.claude/settings.json with agent teams enabled"
|
|
524
537
|
fi
|
|
525
538
|
|
|
@@ -607,6 +620,7 @@ GLOBAL_CLAUDE_MD="$CLAUDE_DIR/CLAUDE.md"
|
|
|
607
620
|
if [[ "$SKIP_CLAUDE_MD" == "false" && -f "$CLAUDE_MD_SRC" ]]; then
|
|
608
621
|
if [[ -f "$GLOBAL_CLAUDE_MD" ]]; then
|
|
609
622
|
if grep -q "Shipwright" "$GLOBAL_CLAUDE_MD" 2>/dev/null; then
|
|
623
|
+
# shellcheck disable=SC2088
|
|
610
624
|
info "~/.claude/CLAUDE.md already contains Shipwright instructions"
|
|
611
625
|
else
|
|
612
626
|
{ echo ""; echo "---"; echo ""; cat "$CLAUDE_MD_SRC"; } >> "$GLOBAL_CLAUDE_MD"
|
|
@@ -722,6 +736,7 @@ detect_deploy_platform() {
|
|
|
722
736
|
for adapter_file in "$ADAPTERS_DIR"/*-deploy.sh; do
|
|
723
737
|
[[ -f "$adapter_file" ]] || continue
|
|
724
738
|
# Source the adapter in a subshell to get detection
|
|
739
|
+
# shellcheck disable=SC1090
|
|
725
740
|
if ( source "$adapter_file" && detect_platform ); then
|
|
726
741
|
local name
|
|
727
742
|
name=$(basename "$adapter_file" | sed 's/-deploy\.sh$//')
|
|
@@ -788,6 +803,7 @@ fi
|
|
|
788
803
|
|
|
789
804
|
# Source the adapter to get command values
|
|
790
805
|
ADAPTER_FILE="$ADAPTERS_DIR/${DEPLOY_PLATFORM}-deploy.sh"
|
|
806
|
+
# shellcheck disable=SC1090
|
|
791
807
|
source "$ADAPTER_FILE"
|
|
792
808
|
|
|
793
809
|
staging_cmd=$(get_staging_cmd)
|
package/scripts/sw-instrument.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
|
}
|
|
@@ -190,6 +192,7 @@ cmd_start() {
|
|
|
190
192
|
local run_file="${INSTRUMENT_ACTIVE}/${run_id}.json"
|
|
191
193
|
local tmp_file
|
|
192
194
|
tmp_file="$(mktemp "${INSTRUMENT_ACTIVE}/.tmp.XXXXXX")"
|
|
195
|
+
# shellcheck disable=SC2064
|
|
193
196
|
trap "rm -f '$tmp_file'" RETURN
|
|
194
197
|
|
|
195
198
|
# Get repo info if not provided
|
|
@@ -257,6 +260,7 @@ cmd_record() {
|
|
|
257
260
|
|
|
258
261
|
local tmp_file
|
|
259
262
|
tmp_file="$(mktemp "${INSTRUMENT_ACTIVE}/.tmp.XXXXXX")"
|
|
263
|
+
# shellcheck disable=SC2064
|
|
260
264
|
trap "rm -f '$tmp_file'" RETURN
|
|
261
265
|
|
|
262
266
|
# Parse value as number if it's numeric
|
|
@@ -320,6 +324,7 @@ cmd_stage_start() {
|
|
|
320
324
|
|
|
321
325
|
local tmp_file
|
|
322
326
|
tmp_file="$(mktemp "${INSTRUMENT_ACTIVE}/.tmp.XXXXXX")"
|
|
327
|
+
# shellcheck disable=SC2064
|
|
323
328
|
trap "rm -f '$tmp_file'" RETURN
|
|
324
329
|
|
|
325
330
|
jq \
|
|
@@ -366,6 +371,7 @@ cmd_stage_end() {
|
|
|
366
371
|
|
|
367
372
|
local tmp_file
|
|
368
373
|
tmp_file="$(mktemp "${INSTRUMENT_ACTIVE}/.tmp.XXXXXX")"
|
|
374
|
+
# shellcheck disable=SC2064
|
|
369
375
|
trap "rm -f '$tmp_file'" RETURN
|
|
370
376
|
|
|
371
377
|
jq \
|
|
@@ -416,6 +422,7 @@ cmd_finish() {
|
|
|
416
422
|
|
|
417
423
|
local tmp_file
|
|
418
424
|
tmp_file="$(mktemp "${INSTRUMENT_ACTIVE}/.tmp.XXXXXX")"
|
|
425
|
+
# shellcheck disable=SC2064
|
|
419
426
|
trap "rm -f '$tmp_file'" RETURN
|
|
420
427
|
|
|
421
428
|
# Update run record with finish data
|
|
@@ -476,6 +483,7 @@ cmd_summary() {
|
|
|
476
483
|
tmp_file="$(mktemp)"
|
|
477
484
|
grep "\"run_id\":\"${run_id}\"" "$INSTRUMENT_COMPLETED" | head -1 > "$tmp_file"
|
|
478
485
|
run_file="$tmp_file"
|
|
486
|
+
# shellcheck disable=SC2064
|
|
479
487
|
trap "rm -f '$tmp_file'" RETURN
|
|
480
488
|
fi
|
|
481
489
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
9
|
-
VERSION="3.
|
|
9
|
+
VERSION="3.3.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
REPO_DIR="${REPO_DIR:-$(cd "$SCRIPT_DIR/.." && pwd)}"
|
|
12
12
|
|
|
@@ -31,6 +31,7 @@ fi
|
|
|
31
31
|
if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
32
32
|
emit_event() {
|
|
33
33
|
local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
|
|
34
|
+
# shellcheck disable=SC2155
|
|
34
35
|
local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
|
|
35
36
|
while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
|
|
36
37
|
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
@@ -159,6 +160,7 @@ _intelligence_track_cache_access() {
|
|
|
159
160
|
|
|
160
161
|
local tmp_file
|
|
161
162
|
tmp_file=$(mktemp "${TMPDIR:-/tmp}/sw-cache-stats.XXXXXX")
|
|
163
|
+
# shellcheck disable=SC2064
|
|
162
164
|
trap "rm -f '$tmp_file'" RETURN
|
|
163
165
|
if [[ "$hit_or_miss" == "hit" ]]; then
|
|
164
166
|
jq '.hits += 1 | .total += 1' "$CACHE_STATS_FILE" > "$tmp_file" && mv "$tmp_file" "$CACHE_STATS_FILE" || rm -f "$tmp_file"
|
|
@@ -179,6 +181,7 @@ _intelligence_adjust_cache_ttl() {
|
|
|
179
181
|
[[ -f "$CACHE_STATS_FILE" ]] || return 0
|
|
180
182
|
|
|
181
183
|
local hits misses total
|
|
184
|
+
# shellcheck disable=SC2034
|
|
182
185
|
hits=$(jq '.hits // 0' "$CACHE_STATS_FILE" 2>/dev/null || echo "0")
|
|
183
186
|
misses=$(jq '.misses // 0' "$CACHE_STATS_FILE" 2>/dev/null || echo "0")
|
|
184
187
|
total=$(jq '.total // 0' "$CACHE_STATS_FILE" 2>/dev/null || echo "0")
|
|
@@ -203,6 +206,7 @@ _intelligence_adjust_cache_ttl() {
|
|
|
203
206
|
if [[ "$new_ttl" != "$current_ttl" ]]; then
|
|
204
207
|
local tmp_file
|
|
205
208
|
tmp_file=$(mktemp "${TMPDIR:-/tmp}/sw-cache-ttl.XXXXXX")
|
|
209
|
+
# shellcheck disable=SC2064
|
|
206
210
|
trap "rm -f '$tmp_file'" RETURN
|
|
207
211
|
jq -n \
|
|
208
212
|
--argjson ttl "$new_ttl" \
|
|
@@ -220,6 +224,7 @@ _intelligence_adjust_cache_ttl() {
|
|
|
220
224
|
# Reset stats for next window
|
|
221
225
|
local tmp_reset
|
|
222
226
|
tmp_reset=$(mktemp "${TMPDIR:-/tmp}/sw-cache-reset.XXXXXX")
|
|
227
|
+
# shellcheck disable=SC2064
|
|
223
228
|
trap "rm -f '$tmp_reset'" RETURN
|
|
224
229
|
echo '{"hits":0,"misses":0,"total":0}' > "$tmp_reset" && mv "$tmp_reset" "$CACHE_STATS_FILE" || rm -f "$tmp_reset"
|
|
225
230
|
}
|
|
@@ -306,6 +311,7 @@ _intelligence_cache_set() {
|
|
|
306
311
|
|
|
307
312
|
local tmp_file
|
|
308
313
|
tmp_file=$(mktemp "${TMPDIR:-/tmp}/sw-intel-cache.XXXXXX")
|
|
314
|
+
# shellcheck disable=SC2064
|
|
309
315
|
trap "rm -f '$tmp_file'" RETURN
|
|
310
316
|
jq --arg h "$hash" \
|
|
311
317
|
--argjson result "$result" \
|
|
@@ -321,6 +327,7 @@ _intelligence_call_claude() {
|
|
|
321
327
|
local prompt="$1"
|
|
322
328
|
local cache_key="${2:-}"
|
|
323
329
|
local ttl="${3:-$DEFAULT_CACHE_TTL}"
|
|
330
|
+
local model="${4:-${INTELLIGENCE_MODEL:-haiku}}"
|
|
324
331
|
|
|
325
332
|
# Check cache first
|
|
326
333
|
local cached
|
|
@@ -346,8 +353,13 @@ _intelligence_call_claude() {
|
|
|
346
353
|
elif command -v timeout >/dev/null 2>&1; then _timeout_cmd="timeout $_claude_timeout"
|
|
347
354
|
fi
|
|
348
355
|
|
|
356
|
+
local _model_flag=""
|
|
357
|
+
[[ -n "$model" ]] && _model_flag="--model $model"
|
|
358
|
+
|
|
359
|
+
type daemon_log &>/dev/null && daemon_log INFO "Intelligence: calling Claude (model=${model:-default}, timeout=${_claude_timeout}s)"
|
|
360
|
+
|
|
349
361
|
local response
|
|
350
|
-
if ! response=$($_timeout_cmd claude -p "$prompt" 2>/dev/null); then
|
|
362
|
+
if ! response=$($_timeout_cmd claude -p "$prompt" $_model_flag 2>/dev/null); then
|
|
351
363
|
error "Claude call failed or timed out"
|
|
352
364
|
echo '{"error":"claude_call_failed"}'
|
|
353
365
|
return 1
|
|
@@ -395,7 +407,8 @@ _intelligence_fallback_analyze() {
|
|
|
395
407
|
local outcomes_file="$HOME/.shipwright/optimization/outcomes.jsonl"
|
|
396
408
|
if [[ -f "$outcomes_file" ]] && command -v jq &>/dev/null; then
|
|
397
409
|
local sample_count
|
|
398
|
-
sample_count=$(wc -l < "$outcomes_file" 2>/dev/null ||
|
|
410
|
+
sample_count=$(wc -l < "$outcomes_file" 2>/dev/null || true)
|
|
411
|
+
sample_count="${sample_count:-0}"
|
|
399
412
|
|
|
400
413
|
if [[ "$sample_count" -gt 5 ]]; then
|
|
401
414
|
# Compute average complexity from past outcomes
|
|
@@ -410,6 +423,28 @@ _intelligence_fallback_analyze() {
|
|
|
410
423
|
fi
|
|
411
424
|
fi
|
|
412
425
|
|
|
426
|
+
# Issue type classification from labels
|
|
427
|
+
local issue_type="backend"
|
|
428
|
+
if echo "$labels" | grep -qiE 'ui|frontend|css|design' 2>/dev/null; then
|
|
429
|
+
issue_type="frontend"
|
|
430
|
+
elif echo "$labels" | grep -qiE 'api|endpoint|rest|graphql' 2>/dev/null; then
|
|
431
|
+
issue_type="api"
|
|
432
|
+
elif echo "$labels" | grep -qiE 'db|migration|schema|database' 2>/dev/null; then
|
|
433
|
+
issue_type="database"
|
|
434
|
+
elif echo "$labels" | grep -qiE 'security|auth|cve' 2>/dev/null; then
|
|
435
|
+
issue_type="security"
|
|
436
|
+
elif echo "$labels" | grep -qiE 'perf|slow|latency|performance' 2>/dev/null; then
|
|
437
|
+
issue_type="performance"
|
|
438
|
+
elif echo "$labels" | grep -qiE 'test|coverage' 2>/dev/null; then
|
|
439
|
+
issue_type="testing"
|
|
440
|
+
elif echo "$labels" | grep -qiE 'docs|readme|documentation' 2>/dev/null; then
|
|
441
|
+
issue_type="documentation"
|
|
442
|
+
elif echo "$labels" | grep -qiE 'infra|ci|deploy|infrastructure' 2>/dev/null; then
|
|
443
|
+
issue_type="infrastructure"
|
|
444
|
+
elif echo "$labels" | grep -qiE 'refactor|cleanup' 2>/dev/null; then
|
|
445
|
+
issue_type="refactor"
|
|
446
|
+
fi
|
|
447
|
+
|
|
413
448
|
# Label-based heuristics (better than nothing)
|
|
414
449
|
if echo "$labels" | grep -qiE 'bug|fix|hotfix' 2>/dev/null; then
|
|
415
450
|
complexity=$((complexity > 3 ? complexity - 2 : complexity))
|
|
@@ -435,7 +470,7 @@ _intelligence_fallback_analyze() {
|
|
|
435
470
|
complexity=$((complexity + 2 > 10 ? 10 : complexity + 2))
|
|
436
471
|
fi
|
|
437
472
|
|
|
438
|
-
echo "{\"complexity\":$complexity,\"risk_level\":\"$risk\",\"success_probability\":$probability,\"recommended_template\":\"$template\"}"
|
|
473
|
+
echo "{\"complexity\":$complexity,\"risk_level\":\"$risk\",\"success_probability\":$probability,\"recommended_template\":\"$template\",\"issue_type\":\"$issue_type\"}"
|
|
439
474
|
}
|
|
440
475
|
|
|
441
476
|
_intelligence_fallback_cost() {
|
|
@@ -487,7 +522,7 @@ intelligence_analyze_issue() {
|
|
|
487
522
|
if [[ -n "${merged:-}" ]]; then
|
|
488
523
|
echo "$merged"
|
|
489
524
|
else
|
|
490
|
-
echo '{"error":"intelligence_disabled","complexity":5,"risk_level":"medium","success_probability":50,"recommended_template":"standard","key_risks":[],"implementation_hints":[]}'
|
|
525
|
+
echo '{"error":"intelligence_disabled","complexity":5,"risk_level":"medium","success_probability":50,"recommended_template":"standard","issue_type":"backend","key_risks":[],"implementation_hints":[]}'
|
|
491
526
|
fi
|
|
492
527
|
return 0
|
|
493
528
|
fi
|
|
@@ -505,6 +540,7 @@ Return JSON with exactly these fields:
|
|
|
505
540
|
\"risk_level\": \"<low|medium|high|critical>\",
|
|
506
541
|
\"success_probability\": <number 0-100>,
|
|
507
542
|
\"recommended_template\": \"<fast|standard|full|hotfix|autonomous|enterprise|cost-aware>\",
|
|
543
|
+
\"issue_type\": \"<frontend|backend|api|database|infrastructure|documentation|security|performance|refactor|testing>\",
|
|
508
544
|
\"key_risks\": [\"risk1\", \"risk2\"],
|
|
509
545
|
\"implementation_hints\": [\"hint1\", \"hint2\"]
|
|
510
546
|
}"
|
|
@@ -527,7 +563,8 @@ Return JSON with exactly these fields:
|
|
|
527
563
|
"complexity=$(echo "$result" | jq -r '.complexity')" \
|
|
528
564
|
"risk_level=$(echo "$result" | jq -r '.risk_level')" \
|
|
529
565
|
"success_probability=$(echo "$result" | jq -r '.success_probability')" \
|
|
530
|
-
"recommended_template=$(echo "$result" | jq -r '.recommended_template')"
|
|
566
|
+
"recommended_template=$(echo "$result" | jq -r '.recommended_template')" \
|
|
567
|
+
"issue_type=$(echo "$result" | jq -r '.issue_type // "backend"')"
|
|
531
568
|
|
|
532
569
|
# Enrich with GitHub data if available
|
|
533
570
|
result=$(intelligence_github_enrich "$result")
|
|
@@ -537,7 +574,7 @@ Return JSON with exactly these fields:
|
|
|
537
574
|
else
|
|
538
575
|
local fallback
|
|
539
576
|
fallback=$(_intelligence_fallback_analyze "$title" "$body" "$labels")
|
|
540
|
-
echo "$fallback" | jq -c '. + {error: "analysis_failed", key_risks: ["analysis_failed"], implementation_hints: []}' 2>/dev/null || echo '{"error":"analysis_failed","complexity":5,"risk_level":"medium","success_probability":50,"recommended_template":"standard","key_risks":["analysis_failed"],"implementation_hints":[]}'
|
|
577
|
+
echo "$fallback" | jq -c '. + {error: "analysis_failed", key_risks: ["analysis_failed"], implementation_hints: []}' 2>/dev/null || echo '{"error":"analysis_failed","complexity":5,"risk_level":"medium","success_probability":50,"recommended_template":"standard","issue_type":"backend","key_risks":["analysis_failed"],"implementation_hints":[]}'
|
|
541
578
|
return 0
|
|
542
579
|
fi
|
|
543
580
|
}
|
package/scripts/sw-jira.sh
CHANGED
|
@@ -6,8 +6,10 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
# shellcheck disable=SC2034
|
|
10
|
+
VERSION="3.3.0"
|
|
10
11
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
+
# shellcheck disable=SC2034
|
|
11
13
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
14
|
|
|
13
15
|
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
|
@@ -30,6 +32,7 @@ fi
|
|
|
30
32
|
if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
31
33
|
emit_event() {
|
|
32
34
|
local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
|
|
35
|
+
# shellcheck disable=SC2155
|
|
33
36
|
local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
|
|
34
37
|
while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
|
|
35
38
|
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
@@ -206,6 +209,7 @@ cmd_sync() {
|
|
|
206
209
|
synced=$((synced + 1))
|
|
207
210
|
else
|
|
208
211
|
# Create GitHub issue
|
|
212
|
+
# shellcheck disable=SC2155
|
|
209
213
|
local labels="$(_config_get "labels.ready_to_build" "ready-to-build")"
|
|
210
214
|
if [[ -n "$priority_label" ]]; then
|
|
211
215
|
labels="${labels},${priority_label}"
|