shipwright-cli 1.7.1 → 1.9.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 (105) hide show
  1. package/.claude/agents/code-reviewer.md +90 -0
  2. package/.claude/agents/devops-engineer.md +142 -0
  3. package/.claude/agents/pipeline-agent.md +80 -0
  4. package/.claude/agents/shell-script-specialist.md +150 -0
  5. package/.claude/agents/test-specialist.md +196 -0
  6. package/.claude/hooks/post-tool-use.sh +38 -0
  7. package/.claude/hooks/pre-tool-use.sh +25 -0
  8. package/.claude/hooks/session-started.sh +37 -0
  9. package/README.md +212 -814
  10. package/claude-code/CLAUDE.md.shipwright +54 -0
  11. package/claude-code/hooks/notify-idle.sh +2 -2
  12. package/claude-code/hooks/session-start.sh +24 -0
  13. package/claude-code/hooks/task-completed.sh +6 -2
  14. package/claude-code/settings.json.template +12 -0
  15. package/dashboard/public/app.js +4422 -0
  16. package/dashboard/public/index.html +816 -0
  17. package/dashboard/public/styles.css +4755 -0
  18. package/dashboard/server.ts +4315 -0
  19. package/docs/KNOWN-ISSUES.md +18 -10
  20. package/docs/TIPS.md +38 -26
  21. package/docs/patterns/README.md +33 -23
  22. package/package.json +9 -5
  23. package/scripts/adapters/iterm2-adapter.sh +1 -1
  24. package/scripts/adapters/tmux-adapter.sh +52 -23
  25. package/scripts/adapters/wezterm-adapter.sh +26 -14
  26. package/scripts/lib/compat.sh +200 -0
  27. package/scripts/lib/helpers.sh +72 -0
  28. package/scripts/postinstall.mjs +72 -13
  29. package/scripts/{cct → sw} +109 -21
  30. package/scripts/sw-adversarial.sh +274 -0
  31. package/scripts/sw-architecture-enforcer.sh +330 -0
  32. package/scripts/sw-checkpoint.sh +390 -0
  33. package/scripts/{cct-cleanup.sh → sw-cleanup.sh} +3 -1
  34. package/scripts/sw-connect.sh +619 -0
  35. package/scripts/{cct-cost.sh → sw-cost.sh} +368 -34
  36. package/scripts/{cct-daemon.sh → sw-daemon.sh} +2217 -204
  37. package/scripts/sw-dashboard.sh +477 -0
  38. package/scripts/sw-developer-simulation.sh +252 -0
  39. package/scripts/sw-docs.sh +635 -0
  40. package/scripts/sw-doctor.sh +907 -0
  41. package/scripts/{cct-fix.sh → sw-fix.sh} +10 -6
  42. package/scripts/{cct-fleet.sh → sw-fleet.sh} +498 -22
  43. package/scripts/sw-github-checks.sh +521 -0
  44. package/scripts/sw-github-deploy.sh +533 -0
  45. package/scripts/sw-github-graphql.sh +972 -0
  46. package/scripts/sw-heartbeat.sh +293 -0
  47. package/scripts/{cct-init.sh → sw-init.sh} +144 -11
  48. package/scripts/sw-intelligence.sh +1196 -0
  49. package/scripts/sw-jira.sh +643 -0
  50. package/scripts/sw-launchd.sh +364 -0
  51. package/scripts/sw-linear.sh +648 -0
  52. package/scripts/{cct-logs.sh → sw-logs.sh} +72 -2
  53. package/scripts/{cct-loop.sh → sw-loop.sh} +534 -44
  54. package/scripts/{cct-memory.sh → sw-memory.sh} +321 -38
  55. package/scripts/sw-patrol-meta.sh +417 -0
  56. package/scripts/sw-pipeline-composer.sh +455 -0
  57. package/scripts/{cct-pipeline.sh → sw-pipeline.sh} +2319 -178
  58. package/scripts/sw-predictive.sh +820 -0
  59. package/scripts/{cct-prep.sh → sw-prep.sh} +339 -49
  60. package/scripts/{cct-ps.sh → sw-ps.sh} +6 -4
  61. package/scripts/{cct-reaper.sh → sw-reaper.sh} +6 -4
  62. package/scripts/sw-remote.sh +687 -0
  63. package/scripts/sw-self-optimize.sh +947 -0
  64. package/scripts/sw-session.sh +519 -0
  65. package/scripts/sw-setup.sh +234 -0
  66. package/scripts/sw-status.sh +605 -0
  67. package/scripts/{cct-templates.sh → sw-templates.sh} +9 -4
  68. package/scripts/sw-tmux.sh +591 -0
  69. package/scripts/sw-tracker-jira.sh +277 -0
  70. package/scripts/sw-tracker-linear.sh +292 -0
  71. package/scripts/sw-tracker.sh +409 -0
  72. package/scripts/{cct-upgrade.sh → sw-upgrade.sh} +103 -46
  73. package/scripts/{cct-worktree.sh → sw-worktree.sh} +3 -0
  74. package/templates/pipelines/autonomous.json +27 -5
  75. package/templates/pipelines/full.json +12 -0
  76. package/templates/pipelines/standard.json +12 -0
  77. package/tmux/{claude-teams-overlay.conf → shipwright-overlay.conf} +27 -9
  78. package/tmux/templates/accessibility.json +34 -0
  79. package/tmux/templates/api-design.json +35 -0
  80. package/tmux/templates/architecture.json +1 -0
  81. package/tmux/templates/bug-fix.json +9 -0
  82. package/tmux/templates/code-review.json +1 -0
  83. package/tmux/templates/compliance.json +36 -0
  84. package/tmux/templates/data-pipeline.json +36 -0
  85. package/tmux/templates/debt-paydown.json +34 -0
  86. package/tmux/templates/devops.json +1 -0
  87. package/tmux/templates/documentation.json +1 -0
  88. package/tmux/templates/exploration.json +1 -0
  89. package/tmux/templates/feature-dev.json +1 -0
  90. package/tmux/templates/full-stack.json +8 -0
  91. package/tmux/templates/i18n.json +34 -0
  92. package/tmux/templates/incident-response.json +36 -0
  93. package/tmux/templates/migration.json +1 -0
  94. package/tmux/templates/observability.json +35 -0
  95. package/tmux/templates/onboarding.json +33 -0
  96. package/tmux/templates/performance.json +35 -0
  97. package/tmux/templates/refactor.json +1 -0
  98. package/tmux/templates/release.json +35 -0
  99. package/tmux/templates/security-audit.json +8 -0
  100. package/tmux/templates/spike.json +34 -0
  101. package/tmux/templates/testing.json +1 -0
  102. package/tmux/tmux.conf +98 -9
  103. package/scripts/cct-doctor.sh +0 -414
  104. package/scripts/cct-session.sh +0 -284
  105. package/scripts/cct-status.sh +0 -169
@@ -0,0 +1,417 @@
1
+ #!/usr/bin/env bash
2
+ # ╔═══════════════════════════════════════════════════════════════════════════╗
3
+ # ║ sw-patrol-meta.sh — Shipwright Self-Improvement Patrol ║
4
+ # ║ Sourced by sw-daemon.sh during patrol — creates improvement issues ║
5
+ # ╚═══════════════════════════════════════════════════════════════════════════╝
6
+ # NOTE: This file is sourced (not executed). Do NOT add set -euo pipefail.
7
+ # All functions from the parent (sw-daemon.sh) are available.
8
+
9
+ # ─── Helper: Create issue if not duplicate ─────────────────────────────────
10
+ patrol_meta_create_issue() {
11
+ local title="$1"
12
+ local body="$2"
13
+ local extra_labels="${3:-}"
14
+
15
+ if [[ "${NO_GITHUB:-false}" == "true" ]]; then
16
+ info " [dry-run] Would create: $title"
17
+ return 0
18
+ fi
19
+
20
+ # Dedup: check if an open issue with this exact title already exists
21
+ local existing
22
+ existing=$(gh issue list --state open --search "$title" --json number,title --jq ".[].title" 2>/dev/null || echo "")
23
+ if echo "$existing" | grep -qF "$title" 2>/dev/null; then
24
+ info " Skipping duplicate: $title"
25
+ return 0
26
+ fi
27
+
28
+ local labels="auto-patrol,meta-improvement,ready-to-build"
29
+ if [[ -n "$extra_labels" ]]; then
30
+ labels="${labels},${extra_labels}"
31
+ fi
32
+
33
+ gh issue create \
34
+ --title "$title" \
35
+ --body "$body" \
36
+ --label "$labels" 2>/dev/null || {
37
+ warn " Failed to create issue: $title"
38
+ return 1
39
+ }
40
+
41
+ issues_created=$((issues_created + 1))
42
+ total_findings=$((total_findings + 1))
43
+ emit_event "patrol.meta_issue_created" "title=$title"
44
+ success " Created issue: $title"
45
+ }
46
+
47
+ # ─── Check 1: Untested Scripts ──────────────────────────────────────────────
48
+ patrol_meta_untested_scripts() {
49
+ info " Checking for untested Shipwright scripts..."
50
+ local count=0
51
+
52
+ for script in "$SCRIPT_DIR"/sw-*.sh; do
53
+ [[ ! -f "$script" ]] && continue
54
+ local base
55
+ base=$(basename "$script" .sh)
56
+
57
+ # Skip test scripts themselves
58
+ [[ "$base" == *-test ]] && continue
59
+ # Skip sourced provider scripts (no standalone execution)
60
+ [[ "$base" == sw-tracker-linear ]] && continue
61
+ [[ "$base" == sw-tracker-jira ]] && continue
62
+ [[ "$base" == sw-patrol-meta ]] && continue
63
+
64
+ local test_file="$SCRIPT_DIR/${base}-test.sh"
65
+ if [[ ! -f "$test_file" ]]; then
66
+ count=$((count + 1))
67
+ patrol_meta_create_issue \
68
+ "[Meta] Add tests for ${base}.sh" \
69
+ "## Missing Test Suite
70
+
71
+ The script \`scripts/${base}.sh\` does not have a corresponding test file \`scripts/${base}-test.sh\`.
72
+
73
+ ### Requirements
74
+ - Create \`scripts/${base}-test.sh\` following the existing test patterns
75
+ - Use mock environment (TEMP_DIR with mock binaries on PATH)
76
+ - Include PASS/FAIL counters, colored output, summary section
77
+ - Add ERR trap: \`trap 'echo \"ERROR: \\\$BASH_SOURCE:\\\$LINENO exited with status \\\$?\" >&2' ERR\`
78
+ - Register in \`package.json\` test script chain
79
+ - Aim for at least 10 meaningful test cases
80
+
81
+ ### Context
82
+ All other Shipwright scripts follow this test pattern. See \`scripts/sw-daemon-test.sh\` as a reference.
83
+
84
+ _Auto-generated by Shipwright meta-patrol_"
85
+ fi
86
+ done
87
+
88
+ if [[ "$count" -eq 0 ]]; then
89
+ echo -e " ${GREEN}●${RESET} All scripts have test suites"
90
+ else
91
+ echo -e " ${CYAN}●${RESET} Found ${count} untested script(s)"
92
+ fi
93
+ }
94
+
95
+ # ─── Check 2: Bash 3.2 Compatibility ───────────────────────────────────────
96
+ patrol_meta_bash_compat() {
97
+ info " Checking Bash 3.2 compatibility..."
98
+ local count=0
99
+
100
+ for script in "$SCRIPT_DIR"/sw-*.sh; do
101
+ [[ ! -f "$script" ]] && continue
102
+ local base
103
+ base=$(basename "$script")
104
+
105
+ # Look for Bash 4+ patterns
106
+ local violations=""
107
+ local v
108
+
109
+ # declare -A (associative arrays)
110
+ v=$(grep -n 'declare -A' "$script" 2>/dev/null | head -3 || true)
111
+ [[ -n "$v" ]] && violations="${violations}\n- \`declare -A\` (associative arrays): ${v}"
112
+
113
+ # readarray / mapfile
114
+ v=$(grep -n 'readarray\|mapfile' "$script" 2>/dev/null | head -3 || true)
115
+ [[ -n "$v" ]] && violations="${violations}\n- \`readarray/mapfile\`: ${v}"
116
+
117
+ # ${var,,} lowercase
118
+ v=$(grep -n '${[a-zA-Z_]*,,}' "$script" 2>/dev/null | head -3 || true)
119
+ [[ -n "$v" ]] && violations="${violations}\n- \`\${var,,}\` lowercase: ${v}"
120
+
121
+ # ${var^^} uppercase
122
+ v=$(grep -n '${[a-zA-Z_]*\^\^}' "$script" 2>/dev/null | head -3 || true)
123
+ [[ -n "$v" ]] && violations="${violations}\n- \`\${var^^}\` uppercase: ${v}"
124
+
125
+ if [[ -n "$violations" ]]; then
126
+ count=$((count + 1))
127
+ patrol_meta_create_issue \
128
+ "[Meta] Fix Bash 3.2 incompatibility in ${base}" \
129
+ "## Bash 3.2 Compatibility Issue
130
+
131
+ The script \`scripts/${base}\` uses Bash 4+ features that break on macOS default Bash (3.2).
132
+
133
+ ### Violations Found
134
+ $(echo -e "$violations")
135
+
136
+ ### Required Changes
137
+ - Replace \`declare -A\` with indexed arrays or separate variables
138
+ - Replace \`readarray\` with \`while IFS= read -r\` loops
139
+ - Replace \`\${var,,}\` with \`echo \"\$var\" | tr '[:upper:]' '[:lower:]'\`
140
+ - Replace \`\${var^^}\` with \`echo \"\$var\" | tr '[:lower:]' '[:upper:]'\`
141
+
142
+ _Auto-generated by Shipwright meta-patrol_"
143
+ fi
144
+ done
145
+
146
+ if [[ "$count" -eq 0 ]]; then
147
+ echo -e " ${GREEN}●${RESET} All scripts are Bash 3.2 compatible"
148
+ else
149
+ echo -e " ${CYAN}●${RESET} Found ${count} script(s) with Bash 4+ patterns"
150
+ fi
151
+ }
152
+
153
+ # ─── Check 3: VERSION Sync ─────────────────────────────────────────────────
154
+ patrol_meta_version_sync() {
155
+ info " Checking VERSION consistency..."
156
+ local versions=""
157
+ local mismatches=""
158
+ local reference_version=""
159
+
160
+ for script in "$SCRIPT_DIR"/sw-*.sh "$SCRIPT_DIR/sw"; do
161
+ [[ ! -f "$script" ]] && continue
162
+ local base
163
+ base=$(basename "$script")
164
+ local ver
165
+ ver=$(grep -m1 '^VERSION=' "$script" 2>/dev/null | sed 's/VERSION="//' | sed 's/"//' || true)
166
+ if [[ -n "$ver" ]]; then
167
+ if [[ -z "$reference_version" ]]; then
168
+ reference_version="$ver"
169
+ elif [[ "$ver" != "$reference_version" ]]; then
170
+ mismatches="${mismatches}\n- \`${base}\`: ${ver} (expected ${reference_version})"
171
+ fi
172
+ fi
173
+ done
174
+
175
+ if [[ -n "$mismatches" ]]; then
176
+ patrol_meta_create_issue \
177
+ "[Meta] Fix VERSION mismatch across scripts" \
178
+ "## VERSION Mismatch
179
+
180
+ Some scripts have different VERSION values. All scripts should use the same version.
181
+
182
+ ### Reference Version: \`${reference_version}\`
183
+
184
+ ### Mismatches
185
+ $(echo -e "$mismatches")
186
+
187
+ ### Fix
188
+ Update all VERSION= lines to match the reference version.
189
+
190
+ _Auto-generated by Shipwright meta-patrol_"
191
+ echo -e " ${CYAN}●${RESET} VERSION mismatch detected"
192
+ else
193
+ echo -e " ${GREEN}●${RESET} All script versions match: ${reference_version:-unknown}"
194
+ fi
195
+ }
196
+
197
+ # ─── Check 4: DORA Trends (30-day) ─────────────────────────────────────────
198
+ patrol_meta_dora_trends() {
199
+ info " Analyzing 30-day DORA trends..."
200
+
201
+ if [[ ! -f "$EVENTS_FILE" ]]; then
202
+ echo -e " ${DIM}●${RESET} No events data available"
203
+ return
204
+ fi
205
+
206
+ local now_e
207
+ now_e=$(now_epoch)
208
+ local thirty_days_ago=$(( now_e - 2592000 ))
209
+ local fifteen_days_ago=$(( now_e - 1296000 ))
210
+
211
+ # Compare first half (days 16-30) vs second half (days 1-15)
212
+ local first_half_lt second_half_lt
213
+ first_half_lt=$(jq -s "[.[] | select(.type == \"pipeline.completed\" and .result == \"success\" and (.ts_epoch // 0) >= $thirty_days_ago and (.ts_epoch // 0) < $fifteen_days_ago) | .duration_s // 0] | if length > 0 then (add / length) else 0 end" "$EVENTS_FILE" 2>/dev/null || echo "0")
214
+ second_half_lt=$(jq -s "[.[] | select(.type == \"pipeline.completed\" and .result == \"success\" and (.ts_epoch // 0) >= $fifteen_days_ago) | .duration_s // 0] | if length > 0 then (add / length) else 0 end" "$EVENTS_FILE" 2>/dev/null || echo "0")
215
+
216
+ # Check if lead time regressed > 20%
217
+ if [[ "$first_half_lt" != "0" ]] && [[ "$second_half_lt" != "0" ]]; then
218
+ local increase_pct
219
+ increase_pct=$(echo "$first_half_lt $second_half_lt" | awk '{if ($1 > 0) printf "%.0f", (($2 - $1) / $1) * 100; else print "0"}')
220
+
221
+ if [[ "$increase_pct" -gt 20 ]]; then
222
+ patrol_meta_create_issue \
223
+ "[Meta] Investigate lead time regression (+${increase_pct}%)" \
224
+ "## DORA Lead Time Regression
225
+
226
+ Average pipeline lead time has increased by **${increase_pct}%** over the last 30 days.
227
+
228
+ | Period | Avg Lead Time |
229
+ |--------|--------------|
230
+ | Days 16-30 | ${first_half_lt}s |
231
+ | Days 1-15 | ${second_half_lt}s |
232
+
233
+ ### Suggested Actions
234
+ - Check if pipeline template complexity has increased
235
+ - Review build stage iteration counts
236
+ - Look for test suite slowdowns
237
+ - Check if retry rates have increased
238
+
239
+ _Auto-generated by Shipwright meta-patrol_"
240
+ echo -e " ${CYAN}●${RESET} Lead time regression: +${increase_pct}%"
241
+ else
242
+ echo -e " ${GREEN}●${RESET} Lead time stable (${increase_pct}% change)"
243
+ fi
244
+ else
245
+ echo -e " ${DIM}●${RESET} Insufficient data for trend analysis"
246
+ fi
247
+ }
248
+
249
+ # ─── Check 5: Template Effectiveness ───────────────────────────────────────
250
+ patrol_meta_template_effectiveness() {
251
+ info " Analyzing pipeline template effectiveness..."
252
+
253
+ if [[ ! -f "$EVENTS_FILE" ]]; then
254
+ echo -e " ${DIM}●${RESET} No events data available"
255
+ return
256
+ fi
257
+
258
+ # Get success rates by template from the last 30 days
259
+ local template_stats
260
+ template_stats=$(jq -s '
261
+ [.[] | select(.type == "pipeline.completed" and (.ts_epoch // 0) > (now - 2592000))]
262
+ | group_by(.template // "unknown")
263
+ | map({
264
+ template: .[0].template // "unknown",
265
+ total: length,
266
+ successes: [.[] | select(.result == "success")] | length
267
+ })
268
+ | map(. + { rate: (if .total > 0 then (.successes * 100 / .total) else 0 end) })
269
+ | sort_by(-.rate)
270
+ ' "$EVENTS_FILE" 2>/dev/null || echo "[]")
271
+
272
+ local template_count
273
+ template_count=$(echo "$template_stats" | jq 'length' 2>/dev/null || echo "0")
274
+
275
+ if [[ "$template_count" -gt 1 ]]; then
276
+ local best_template best_rate worst_template worst_rate
277
+ best_template=$(echo "$template_stats" | jq -r '.[0].template' 2>/dev/null || echo "unknown")
278
+ best_rate=$(echo "$template_stats" | jq -r '.[0].rate' 2>/dev/null || echo "0")
279
+ worst_template=$(echo "$template_stats" | jq -r '.[-1].template' 2>/dev/null || echo "unknown")
280
+ worst_rate=$(echo "$template_stats" | jq -r '.[-1].rate' 2>/dev/null || echo "0")
281
+
282
+ local rate_diff=$(( ${best_rate%.*} - ${worst_rate%.*} ))
283
+
284
+ if [[ "$rate_diff" -gt 30 ]]; then
285
+ patrol_meta_create_issue \
286
+ "[Meta] Consider defaulting to '${best_template}' pipeline template" \
287
+ "## Template Effectiveness Analysis
288
+
289
+ The **${best_template}** template significantly outperforms other templates.
290
+
291
+ ### Success Rates (Last 30 Days)
292
+ $(echo "$template_stats" | jq -r '.[] | "| \(.template) | \(.successes)/\(.total) | \(.rate)% |"' 2>/dev/null || echo "No data")
293
+
294
+ ### Recommendation
295
+ Consider setting \`pipeline_template: \"${best_template}\"\` as the default in \`daemon-config.json\`.
296
+
297
+ _Auto-generated by Shipwright meta-patrol_"
298
+ echo -e " ${CYAN}●${RESET} '${best_template}' outperforms by ${rate_diff}%"
299
+ else
300
+ echo -e " ${GREEN}●${RESET} Templates performing similarly (${rate_diff}% spread)"
301
+ fi
302
+ else
303
+ echo -e " ${DIM}●${RESET} Not enough template variety for comparison"
304
+ fi
305
+ }
306
+
307
+ # ─── Check 6: Memory Pruning ───────────────────────────────────────────────
308
+ patrol_meta_memory_pruning() {
309
+ info " Checking memory file sizes..."
310
+
311
+ local memory_dir="$HOME/.shipwright/memory"
312
+ if [[ ! -d "$memory_dir" ]]; then
313
+ echo -e " ${DIM}●${RESET} No memory directory"
314
+ return
315
+ fi
316
+
317
+ local total_size
318
+ total_size=$(du -sk "$memory_dir" 2>/dev/null | awk '{print $1}' || echo "0")
319
+ local total_mb=$(( total_size / 1024 ))
320
+
321
+ if [[ "$total_mb" -gt 10 ]]; then
322
+ patrol_meta_create_issue \
323
+ "[Meta] Prune memory files (${total_mb}MB)" \
324
+ "## Memory Storage Cleanup Needed
325
+
326
+ The Shipwright memory directory has grown to **${total_mb}MB**.
327
+
328
+ ### Location
329
+ \`~/.shipwright/memory/\`
330
+
331
+ ### Suggested Actions
332
+ - Archive old memory files (older than 90 days)
333
+ - Deduplicate similar failure patterns
334
+ - Compress rarely-accessed memories
335
+ - Consider implementing automatic memory rotation
336
+
337
+ _Auto-generated by Shipwright meta-patrol_"
338
+ echo -e " ${CYAN}●${RESET} Memory files: ${total_mb}MB (needs pruning)"
339
+ else
340
+ echo -e " ${GREEN}●${RESET} Memory files: ${total_mb}MB (healthy)"
341
+ fi
342
+ }
343
+
344
+ # ─── Check 7: Event Analysis (Top Recurring Failures) ──────────────────────
345
+ patrol_meta_event_analysis() {
346
+ info " Analyzing recurring failure patterns..."
347
+
348
+ if [[ ! -f "$EVENTS_FILE" ]]; then
349
+ echo -e " ${DIM}●${RESET} No events data available"
350
+ return
351
+ fi
352
+
353
+ # Find top 3 most common failure reasons in the last 7 days
354
+ local now_e
355
+ now_e=$(now_epoch)
356
+ local seven_days_ago=$(( now_e - 604800 ))
357
+
358
+ local top_failures
359
+ top_failures=$(jq -s "
360
+ [.[] | select(.type == \"pipeline.completed\" and .result != \"success\" and (.ts_epoch // 0) >= $seven_days_ago)]
361
+ | group_by(.failed_stage // \"unknown\")
362
+ | map({ stage: .[0].failed_stage // \"unknown\", count: length })
363
+ | sort_by(-.count)
364
+ | .[0:3]
365
+ " "$EVENTS_FILE" 2>/dev/null || echo "[]")
366
+
367
+ local failure_count
368
+ failure_count=$(echo "$top_failures" | jq 'length' 2>/dev/null || echo "0")
369
+
370
+ if [[ "$failure_count" -gt 0 ]]; then
371
+ local created=0
372
+ while IFS= read -r failure; do
373
+ local stage count
374
+ stage=$(echo "$failure" | jq -r '.stage' 2>/dev/null || echo "unknown")
375
+ count=$(echo "$failure" | jq -r '.count' 2>/dev/null || echo "0")
376
+
377
+ if [[ "$count" -ge 3 ]]; then
378
+ patrol_meta_create_issue \
379
+ "[Meta] Investigate recurring '${stage}' stage failures (${count}x in 7d)" \
380
+ "## Recurring Pipeline Failure
381
+
382
+ The **${stage}** stage has failed **${count} times** in the last 7 days.
383
+
384
+ ### Suggested Investigation
385
+ - Check events.jsonl for error details: \`jq 'select(.type == \"pipeline.completed\" and .failed_stage == \"${stage}\")' ~/.shipwright/events.jsonl\`
386
+ - Review memory patterns: \`shipwright memory search \"${stage}\"\`
387
+ - Check if a specific issue type triggers this failure
388
+
389
+ _Auto-generated by Shipwright meta-patrol_"
390
+ created=$((created + 1))
391
+ fi
392
+ done < <(echo "$top_failures" | jq -c '.[]' 2>/dev/null)
393
+
394
+ if [[ "$created" -gt 0 ]]; then
395
+ echo -e " ${CYAN}●${RESET} Created ${created} issue(s) for recurring failures"
396
+ else
397
+ echo -e " ${GREEN}●${RESET} No recurring failures above threshold"
398
+ fi
399
+ else
400
+ echo -e " ${GREEN}●${RESET} No failures in last 7 days"
401
+ fi
402
+ }
403
+
404
+ # ─── Main Entry Point ──────────────────────────────────────────────────────
405
+ patrol_meta_run() {
406
+ echo -e "\n ${BOLD}Meta Self-Improvement Checks${RESET}"
407
+
408
+ patrol_meta_untested_scripts
409
+ patrol_meta_bash_compat
410
+ patrol_meta_version_sync
411
+ patrol_meta_dora_trends
412
+ patrol_meta_template_effectiveness
413
+ patrol_meta_memory_pruning
414
+ patrol_meta_event_analysis
415
+
416
+ echo ""
417
+ }