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.
Files changed (283) hide show
  1. package/.claude/agents/code-reviewer.md +2 -0
  2. package/.claude/agents/devops-engineer.md +2 -0
  3. package/.claude/agents/doc-fleet-agent.md +2 -0
  4. package/.claude/agents/pipeline-agent.md +2 -0
  5. package/.claude/agents/shell-script-specialist.md +2 -0
  6. package/.claude/agents/test-specialist.md +2 -0
  7. package/.claude/hooks/agent-crash-capture.sh +32 -0
  8. package/.claude/hooks/post-tool-use.sh +3 -2
  9. package/.claude/hooks/pre-tool-use.sh +35 -3
  10. package/README.md +22 -8
  11. package/claude-code/hooks/config-change.sh +18 -0
  12. package/claude-code/hooks/instructions-reloaded.sh +7 -0
  13. package/claude-code/hooks/worktree-create.sh +25 -0
  14. package/claude-code/hooks/worktree-remove.sh +20 -0
  15. package/config/code-constitution.json +130 -0
  16. package/config/defaults.json +25 -2
  17. package/config/policy.json +1 -1
  18. package/dashboard/middleware/auth.ts +134 -0
  19. package/dashboard/middleware/constants.ts +21 -0
  20. package/dashboard/public/index.html +8 -6
  21. package/dashboard/public/styles.css +176 -97
  22. package/dashboard/routes/auth.ts +38 -0
  23. package/dashboard/server.ts +117 -25
  24. package/dashboard/services/config.ts +26 -0
  25. package/dashboard/services/db.ts +118 -0
  26. package/dashboard/src/canvas/pixel-agent.ts +298 -0
  27. package/dashboard/src/canvas/pixel-sprites.ts +440 -0
  28. package/dashboard/src/canvas/shipyard-effects.ts +367 -0
  29. package/dashboard/src/canvas/shipyard-scene.ts +616 -0
  30. package/dashboard/src/canvas/submarine-layout.ts +267 -0
  31. package/dashboard/src/components/header.ts +8 -7
  32. package/dashboard/src/core/api.ts +5 -0
  33. package/dashboard/src/core/router.ts +1 -0
  34. package/dashboard/src/design/submarine-theme.ts +253 -0
  35. package/dashboard/src/main.ts +2 -0
  36. package/dashboard/src/types/api.ts +12 -1
  37. package/dashboard/src/views/activity.ts +2 -1
  38. package/dashboard/src/views/metrics.ts +69 -1
  39. package/dashboard/src/views/shipyard.ts +39 -0
  40. package/dashboard/types/index.ts +166 -0
  41. package/docs/plans/2026-02-28-compound-audit-and-shipyard-design.md +186 -0
  42. package/docs/plans/2026-02-28-skipper-shipwright-implementation-plan.md +1182 -0
  43. package/docs/plans/2026-02-28-skipper-shipwright-integration-design.md +531 -0
  44. package/docs/plans/2026-03-01-ai-powered-skill-injection-design.md +298 -0
  45. package/docs/plans/2026-03-01-ai-powered-skill-injection-plan.md +1109 -0
  46. package/docs/plans/2026-03-01-capabilities-cleanup-plan.md +658 -0
  47. package/docs/plans/2026-03-01-clean-architecture-plan.md +924 -0
  48. package/docs/plans/2026-03-01-compound-audit-cascade-design.md +191 -0
  49. package/docs/plans/2026-03-01-compound-audit-cascade-plan.md +921 -0
  50. package/docs/plans/2026-03-01-deep-integration-plan.md +851 -0
  51. package/docs/plans/2026-03-01-pipeline-audit-trail-design.md +145 -0
  52. package/docs/plans/2026-03-01-pipeline-audit-trail-plan.md +770 -0
  53. package/docs/plans/2026-03-01-refined-depths-brand-design.md +382 -0
  54. package/docs/plans/2026-03-01-refined-depths-implementation.md +599 -0
  55. package/docs/plans/2026-03-01-skipper-kernel-integration-design.md +203 -0
  56. package/docs/plans/2026-03-01-unified-platform-design.md +272 -0
  57. package/docs/plans/2026-03-07-claude-code-feature-integration-design.md +189 -0
  58. package/docs/plans/2026-03-07-claude-code-feature-integration-plan.md +1165 -0
  59. package/docs/research/BACKLOG_QUICK_REFERENCE.md +352 -0
  60. package/docs/research/CUTTING_EDGE_RESEARCH_2026.md +546 -0
  61. package/docs/research/RESEARCH_INDEX.md +439 -0
  62. package/docs/research/RESEARCH_SOURCES.md +440 -0
  63. package/docs/research/RESEARCH_SUMMARY.txt +275 -0
  64. package/docs/superpowers/specs/2026-03-10-pipeline-quality-revolution-design.md +341 -0
  65. package/package.json +2 -2
  66. package/scripts/lib/adaptive-model.sh +427 -0
  67. package/scripts/lib/adaptive-timeout.sh +316 -0
  68. package/scripts/lib/audit-trail.sh +309 -0
  69. package/scripts/lib/auto-recovery.sh +471 -0
  70. package/scripts/lib/bandit-selector.sh +431 -0
  71. package/scripts/lib/bootstrap.sh +104 -2
  72. package/scripts/lib/causal-graph.sh +455 -0
  73. package/scripts/lib/compat.sh +126 -0
  74. package/scripts/lib/compound-audit.sh +337 -0
  75. package/scripts/lib/constitutional.sh +454 -0
  76. package/scripts/lib/context-budget.sh +359 -0
  77. package/scripts/lib/convergence.sh +594 -0
  78. package/scripts/lib/cost-optimizer.sh +634 -0
  79. package/scripts/lib/daemon-adaptive.sh +14 -2
  80. package/scripts/lib/daemon-dispatch.sh +106 -17
  81. package/scripts/lib/daemon-failure.sh +34 -4
  82. package/scripts/lib/daemon-patrol.sh +25 -4
  83. package/scripts/lib/daemon-poll-github.sh +361 -0
  84. package/scripts/lib/daemon-poll-health.sh +299 -0
  85. package/scripts/lib/daemon-poll.sh +27 -611
  86. package/scripts/lib/daemon-state.sh +119 -66
  87. package/scripts/lib/daemon-triage.sh +10 -0
  88. package/scripts/lib/dod-scorecard.sh +442 -0
  89. package/scripts/lib/error-actionability.sh +300 -0
  90. package/scripts/lib/formal-spec.sh +461 -0
  91. package/scripts/lib/helpers.sh +180 -5
  92. package/scripts/lib/intent-analysis.sh +409 -0
  93. package/scripts/lib/loop-convergence.sh +350 -0
  94. package/scripts/lib/loop-iteration.sh +682 -0
  95. package/scripts/lib/loop-progress.sh +48 -0
  96. package/scripts/lib/loop-restart.sh +185 -0
  97. package/scripts/lib/memory-effectiveness.sh +506 -0
  98. package/scripts/lib/mutation-executor.sh +352 -0
  99. package/scripts/lib/outcome-feedback.sh +521 -0
  100. package/scripts/lib/pipeline-cli.sh +336 -0
  101. package/scripts/lib/pipeline-commands.sh +1216 -0
  102. package/scripts/lib/pipeline-detection.sh +101 -3
  103. package/scripts/lib/pipeline-execution.sh +897 -0
  104. package/scripts/lib/pipeline-github.sh +28 -3
  105. package/scripts/lib/pipeline-intelligence-compound.sh +431 -0
  106. package/scripts/lib/pipeline-intelligence-scoring.sh +407 -0
  107. package/scripts/lib/pipeline-intelligence-skip.sh +181 -0
  108. package/scripts/lib/pipeline-intelligence.sh +104 -1138
  109. package/scripts/lib/pipeline-quality-bash-compat.sh +182 -0
  110. package/scripts/lib/pipeline-quality-checks.sh +17 -711
  111. package/scripts/lib/pipeline-quality-gates.sh +563 -0
  112. package/scripts/lib/pipeline-stages-build.sh +730 -0
  113. package/scripts/lib/pipeline-stages-delivery.sh +965 -0
  114. package/scripts/lib/pipeline-stages-intake.sh +1133 -0
  115. package/scripts/lib/pipeline-stages-monitor.sh +407 -0
  116. package/scripts/lib/pipeline-stages-review.sh +1022 -0
  117. package/scripts/lib/pipeline-stages.sh +161 -2901
  118. package/scripts/lib/pipeline-state.sh +36 -5
  119. package/scripts/lib/pipeline-util.sh +487 -0
  120. package/scripts/lib/policy-learner.sh +438 -0
  121. package/scripts/lib/process-reward.sh +493 -0
  122. package/scripts/lib/project-detect.sh +649 -0
  123. package/scripts/lib/quality-profile.sh +334 -0
  124. package/scripts/lib/recruit-commands.sh +885 -0
  125. package/scripts/lib/recruit-learning.sh +739 -0
  126. package/scripts/lib/recruit-roles.sh +648 -0
  127. package/scripts/lib/reward-aggregator.sh +458 -0
  128. package/scripts/lib/rl-optimizer.sh +362 -0
  129. package/scripts/lib/root-cause.sh +427 -0
  130. package/scripts/lib/scope-enforcement.sh +445 -0
  131. package/scripts/lib/session-restart.sh +493 -0
  132. package/scripts/lib/skill-memory.sh +300 -0
  133. package/scripts/lib/skill-registry.sh +775 -0
  134. package/scripts/lib/spec-driven.sh +476 -0
  135. package/scripts/lib/test-helpers.sh +18 -7
  136. package/scripts/lib/test-holdout.sh +429 -0
  137. package/scripts/lib/test-optimizer.sh +511 -0
  138. package/scripts/shipwright-file-suggest.sh +45 -0
  139. package/scripts/skills/adversarial-quality.md +61 -0
  140. package/scripts/skills/api-design.md +44 -0
  141. package/scripts/skills/architecture-design.md +50 -0
  142. package/scripts/skills/brainstorming.md +43 -0
  143. package/scripts/skills/data-pipeline.md +44 -0
  144. package/scripts/skills/deploy-safety.md +64 -0
  145. package/scripts/skills/documentation.md +38 -0
  146. package/scripts/skills/frontend-design.md +45 -0
  147. package/scripts/skills/generated/.gitkeep +0 -0
  148. package/scripts/skills/generated/_refinements/.gitkeep +0 -0
  149. package/scripts/skills/generated/_refinements/adversarial-quality.patch.md +3 -0
  150. package/scripts/skills/generated/_refinements/architecture-design.patch.md +3 -0
  151. package/scripts/skills/generated/_refinements/brainstorming.patch.md +3 -0
  152. package/scripts/skills/generated/cli-version-management.md +29 -0
  153. package/scripts/skills/generated/collection-system-validation.md +99 -0
  154. package/scripts/skills/generated/large-scale-c-refactoring-coordination.md +97 -0
  155. package/scripts/skills/generated/pattern-matching-similarity-scoring.md +195 -0
  156. package/scripts/skills/generated/test-parallelization-detection.md +65 -0
  157. package/scripts/skills/observability.md +79 -0
  158. package/scripts/skills/performance.md +48 -0
  159. package/scripts/skills/pr-quality.md +49 -0
  160. package/scripts/skills/product-thinking.md +43 -0
  161. package/scripts/skills/security-audit.md +49 -0
  162. package/scripts/skills/systematic-debugging.md +40 -0
  163. package/scripts/skills/testing-strategy.md +47 -0
  164. package/scripts/skills/two-stage-review.md +52 -0
  165. package/scripts/skills/validation-thoroughness.md +55 -0
  166. package/scripts/sw +9 -3
  167. package/scripts/sw-activity.sh +9 -8
  168. package/scripts/sw-adaptive.sh +8 -7
  169. package/scripts/sw-adversarial.sh +2 -1
  170. package/scripts/sw-architecture-enforcer.sh +3 -1
  171. package/scripts/sw-auth.sh +12 -2
  172. package/scripts/sw-autonomous.sh +5 -1
  173. package/scripts/sw-changelog.sh +4 -1
  174. package/scripts/sw-checkpoint.sh +2 -1
  175. package/scripts/sw-ci.sh +15 -6
  176. package/scripts/sw-cleanup.sh +4 -26
  177. package/scripts/sw-code-review.sh +45 -20
  178. package/scripts/sw-connect.sh +2 -1
  179. package/scripts/sw-context.sh +2 -1
  180. package/scripts/sw-cost.sh +107 -5
  181. package/scripts/sw-daemon.sh +71 -11
  182. package/scripts/sw-dashboard.sh +3 -1
  183. package/scripts/sw-db.sh +71 -20
  184. package/scripts/sw-decide.sh +8 -2
  185. package/scripts/sw-decompose.sh +360 -17
  186. package/scripts/sw-deps.sh +4 -1
  187. package/scripts/sw-developer-simulation.sh +4 -1
  188. package/scripts/sw-discovery.sh +378 -5
  189. package/scripts/sw-doc-fleet.sh +4 -1
  190. package/scripts/sw-docs-agent.sh +3 -1
  191. package/scripts/sw-docs.sh +2 -1
  192. package/scripts/sw-doctor.sh +453 -2
  193. package/scripts/sw-dora.sh +4 -1
  194. package/scripts/sw-durable.sh +12 -7
  195. package/scripts/sw-e2e-orchestrator.sh +17 -16
  196. package/scripts/sw-eventbus.sh +13 -4
  197. package/scripts/sw-evidence.sh +364 -12
  198. package/scripts/sw-feedback.sh +550 -9
  199. package/scripts/sw-fix.sh +20 -1
  200. package/scripts/sw-fleet-discover.sh +6 -2
  201. package/scripts/sw-fleet-viz.sh +9 -4
  202. package/scripts/sw-fleet.sh +5 -1
  203. package/scripts/sw-github-app.sh +18 -4
  204. package/scripts/sw-github-checks.sh +3 -2
  205. package/scripts/sw-github-deploy.sh +3 -2
  206. package/scripts/sw-github-graphql.sh +18 -7
  207. package/scripts/sw-guild.sh +5 -1
  208. package/scripts/sw-heartbeat.sh +5 -30
  209. package/scripts/sw-hello.sh +67 -0
  210. package/scripts/sw-hygiene.sh +10 -3
  211. package/scripts/sw-incident.sh +273 -5
  212. package/scripts/sw-init.sh +18 -2
  213. package/scripts/sw-instrument.sh +10 -2
  214. package/scripts/sw-intelligence.sh +44 -7
  215. package/scripts/sw-jira.sh +5 -1
  216. package/scripts/sw-launchd.sh +2 -1
  217. package/scripts/sw-linear.sh +4 -1
  218. package/scripts/sw-logs.sh +4 -1
  219. package/scripts/sw-loop.sh +436 -1076
  220. package/scripts/sw-memory.sh +357 -3
  221. package/scripts/sw-mission-control.sh +6 -1
  222. package/scripts/sw-model-router.sh +483 -27
  223. package/scripts/sw-otel.sh +15 -4
  224. package/scripts/sw-oversight.sh +14 -5
  225. package/scripts/sw-patrol-meta.sh +334 -0
  226. package/scripts/sw-pipeline-composer.sh +7 -1
  227. package/scripts/sw-pipeline-vitals.sh +12 -6
  228. package/scripts/sw-pipeline.sh +54 -2653
  229. package/scripts/sw-pm.sh +16 -8
  230. package/scripts/sw-pr-lifecycle.sh +2 -1
  231. package/scripts/sw-predictive.sh +17 -5
  232. package/scripts/sw-prep.sh +185 -2
  233. package/scripts/sw-ps.sh +5 -25
  234. package/scripts/sw-public-dashboard.sh +17 -4
  235. package/scripts/sw-quality.sh +14 -6
  236. package/scripts/sw-reaper.sh +8 -25
  237. package/scripts/sw-recruit.sh +156 -2303
  238. package/scripts/sw-regression.sh +19 -12
  239. package/scripts/sw-release-manager.sh +3 -1
  240. package/scripts/sw-release.sh +4 -1
  241. package/scripts/sw-remote.sh +3 -1
  242. package/scripts/sw-replay.sh +7 -1
  243. package/scripts/sw-retro.sh +158 -1
  244. package/scripts/sw-review-rerun.sh +3 -1
  245. package/scripts/sw-scale.sh +14 -5
  246. package/scripts/sw-security-audit.sh +6 -1
  247. package/scripts/sw-self-optimize.sh +173 -6
  248. package/scripts/sw-session.sh +9 -3
  249. package/scripts/sw-setup.sh +3 -1
  250. package/scripts/sw-stall-detector.sh +406 -0
  251. package/scripts/sw-standup.sh +15 -7
  252. package/scripts/sw-status.sh +3 -1
  253. package/scripts/sw-strategic.sh +14 -6
  254. package/scripts/sw-stream.sh +13 -4
  255. package/scripts/sw-swarm.sh +20 -7
  256. package/scripts/sw-team-stages.sh +13 -6
  257. package/scripts/sw-templates.sh +7 -31
  258. package/scripts/sw-testgen.sh +17 -6
  259. package/scripts/sw-tmux-pipeline.sh +4 -1
  260. package/scripts/sw-tmux-role-color.sh +2 -0
  261. package/scripts/sw-tmux-status.sh +1 -1
  262. package/scripts/sw-tmux.sh +37 -1
  263. package/scripts/sw-trace.sh +3 -1
  264. package/scripts/sw-tracker-github.sh +3 -0
  265. package/scripts/sw-tracker-jira.sh +3 -0
  266. package/scripts/sw-tracker-linear.sh +3 -0
  267. package/scripts/sw-tracker.sh +3 -1
  268. package/scripts/sw-triage.sh +3 -2
  269. package/scripts/sw-upgrade.sh +3 -1
  270. package/scripts/sw-ux.sh +5 -2
  271. package/scripts/sw-webhook.sh +5 -2
  272. package/scripts/sw-widgets.sh +9 -4
  273. package/scripts/sw-worktree.sh +15 -3
  274. package/scripts/test-skill-injection.sh +1233 -0
  275. package/templates/pipelines/autonomous.json +27 -3
  276. package/templates/pipelines/cost-aware.json +34 -8
  277. package/templates/pipelines/deployed.json +12 -0
  278. package/templates/pipelines/enterprise.json +12 -0
  279. package/templates/pipelines/fast.json +6 -0
  280. package/templates/pipelines/full.json +27 -3
  281. package/templates/pipelines/hotfix.json +6 -0
  282. package/templates/pipelines/standard.json +12 -0
  283. package/templates/pipelines/tdd.json +12 -0
@@ -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
- VERSION="3.1.0"
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" || echo "0")
137
- memory_hits=$(echo "$failure_log" | grep -ic "out of memory\|OOM\|heap" || echo "0")
138
- dependency_hits=$(echo "$failure_log" | grep -ic "dependency\|import\|require\|not found" || echo "0")
139
- error_hits=$(echo "$failure_log" | grep -c . || echo "0")
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
@@ -8,7 +8,8 @@
8
8
  # ║ ║
9
9
  # ║ --deploy Detect platform and generate deployed.json template ║
10
10
  # ╚═══════════════════════════════════════════════════════════════════════════╝
11
- VERSION="3.1.0"
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)
@@ -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.1.0"
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="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
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.1.0"
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 || echo "0")
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
  }
@@ -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
- VERSION="3.1.0"
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}"