shipwright-cli 2.3.0 → 2.4.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 (112) hide show
  1. package/README.md +82 -20
  2. package/config/policy.json +160 -2
  3. package/config/policy.schema.json +162 -1
  4. package/dashboard/public/index.html +1 -1
  5. package/dashboard/src/core/api.test.ts +362 -0
  6. package/dashboard/src/core/router.test.ts +266 -0
  7. package/dashboard/src/core/state.test.ts +235 -0
  8. package/dashboard/src/core/ws.test.ts +216 -0
  9. package/dashboard/src/design/icons.test.ts +105 -0
  10. package/dashboard/src/design/tokens.test.ts +204 -0
  11. package/dashboard/tsconfig.json +1 -1
  12. package/dashboard/vitest.config.ts +27 -0
  13. package/package.json +23 -4
  14. package/scripts/lib/pipeline-stages.sh +59 -0
  15. package/scripts/sw +1 -1
  16. package/scripts/sw-activity.sh +1 -1
  17. package/scripts/sw-adaptive.sh +1 -1
  18. package/scripts/sw-adversarial.sh +1 -1
  19. package/scripts/sw-architecture-enforcer.sh +1 -1
  20. package/scripts/sw-auth.sh +1 -1
  21. package/scripts/sw-autonomous.sh +230 -13
  22. package/scripts/sw-changelog.sh +1 -1
  23. package/scripts/sw-checkpoint.sh +1 -1
  24. package/scripts/sw-ci.sh +1 -1
  25. package/scripts/sw-cleanup.sh +1 -1
  26. package/scripts/sw-code-review.sh +1 -1
  27. package/scripts/sw-connect.sh +1 -1
  28. package/scripts/sw-context.sh +1 -1
  29. package/scripts/sw-cost.sh +1 -1
  30. package/scripts/sw-daemon.sh +1 -1
  31. package/scripts/sw-dashboard.sh +1 -1
  32. package/scripts/sw-db.sh +1 -1
  33. package/scripts/sw-decompose.sh +1 -1
  34. package/scripts/sw-deps.sh +1 -1
  35. package/scripts/sw-developer-simulation.sh +1 -1
  36. package/scripts/sw-discovery.sh +1 -1
  37. package/scripts/sw-doc-fleet.sh +1 -1
  38. package/scripts/sw-docs-agent.sh +1 -1
  39. package/scripts/sw-docs.sh +1 -1
  40. package/scripts/sw-doctor.sh +1 -1
  41. package/scripts/sw-dora.sh +1 -1
  42. package/scripts/sw-durable.sh +1 -1
  43. package/scripts/sw-e2e-orchestrator.sh +1 -1
  44. package/scripts/sw-eventbus.sh +1 -1
  45. package/scripts/sw-evidence.sh +664 -0
  46. package/scripts/sw-feedback.sh +1 -1
  47. package/scripts/sw-fix.sh +1 -1
  48. package/scripts/sw-fleet-discover.sh +1 -1
  49. package/scripts/sw-fleet-viz.sh +1 -1
  50. package/scripts/sw-fleet.sh +1 -1
  51. package/scripts/sw-github-app.sh +1 -1
  52. package/scripts/sw-github-checks.sh +1 -1
  53. package/scripts/sw-github-deploy.sh +1 -1
  54. package/scripts/sw-github-graphql.sh +1 -1
  55. package/scripts/sw-guild.sh +1 -1
  56. package/scripts/sw-heartbeat.sh +1 -1
  57. package/scripts/sw-hygiene.sh +1 -1
  58. package/scripts/sw-incident.sh +244 -1
  59. package/scripts/sw-init.sh +1 -1
  60. package/scripts/sw-instrument.sh +1 -1
  61. package/scripts/sw-intelligence.sh +1 -1
  62. package/scripts/sw-jira.sh +1 -1
  63. package/scripts/sw-launchd.sh +1 -1
  64. package/scripts/sw-linear.sh +1 -1
  65. package/scripts/sw-logs.sh +1 -1
  66. package/scripts/sw-loop.sh +1 -1
  67. package/scripts/sw-memory.sh +1 -1
  68. package/scripts/sw-mission-control.sh +1 -1
  69. package/scripts/sw-model-router.sh +1 -1
  70. package/scripts/sw-otel.sh +1 -1
  71. package/scripts/sw-oversight.sh +1 -1
  72. package/scripts/sw-pipeline-composer.sh +1 -1
  73. package/scripts/sw-pipeline-vitals.sh +1 -1
  74. package/scripts/sw-pipeline.sh +1 -1
  75. package/scripts/sw-pm.sh +1 -1
  76. package/scripts/sw-pr-lifecycle.sh +177 -5
  77. package/scripts/sw-predictive.sh +1 -1
  78. package/scripts/sw-prep.sh +1 -1
  79. package/scripts/sw-ps.sh +1 -1
  80. package/scripts/sw-public-dashboard.sh +1 -1
  81. package/scripts/sw-quality.sh +1 -1
  82. package/scripts/sw-reaper.sh +1 -1
  83. package/scripts/sw-regression.sh +1 -1
  84. package/scripts/sw-release-manager.sh +1 -1
  85. package/scripts/sw-release.sh +1 -1
  86. package/scripts/sw-remote.sh +1 -1
  87. package/scripts/sw-replay.sh +1 -1
  88. package/scripts/sw-retro.sh +4 -1
  89. package/scripts/sw-review-rerun.sh +220 -0
  90. package/scripts/sw-scale.sh +1 -1
  91. package/scripts/sw-security-audit.sh +1 -1
  92. package/scripts/sw-self-optimize.sh +99 -1
  93. package/scripts/sw-session.sh +1 -1
  94. package/scripts/sw-setup.sh +1 -1
  95. package/scripts/sw-standup.sh +1 -1
  96. package/scripts/sw-status.sh +1 -1
  97. package/scripts/sw-strategic.sh +1 -1
  98. package/scripts/sw-stream.sh +1 -1
  99. package/scripts/sw-swarm.sh +1 -1
  100. package/scripts/sw-team-stages.sh +1 -1
  101. package/scripts/sw-templates.sh +1 -1
  102. package/scripts/sw-testgen.sh +1 -1
  103. package/scripts/sw-tmux-pipeline.sh +1 -1
  104. package/scripts/sw-tmux.sh +1 -1
  105. package/scripts/sw-trace.sh +1 -1
  106. package/scripts/sw-tracker.sh +1 -1
  107. package/scripts/sw-triage.sh +198 -11
  108. package/scripts/sw-upgrade.sh +1 -1
  109. package/scripts/sw-ux.sh +1 -1
  110. package/scripts/sw-webhook.sh +1 -1
  111. package/scripts/sw-widgets.sh +1 -1
  112. package/scripts/sw-worktree.sh +1 -1
@@ -7,7 +7,7 @@
7
7
  set -euo pipefail
8
8
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
9
9
 
10
- VERSION="2.3.0"
10
+ VERSION="2.4.0"
11
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
12
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
13
13
 
@@ -64,6 +64,7 @@ STATE_FILE="${STATE_DIR}/state.json"
64
64
  HISTORY_FILE="${STATE_DIR}/history.jsonl"
65
65
  CONFIG_FILE="${STATE_DIR}/config.json"
66
66
  CYCLE_COUNTER="${STATE_DIR}/cycle-counter.txt"
67
+ AUTONOMOUS_OVERLAP_FILE="${HOME}/.shipwright/autonomous-state.json"
67
68
 
68
69
  # Ensure directories exist
69
70
  ensure_state_dir() {
@@ -80,7 +81,8 @@ ensure_state_dir() {
80
81
  "max_consecutive_failures": 3,
81
82
  "learning_enabled": true,
82
83
  "self_improvement_enabled": true,
83
- "shipwright_self_improvement_threshold": 3
84
+ "shipwright_self_improvement_threshold": 3,
85
+ "daemon_aware": true
84
86
  }
85
87
  EOF
86
88
  info "Created default config at ${CONFIG_FILE}"
@@ -179,6 +181,110 @@ record_cycle() {
179
181
  >> "$HISTORY_FILE"
180
182
  }
181
183
 
184
+ # ─── Strategic Agent Integration ────────────────────────────────────────────
185
+
186
+ # Compute word-overlap similarity between two titles (0-100). Used for dedup.
187
+ title_similarity_percent() {
188
+ local title_a="$1"
189
+ local title_b="$2"
190
+ local words_a words_b
191
+ words_a=$(printf '%s' "$title_a" | tr '[:upper:]' '[:lower:]' | tr -cs '[:alnum:]' '\n' | \
192
+ grep -vE '^(a|an|the|and|or|for|to|in|of|is|it|by|on|at|with|from|based)$' | grep -v '^$' | sort -u || true)
193
+ words_b=$(printf '%s' "$title_b" | tr '[:upper:]' '[:lower:]' | tr -cs '[:alnum:]' '\n' | \
194
+ grep -vE '^(a|an|the|and|or|for|to|in|of|is|it|by|on|at|with|from|based)$' | grep -v '^$' | sort -u || true)
195
+ [[ -z "$words_a" || -z "$words_b" ]] && echo "0" && return 0
196
+ local count_a count_b shared min_count
197
+ count_a=$(printf '%s\n' "$words_a" | wc -l | tr -d ' ')
198
+ count_b=$(printf '%s\n' "$words_b" | wc -l | tr -d ' ')
199
+ shared=$(comm -12 <(printf '%s\n' "$words_a") <(printf '%s\n' "$words_b") | wc -l | tr -d ' ')
200
+ [[ "$count_a" -le "$count_b" ]] && min_count="$count_a" || min_count="$count_b"
201
+ [[ "$min_count" -eq 0 ]] && echo "0" && return 0
202
+ echo $(( shared * 100 / min_count ))
203
+ }
204
+
205
+ # Record that a strategic issue has been acknowledged so we don't re-process it.
206
+ autonomous_register_strategic_overlap() {
207
+ local title="$1"
208
+ local issue_num="${2:-}"
209
+ mkdir -p "$(dirname "$AUTONOMOUS_OVERLAP_FILE")"
210
+ local tmp_file
211
+ tmp_file=$(mktemp "${TMPDIR:-/tmp}/sw-autonomous-overlap.XXXXXX")
212
+ if [[ -f "$AUTONOMOUS_OVERLAP_FILE" ]]; then
213
+ jq --arg t "$title" --arg n "$issue_num" --arg ts "$(now_iso)" \
214
+ '.strategic_acknowledged = ((.strategic_acknowledged // []) + [{"title": $t, "issue": (if $n != "" then ($n | tonumber) else null end), "acked_at": $ts}])' \
215
+ "$AUTONOMOUS_OVERLAP_FILE" > "$tmp_file" 2>/dev/null || true
216
+ else
217
+ jq -n --arg t "$title" --arg n "$issue_num" --arg ts "$(now_iso)" \
218
+ '{strategic_acknowledged: [{"title": $t, "issue": (if $n != "" then ($n | tonumber) else null end), "acked_at": $ts}]}' \
219
+ > "$tmp_file" 2>/dev/null || true
220
+ fi
221
+ mv "$tmp_file" "$AUTONOMOUS_OVERLAP_FILE" 2>/dev/null || rm -f "$tmp_file" || true
222
+ }
223
+
224
+ # Read strategic.issue_created events from last 24h; return JSON array of findings.
225
+ # Excludes issues already in strategic_acknowledged. Resolves issue numbers via gh when possible.
226
+ ingest_strategic_findings() {
227
+ local events_file="${EVENTS_FILE:-${HOME}/.shipwright/events.jsonl}"
228
+ [[ ! -f "$events_file" ]] && echo "[]" && return 0
229
+
230
+ local now_e
231
+ now_e=$(now_epoch 2>/dev/null || date +%s)
232
+ local cutoff_e=$(( now_e - 86400 ))
233
+
234
+ local acked_titles
235
+ acked_titles=""
236
+ if [[ -f "$AUTONOMOUS_OVERLAP_FILE" ]]; then
237
+ acked_titles=$(jq -r '.strategic_acknowledged // [] | .[].title' "$AUTONOMOUS_OVERLAP_FILE" 2>/dev/null || echo "")
238
+ fi
239
+
240
+ local findings="[]"
241
+ while IFS= read -r line; do
242
+ [[ -z "$line" ]] && continue
243
+ local ev_type ts_epoch title priority complexity
244
+ ev_type=$(echo "$line" | jq -r '.type // ""' 2>/dev/null) || true
245
+ [[ "$ev_type" != "strategic.issue_created" ]] && continue
246
+ ts_epoch=$(echo "$line" | jq -r '.ts_epoch // 0' 2>/dev/null) || true
247
+ [[ "$ts_epoch" -lt "$cutoff_e" ]] && continue
248
+ title=$(echo "$line" | jq -r '.title // ""' 2>/dev/null) || true
249
+ [[ -z "$title" ]] && continue
250
+
251
+ local already_acked=false
252
+ if [[ -n "$acked_titles" ]]; then
253
+ while IFS= read -r acked; do
254
+ [[ -z "$acked" ]] && continue
255
+ local sim
256
+ sim=$(title_similarity_percent "$title" "$acked" 2>/dev/null || echo "0")
257
+ if [[ "${sim:-0}" -ge 60 ]]; then
258
+ already_acked=true
259
+ break
260
+ fi
261
+ done <<< "$acked_titles"
262
+ fi
263
+ [[ "$already_acked" == true ]] && continue
264
+
265
+ priority=$(echo "$line" | jq -r '.priority // "P2"' 2>/dev/null) || true
266
+ complexity=$(echo "$line" | jq -r '.complexity // "standard"' 2>/dev/null) || true
267
+
268
+ local issue_num=""
269
+ if [[ "${NO_GITHUB:-false}" != "true" ]] && command -v gh &>/dev/null; then
270
+ issue_num=$(gh issue list --state open --search "$title" --json number -q '.[0].number' 2>/dev/null || echo "")
271
+ fi
272
+
273
+ local finding
274
+ finding=$(jq -n \
275
+ --arg title "$title" \
276
+ --arg priority "$priority" \
277
+ --arg complexity "$complexity" \
278
+ --arg source "strategic" \
279
+ --arg issue "$issue_num" \
280
+ '{title: $title, description: ("Strategic: " + $title), priority: $priority, effort: (if $complexity == "fast" then "S" elif $complexity == "full" then "L" else "M" end), labels: ["strategic", "shipwright"], category: "strategic", source: $source, issue: $issue}')
281
+
282
+ findings=$(echo "$findings" | jq -c --argjson f "$finding" '. + [$f]' 2>/dev/null || echo "$findings")
283
+ done < <(grep '"strategic.issue_created"' "$events_file" 2>/dev/null || true)
284
+
285
+ echo "$findings"
286
+ }
287
+
182
288
  # ─── Analysis Cycle ────────────────────────────────────────────────────────
183
289
 
184
290
  run_analysis_cycle() {
@@ -238,7 +344,52 @@ Output ONLY a JSON array, no other text.' --max-turns 3 > "$findings" 2>/dev/nul
238
344
  } > "$findings"
239
345
  fi
240
346
 
241
- cat "$findings"
347
+ # Ingest strategic findings and merge with Claude/heuristic findings
348
+ local strategic_json
349
+ strategic_json=$(ingest_strategic_findings 2>/dev/null || echo "[]")
350
+
351
+ local merged_file
352
+ merged_file=$(mktemp "${TMPDIR:-/tmp}/sw-autonomous-merged.XXXXXX")
353
+ # Parse claude findings (may be invalid if Claude wrapped in markdown)
354
+ local claude_array
355
+ claude_array=$(jq -c '.' "$findings" 2>/dev/null || echo "[]")
356
+ # Extract JSON array from potential markdown-wrapped output
357
+ if ! echo "$claude_array" | jq -e 'type == "array"' &>/dev/null; then
358
+ claude_array=$(sed -n '/^\[/,/^\]/p' "$findings" 2>/dev/null | jq -c '.' 2>/dev/null || echo "[]")
359
+ fi
360
+ if ! echo "$claude_array" | jq -e 'type == "array"' &>/dev/null; then
361
+ claude_array="[]"
362
+ fi
363
+
364
+ # Filter: drop Claude findings that overlap (>=60% similar) with strategic
365
+ local filtered_claude="[]"
366
+ local strategic_titles
367
+ strategic_titles=$(echo "$strategic_json" | jq -r '.[].title' 2>/dev/null || true)
368
+ while IFS= read -r cf; do
369
+ [[ -z "$cf" ]] && continue
370
+ local ctitle
371
+ ctitle=$(echo "$cf" | jq -r '.title // ""' 2>/dev/null) || true
372
+ [[ -z "$ctitle" ]] && continue
373
+ local overlaps=false
374
+ if [[ -n "$strategic_titles" ]]; then
375
+ while IFS= read -r stitle; do
376
+ [[ -z "$stitle" ]] && continue
377
+ local sim
378
+ sim=$(title_similarity_percent "$ctitle" "$stitle" 2>/dev/null || echo "0")
379
+ if [[ "${sim:-0}" -ge 60 ]]; then
380
+ overlaps=true
381
+ break
382
+ fi
383
+ done <<< "$strategic_titles"
384
+ fi
385
+ [[ "$overlaps" == true ]] && continue
386
+ filtered_claude=$(echo "$filtered_claude" | jq -c --argjson f "$cf" '. + [$f]' 2>/dev/null || echo "$filtered_claude")
387
+ done < <(echo "$claude_array" | jq -c '.[]' 2>/dev/null || true)
388
+
389
+ # Merge: filtered Claude + strategic
390
+ jq -n -c --argjson c "$filtered_claude" --argjson s "$strategic_json" '$c + $s' 2>/dev/null > "$merged_file" || cat "$findings" > "$merged_file"
391
+ cat "$merged_file"
392
+ rm -f "$merged_file" "$findings" 2>/dev/null || true
242
393
  }
243
394
 
244
395
  # ─── Issue Creation ────────────────────────────────────────────────────────
@@ -255,11 +406,11 @@ create_issue_from_finding() {
255
406
  return 1
256
407
  fi
257
408
 
258
- # Check if issue already exists
409
+ # Dedup: check if an open issue with the same title already exists
259
410
  local existing
260
- existing=$(gh issue list --search "$title" --json number -q 'length' 2>/dev/null || echo "0")
261
- if [[ "${existing:-0}" -gt 0 ]]; then
262
- warn "Issue already exists: $title"
411
+ existing=$(gh issue list --state open --search "$title" --json number,title --limit 20 2>/dev/null | jq -r --arg t "$title" '[.[] | select(.title == $t) | .number][0] // empty' || echo "")
412
+ if [[ -n "$existing" ]]; then
413
+ warn "Open issue with same title already exists: #${existing} ($title)"
263
414
  return 1
264
415
  fi
265
416
 
@@ -290,8 +441,31 @@ create_issue_from_finding() {
290
441
  return 0
291
442
  }
292
443
 
444
+ # ─── Daemon Awareness ───────────────────────────────────────────────────────
445
+ # Check if daemon is running (avoids duplicate work: autonomous vs daemon)
446
+ daemon_is_running() {
447
+ local daemon_state="${HOME}/.shipwright/daemon-state.json"
448
+ local pid_file="${HOME}/.shipwright/daemon.pid"
449
+ if [[ ! -f "$daemon_state" ]]; then
450
+ return 1
451
+ fi
452
+ if [[ ! -f "$pid_file" ]]; then
453
+ return 1
454
+ fi
455
+ local pid
456
+ pid=$(cat "$pid_file" 2>/dev/null || true)
457
+ [[ -z "$pid" ]] && return 1
458
+ kill -0 "$pid" 2>/dev/null || return 1
459
+ # Optional: also check daemon field if present (API/dashboard format)
460
+ local daemon_status
461
+ daemon_status=$(jq -r '.daemon // "running"' "$daemon_state" 2>/dev/null || echo "running")
462
+ [[ "$daemon_status" == "running" ]] || return 1
463
+ return 0
464
+ }
465
+
293
466
  # ─── Issue Processing from Analysis ────────────────────────────────────────
294
- # Trigger pipeline for a finding issue (daemon will also pick it up; this runs immediately)
467
+ # Trigger pipeline for a finding issue. When daemon is running, delegate via
468
+ # ready-to-build label; otherwise trigger pipeline directly.
295
469
  trigger_pipeline_for_finding() {
296
470
  local issue_num="$1"
297
471
  local title="$2"
@@ -302,7 +476,27 @@ trigger_pipeline_for_finding() {
302
476
  return 0
303
477
  fi
304
478
 
305
- # Use recruit for model/team selection when available
479
+ local daemon_aware
480
+ daemon_aware=$(get_config "daemon_aware" "true")
481
+ if [[ "$daemon_aware" != "true" && "$daemon_aware" != "1" ]]; then
482
+ daemon_aware=false
483
+ else
484
+ daemon_aware=true
485
+ fi
486
+
487
+ if [[ "$daemon_aware" == "true" ]] && daemon_is_running; then
488
+ # Daemon running: label ready-to-build and let daemon pick it up
489
+ if [[ "$NO_GITHUB" != "true" ]]; then
490
+ gh issue edit "$issue_num" --add-label "ready-to-build" 2>/dev/null || {
491
+ warn "Failed to add ready-to-build label to #${issue_num}"
492
+ }
493
+ fi
494
+ info "Delegated issue #${issue_num} to daemon (labeled ready-to-build)"
495
+ emit_event "autonomous.delegated_to_daemon" "issue=$issue_num" "title=$title"
496
+ return 0
497
+ fi
498
+
499
+ # Daemon not running or DAEMON_AWARE=false: trigger pipeline directly
306
500
  local -a recruit_args=()
307
501
  if [[ -x "$SCRIPT_DIR/sw-recruit.sh" ]]; then
308
502
  local recruit_match
@@ -386,20 +580,37 @@ process_findings() {
386
580
  [[ -z "$finding" ]] && continue
387
581
 
388
582
  total=$((total + 1))
389
- [[ "$created" -ge "$max_per_cycle" ]] && break
390
583
 
391
- local title description priority effort labels category
584
+ local title description priority effort labels category source issue_num
392
585
  title=$(echo "$finding" | jq -r '.title // ""')
393
586
  description=$(echo "$finding" | jq -r '.description // ""')
394
587
  priority=$(echo "$finding" | jq -r '.priority // "medium"')
395
588
  effort=$(echo "$finding" | jq -r '.effort // "M"')
396
- labels=$(echo "$finding" | jq -r '.labels | join(",") // ""')
589
+ labels=$(echo "$finding" | jq -r '(.labels | if type == "array" then join(",") else . end) // ""')
397
590
  category=$(echo "$finding" | jq -r '.category // ""')
591
+ source=$(echo "$finding" | jq -r '.source // ""')
592
+ issue_num=$(echo "$finding" | jq -r '.issue // ""')
398
593
 
399
594
  if [[ -z "$title" ]]; then
400
595
  continue
401
596
  fi
402
597
 
598
+ # Strategic findings: issue already created by strategic agent; trigger pipeline and register, skip create
599
+ if [[ "$source" == "strategic" ]]; then
600
+ [[ -z "$issue_num" && "${NO_GITHUB:-false}" != "true" ]] && command -v gh &>/dev/null && \
601
+ issue_num=$(gh issue list --state open --search "$title" --json number -q '.[0].number' 2>/dev/null || echo "")
602
+ if [[ -n "$issue_num" && "$issue_num" =~ ^[0-9]+$ ]]; then
603
+ trigger_pipeline_for_finding "$issue_num" "$title"
604
+ autonomous_register_strategic_overlap "$title" "$issue_num" || true
605
+ info "Acknowledged strategic finding: #${issue_num} $title"
606
+ else
607
+ autonomous_register_strategic_overlap "$title" "" || true
608
+ fi
609
+ continue
610
+ fi
611
+
612
+ [[ "$created" -ge "$max_per_cycle" ]] && continue
613
+
403
614
  # Add category to labels if not present
404
615
  if [[ "$labels" != *"$category"* ]]; then
405
616
  labels="${category}${labels:+,$labels}"
@@ -420,7 +631,6 @@ process_findings() {
420
631
  fi
421
632
  fi
422
633
 
423
- local issue_num
424
634
  issue_num=$(create_issue_from_finding "$title" "$description" "$priority" "$effort" "$labels")
425
635
  if [[ $? -eq 0 && -n "$issue_num" ]]; then
426
636
  created=$((created + 1))
@@ -664,6 +874,12 @@ set_cycle_config() {
664
874
  set_config "rollback_on_failures" "$bool_val"
665
875
  success "Rollback on failures set to ${CYAN}${bool_val}${RESET}"
666
876
  ;;
877
+ daemon-aware|daemon_aware)
878
+ local bool_val="true"
879
+ [[ "$value" == "false" || "$value" == "0" || "$value" == "no" ]] && bool_val="false"
880
+ set_config "daemon_aware" "$bool_val"
881
+ success "Daemon-aware (delegate to daemon when running) set to ${CYAN}${bool_val}${RESET}"
882
+ ;;
667
883
  *)
668
884
  error "Unknown config key: $key"
669
885
  return 1
@@ -697,6 +913,7 @@ OPTIONS (for config)
697
913
  set max-pipelines <num> Set max concurrent pipelines (default 2)
698
914
  set approval <bool> Enable human approval mode (default false)
699
915
  set rollback <bool> Rollback on failures (default true)
916
+ set daemon-aware <bool> Delegate to daemon when running (default true)
700
917
 
701
918
  EXAMPLES
702
919
  sw autonomous start # Start the loop
@@ -7,7 +7,7 @@
7
7
  set -euo pipefail
8
8
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
9
9
 
10
- VERSION="2.3.0"
10
+ VERSION="2.4.0"
11
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
12
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
13
13
 
@@ -8,7 +8,7 @@
8
8
  set -euo pipefail
9
9
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
10
10
 
11
- VERSION="2.3.0"
11
+ VERSION="2.4.0"
12
12
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
13
 
14
14
  # ─── Cross-platform compatibility ──────────────────────────────────────────
package/scripts/sw-ci.sh CHANGED
@@ -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="2.3.0"
9
+ VERSION="2.4.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -5,7 +5,7 @@
5
5
  # ║ Default: dry-run (shows what would be cleaned). ║
6
6
  # ║ Use --force to actually kill sessions and remove files. ║
7
7
  # ╚═══════════════════════════════════════════════════════════════════════════╝
8
- VERSION="2.3.0"
8
+ VERSION="2.4.0"
9
9
  set -euo pipefail
10
10
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
11
11
 
@@ -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="2.3.0"
9
+ VERSION="2.4.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -8,7 +8,7 @@
8
8
  set -euo pipefail
9
9
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
10
10
 
11
- VERSION="2.3.0"
11
+ VERSION="2.4.0"
12
12
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
13
 
14
14
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -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="2.3.0"
9
+ VERSION="2.4.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -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="2.3.0"
9
+ VERSION="2.4.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -9,7 +9,7 @@ trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
9
9
  # Allow spawning Claude CLI from within a Claude Code session (daemon, fleet, etc.)
10
10
  unset CLAUDECODE 2>/dev/null || true
11
11
 
12
- VERSION="2.3.0"
12
+ VERSION="2.4.0"
13
13
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
14
14
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
15
15
 
@@ -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="2.3.0"
9
+ VERSION="2.4.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Cross-platform compatibility ──────────────────────────────────────────
package/scripts/sw-db.sh CHANGED
@@ -14,7 +14,7 @@ if [[ -n "${_SW_DB_LOADED:-}" ]] && [[ "${BASH_SOURCE[0]}" != "$0" ]]; then
14
14
  fi
15
15
  _SW_DB_LOADED=1
16
16
 
17
- VERSION="2.3.0"
17
+ VERSION="2.4.0"
18
18
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
19
19
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
20
20
 
@@ -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="2.3.0"
9
+ VERSION="2.4.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -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="2.3.0"
9
+ VERSION="2.4.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -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="2.3.0"
9
+ VERSION="2.4.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -7,7 +7,7 @@
7
7
  set -euo pipefail
8
8
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
9
9
 
10
- VERSION="2.3.0"
10
+ VERSION="2.4.0"
11
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
12
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
13
13
 
@@ -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="2.3.0"
9
+ VERSION="2.4.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -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="2.3.0"
9
+ VERSION="2.4.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -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="2.3.0"
9
+ VERSION="2.4.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -4,7 +4,7 @@
4
4
  # ║ ║
5
5
  # ║ Checks prerequisites, installed files, PATH, and common issues. ║
6
6
  # ╚═══════════════════════════════════════════════════════════════════════════╝
7
- VERSION="2.3.0"
7
+ VERSION="2.4.0"
8
8
  set -euo pipefail
9
9
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
10
10
 
@@ -8,7 +8,7 @@
8
8
  set -euo pipefail
9
9
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
10
10
 
11
- VERSION="2.3.0"
11
+ VERSION="2.4.0"
12
12
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
13
 
14
14
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -7,7 +7,7 @@
7
7
  set -euo pipefail
8
8
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
9
9
 
10
- VERSION="2.3.0"
10
+ VERSION="2.4.0"
11
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
12
 
13
13
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -5,7 +5,7 @@
5
5
  # ╚═══════════════════════════════════════════════════════════════════════════╝
6
6
  set -euo pipefail
7
7
 
8
- VERSION="2.3.0"
8
+ VERSION="2.4.0"
9
9
 
10
10
  # ─── Script directory resolution ────────────────────────────────────────────
11
11
  SOURCE="${BASH_SOURCE[0]}"
@@ -7,7 +7,7 @@
7
7
  set -euo pipefail
8
8
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
9
9
 
10
- VERSION="2.3.0"
10
+ VERSION="2.4.0"
11
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
12
 
13
13
  # ─── Cross-platform compatibility ──────────────────────────────────────────