shipwright-cli 3.2.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 (279) 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 +4 -4
  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/dashboard/middleware/auth.ts +134 -0
  17. package/dashboard/middleware/constants.ts +21 -0
  18. package/dashboard/public/index.html +2 -6
  19. package/dashboard/public/styles.css +100 -97
  20. package/dashboard/routes/auth.ts +38 -0
  21. package/dashboard/server.ts +66 -25
  22. package/dashboard/services/config.ts +26 -0
  23. package/dashboard/services/db.ts +118 -0
  24. package/dashboard/src/canvas/pixel-agent.ts +298 -0
  25. package/dashboard/src/canvas/pixel-sprites.ts +440 -0
  26. package/dashboard/src/canvas/shipyard-effects.ts +367 -0
  27. package/dashboard/src/canvas/shipyard-scene.ts +616 -0
  28. package/dashboard/src/canvas/submarine-layout.ts +267 -0
  29. package/dashboard/src/components/header.ts +8 -7
  30. package/dashboard/src/core/router.ts +1 -0
  31. package/dashboard/src/design/submarine-theme.ts +253 -0
  32. package/dashboard/src/main.ts +2 -0
  33. package/dashboard/src/types/api.ts +2 -1
  34. package/dashboard/src/views/activity.ts +2 -1
  35. package/dashboard/src/views/shipyard.ts +39 -0
  36. package/dashboard/types/index.ts +166 -0
  37. package/docs/plans/2026-02-28-compound-audit-and-shipyard-design.md +186 -0
  38. package/docs/plans/2026-02-28-skipper-shipwright-implementation-plan.md +1182 -0
  39. package/docs/plans/2026-02-28-skipper-shipwright-integration-design.md +531 -0
  40. package/docs/plans/2026-03-01-ai-powered-skill-injection-design.md +298 -0
  41. package/docs/plans/2026-03-01-ai-powered-skill-injection-plan.md +1109 -0
  42. package/docs/plans/2026-03-01-capabilities-cleanup-plan.md +658 -0
  43. package/docs/plans/2026-03-01-clean-architecture-plan.md +924 -0
  44. package/docs/plans/2026-03-01-compound-audit-cascade-design.md +191 -0
  45. package/docs/plans/2026-03-01-compound-audit-cascade-plan.md +921 -0
  46. package/docs/plans/2026-03-01-deep-integration-plan.md +851 -0
  47. package/docs/plans/2026-03-01-pipeline-audit-trail-design.md +145 -0
  48. package/docs/plans/2026-03-01-pipeline-audit-trail-plan.md +770 -0
  49. package/docs/plans/2026-03-01-refined-depths-brand-design.md +382 -0
  50. package/docs/plans/2026-03-01-refined-depths-implementation.md +599 -0
  51. package/docs/plans/2026-03-01-skipper-kernel-integration-design.md +203 -0
  52. package/docs/plans/2026-03-01-unified-platform-design.md +272 -0
  53. package/docs/plans/2026-03-07-claude-code-feature-integration-design.md +189 -0
  54. package/docs/plans/2026-03-07-claude-code-feature-integration-plan.md +1165 -0
  55. package/docs/research/BACKLOG_QUICK_REFERENCE.md +352 -0
  56. package/docs/research/CUTTING_EDGE_RESEARCH_2026.md +546 -0
  57. package/docs/research/RESEARCH_INDEX.md +439 -0
  58. package/docs/research/RESEARCH_SOURCES.md +440 -0
  59. package/docs/research/RESEARCH_SUMMARY.txt +275 -0
  60. package/docs/superpowers/specs/2026-03-10-pipeline-quality-revolution-design.md +341 -0
  61. package/package.json +2 -2
  62. package/scripts/lib/adaptive-model.sh +427 -0
  63. package/scripts/lib/adaptive-timeout.sh +316 -0
  64. package/scripts/lib/audit-trail.sh +309 -0
  65. package/scripts/lib/auto-recovery.sh +471 -0
  66. package/scripts/lib/bandit-selector.sh +431 -0
  67. package/scripts/lib/bootstrap.sh +104 -2
  68. package/scripts/lib/causal-graph.sh +455 -0
  69. package/scripts/lib/compat.sh +126 -0
  70. package/scripts/lib/compound-audit.sh +337 -0
  71. package/scripts/lib/constitutional.sh +454 -0
  72. package/scripts/lib/context-budget.sh +359 -0
  73. package/scripts/lib/convergence.sh +594 -0
  74. package/scripts/lib/cost-optimizer.sh +634 -0
  75. package/scripts/lib/daemon-adaptive.sh +10 -0
  76. package/scripts/lib/daemon-dispatch.sh +106 -17
  77. package/scripts/lib/daemon-failure.sh +34 -4
  78. package/scripts/lib/daemon-patrol.sh +23 -2
  79. package/scripts/lib/daemon-poll-github.sh +361 -0
  80. package/scripts/lib/daemon-poll-health.sh +299 -0
  81. package/scripts/lib/daemon-poll.sh +27 -611
  82. package/scripts/lib/daemon-state.sh +112 -66
  83. package/scripts/lib/daemon-triage.sh +10 -0
  84. package/scripts/lib/dod-scorecard.sh +442 -0
  85. package/scripts/lib/error-actionability.sh +300 -0
  86. package/scripts/lib/formal-spec.sh +461 -0
  87. package/scripts/lib/helpers.sh +177 -4
  88. package/scripts/lib/intent-analysis.sh +409 -0
  89. package/scripts/lib/loop-convergence.sh +350 -0
  90. package/scripts/lib/loop-iteration.sh +682 -0
  91. package/scripts/lib/loop-progress.sh +48 -0
  92. package/scripts/lib/loop-restart.sh +185 -0
  93. package/scripts/lib/memory-effectiveness.sh +506 -0
  94. package/scripts/lib/mutation-executor.sh +352 -0
  95. package/scripts/lib/outcome-feedback.sh +521 -0
  96. package/scripts/lib/pipeline-cli.sh +336 -0
  97. package/scripts/lib/pipeline-commands.sh +1216 -0
  98. package/scripts/lib/pipeline-detection.sh +100 -2
  99. package/scripts/lib/pipeline-execution.sh +897 -0
  100. package/scripts/lib/pipeline-github.sh +28 -3
  101. package/scripts/lib/pipeline-intelligence-compound.sh +431 -0
  102. package/scripts/lib/pipeline-intelligence-scoring.sh +407 -0
  103. package/scripts/lib/pipeline-intelligence-skip.sh +181 -0
  104. package/scripts/lib/pipeline-intelligence.sh +100 -1136
  105. package/scripts/lib/pipeline-quality-bash-compat.sh +182 -0
  106. package/scripts/lib/pipeline-quality-checks.sh +17 -715
  107. package/scripts/lib/pipeline-quality-gates.sh +563 -0
  108. package/scripts/lib/pipeline-stages-build.sh +730 -0
  109. package/scripts/lib/pipeline-stages-delivery.sh +965 -0
  110. package/scripts/lib/pipeline-stages-intake.sh +1133 -0
  111. package/scripts/lib/pipeline-stages-monitor.sh +407 -0
  112. package/scripts/lib/pipeline-stages-review.sh +1022 -0
  113. package/scripts/lib/pipeline-stages.sh +59 -2929
  114. package/scripts/lib/pipeline-state.sh +36 -5
  115. package/scripts/lib/pipeline-util.sh +487 -0
  116. package/scripts/lib/policy-learner.sh +438 -0
  117. package/scripts/lib/process-reward.sh +493 -0
  118. package/scripts/lib/project-detect.sh +649 -0
  119. package/scripts/lib/quality-profile.sh +334 -0
  120. package/scripts/lib/recruit-commands.sh +885 -0
  121. package/scripts/lib/recruit-learning.sh +739 -0
  122. package/scripts/lib/recruit-roles.sh +648 -0
  123. package/scripts/lib/reward-aggregator.sh +458 -0
  124. package/scripts/lib/rl-optimizer.sh +362 -0
  125. package/scripts/lib/root-cause.sh +427 -0
  126. package/scripts/lib/scope-enforcement.sh +445 -0
  127. package/scripts/lib/session-restart.sh +493 -0
  128. package/scripts/lib/skill-memory.sh +300 -0
  129. package/scripts/lib/skill-registry.sh +775 -0
  130. package/scripts/lib/spec-driven.sh +476 -0
  131. package/scripts/lib/test-helpers.sh +18 -7
  132. package/scripts/lib/test-holdout.sh +429 -0
  133. package/scripts/lib/test-optimizer.sh +511 -0
  134. package/scripts/shipwright-file-suggest.sh +45 -0
  135. package/scripts/skills/adversarial-quality.md +61 -0
  136. package/scripts/skills/api-design.md +44 -0
  137. package/scripts/skills/architecture-design.md +50 -0
  138. package/scripts/skills/brainstorming.md +43 -0
  139. package/scripts/skills/data-pipeline.md +44 -0
  140. package/scripts/skills/deploy-safety.md +64 -0
  141. package/scripts/skills/documentation.md +38 -0
  142. package/scripts/skills/frontend-design.md +45 -0
  143. package/scripts/skills/generated/.gitkeep +0 -0
  144. package/scripts/skills/generated/_refinements/.gitkeep +0 -0
  145. package/scripts/skills/generated/_refinements/adversarial-quality.patch.md +3 -0
  146. package/scripts/skills/generated/_refinements/architecture-design.patch.md +3 -0
  147. package/scripts/skills/generated/_refinements/brainstorming.patch.md +3 -0
  148. package/scripts/skills/generated/cli-version-management.md +29 -0
  149. package/scripts/skills/generated/collection-system-validation.md +99 -0
  150. package/scripts/skills/generated/large-scale-c-refactoring-coordination.md +97 -0
  151. package/scripts/skills/generated/pattern-matching-similarity-scoring.md +195 -0
  152. package/scripts/skills/generated/test-parallelization-detection.md +65 -0
  153. package/scripts/skills/observability.md +79 -0
  154. package/scripts/skills/performance.md +48 -0
  155. package/scripts/skills/pr-quality.md +49 -0
  156. package/scripts/skills/product-thinking.md +43 -0
  157. package/scripts/skills/security-audit.md +49 -0
  158. package/scripts/skills/systematic-debugging.md +40 -0
  159. package/scripts/skills/testing-strategy.md +47 -0
  160. package/scripts/skills/two-stage-review.md +52 -0
  161. package/scripts/skills/validation-thoroughness.md +55 -0
  162. package/scripts/sw +9 -3
  163. package/scripts/sw-activity.sh +9 -2
  164. package/scripts/sw-adaptive.sh +2 -1
  165. package/scripts/sw-adversarial.sh +2 -1
  166. package/scripts/sw-architecture-enforcer.sh +3 -1
  167. package/scripts/sw-auth.sh +12 -2
  168. package/scripts/sw-autonomous.sh +5 -1
  169. package/scripts/sw-changelog.sh +4 -1
  170. package/scripts/sw-checkpoint.sh +2 -1
  171. package/scripts/sw-ci.sh +5 -1
  172. package/scripts/sw-cleanup.sh +4 -26
  173. package/scripts/sw-code-review.sh +10 -4
  174. package/scripts/sw-connect.sh +2 -1
  175. package/scripts/sw-context.sh +2 -1
  176. package/scripts/sw-cost.sh +48 -3
  177. package/scripts/sw-daemon.sh +66 -9
  178. package/scripts/sw-dashboard.sh +3 -1
  179. package/scripts/sw-db.sh +59 -16
  180. package/scripts/sw-decide.sh +8 -2
  181. package/scripts/sw-decompose.sh +360 -17
  182. package/scripts/sw-deps.sh +4 -1
  183. package/scripts/sw-developer-simulation.sh +4 -1
  184. package/scripts/sw-discovery.sh +325 -2
  185. package/scripts/sw-doc-fleet.sh +4 -1
  186. package/scripts/sw-docs-agent.sh +3 -1
  187. package/scripts/sw-docs.sh +2 -1
  188. package/scripts/sw-doctor.sh +453 -2
  189. package/scripts/sw-dora.sh +4 -1
  190. package/scripts/sw-durable.sh +4 -3
  191. package/scripts/sw-e2e-orchestrator.sh +17 -16
  192. package/scripts/sw-eventbus.sh +7 -1
  193. package/scripts/sw-evidence.sh +364 -12
  194. package/scripts/sw-feedback.sh +550 -9
  195. package/scripts/sw-fix.sh +20 -1
  196. package/scripts/sw-fleet-discover.sh +6 -2
  197. package/scripts/sw-fleet-viz.sh +4 -1
  198. package/scripts/sw-fleet.sh +5 -1
  199. package/scripts/sw-github-app.sh +16 -3
  200. package/scripts/sw-github-checks.sh +3 -2
  201. package/scripts/sw-github-deploy.sh +3 -2
  202. package/scripts/sw-github-graphql.sh +18 -7
  203. package/scripts/sw-guild.sh +5 -1
  204. package/scripts/sw-heartbeat.sh +5 -30
  205. package/scripts/sw-hello.sh +67 -0
  206. package/scripts/sw-hygiene.sh +6 -1
  207. package/scripts/sw-incident.sh +265 -1
  208. package/scripts/sw-init.sh +18 -2
  209. package/scripts/sw-instrument.sh +10 -2
  210. package/scripts/sw-intelligence.sh +42 -6
  211. package/scripts/sw-jira.sh +5 -1
  212. package/scripts/sw-launchd.sh +2 -1
  213. package/scripts/sw-linear.sh +4 -1
  214. package/scripts/sw-logs.sh +4 -1
  215. package/scripts/sw-loop.sh +432 -1128
  216. package/scripts/sw-memory.sh +356 -2
  217. package/scripts/sw-mission-control.sh +6 -1
  218. package/scripts/sw-model-router.sh +481 -26
  219. package/scripts/sw-otel.sh +13 -4
  220. package/scripts/sw-oversight.sh +14 -5
  221. package/scripts/sw-patrol-meta.sh +334 -0
  222. package/scripts/sw-pipeline-composer.sh +5 -1
  223. package/scripts/sw-pipeline-vitals.sh +2 -1
  224. package/scripts/sw-pipeline.sh +53 -2664
  225. package/scripts/sw-pm.sh +12 -5
  226. package/scripts/sw-pr-lifecycle.sh +2 -1
  227. package/scripts/sw-predictive.sh +7 -1
  228. package/scripts/sw-prep.sh +185 -2
  229. package/scripts/sw-ps.sh +5 -25
  230. package/scripts/sw-public-dashboard.sh +15 -3
  231. package/scripts/sw-quality.sh +2 -1
  232. package/scripts/sw-reaper.sh +8 -25
  233. package/scripts/sw-recruit.sh +156 -2303
  234. package/scripts/sw-regression.sh +19 -12
  235. package/scripts/sw-release-manager.sh +3 -1
  236. package/scripts/sw-release.sh +4 -1
  237. package/scripts/sw-remote.sh +3 -1
  238. package/scripts/sw-replay.sh +7 -1
  239. package/scripts/sw-retro.sh +158 -1
  240. package/scripts/sw-review-rerun.sh +3 -1
  241. package/scripts/sw-scale.sh +10 -3
  242. package/scripts/sw-security-audit.sh +6 -1
  243. package/scripts/sw-self-optimize.sh +6 -3
  244. package/scripts/sw-session.sh +9 -3
  245. package/scripts/sw-setup.sh +3 -1
  246. package/scripts/sw-stall-detector.sh +406 -0
  247. package/scripts/sw-standup.sh +15 -7
  248. package/scripts/sw-status.sh +3 -1
  249. package/scripts/sw-strategic.sh +4 -1
  250. package/scripts/sw-stream.sh +7 -1
  251. package/scripts/sw-swarm.sh +18 -6
  252. package/scripts/sw-team-stages.sh +13 -6
  253. package/scripts/sw-templates.sh +5 -29
  254. package/scripts/sw-testgen.sh +7 -1
  255. package/scripts/sw-tmux-pipeline.sh +4 -1
  256. package/scripts/sw-tmux-role-color.sh +2 -0
  257. package/scripts/sw-tmux-status.sh +1 -1
  258. package/scripts/sw-tmux.sh +3 -1
  259. package/scripts/sw-trace.sh +3 -1
  260. package/scripts/sw-tracker-github.sh +3 -0
  261. package/scripts/sw-tracker-jira.sh +3 -0
  262. package/scripts/sw-tracker-linear.sh +3 -0
  263. package/scripts/sw-tracker.sh +3 -1
  264. package/scripts/sw-triage.sh +2 -1
  265. package/scripts/sw-upgrade.sh +3 -1
  266. package/scripts/sw-ux.sh +5 -2
  267. package/scripts/sw-webhook.sh +3 -1
  268. package/scripts/sw-widgets.sh +3 -1
  269. package/scripts/sw-worktree.sh +15 -3
  270. package/scripts/test-skill-injection.sh +1233 -0
  271. package/templates/pipelines/autonomous.json +27 -3
  272. package/templates/pipelines/cost-aware.json +34 -8
  273. package/templates/pipelines/deployed.json +12 -0
  274. package/templates/pipelines/enterprise.json +12 -0
  275. package/templates/pipelines/fast.json +6 -0
  276. package/templates/pipelines/full.json +27 -3
  277. package/templates/pipelines/hotfix.json +6 -0
  278. package/templates/pipelines/standard.json +12 -0
  279. package/templates/pipelines/tdd.json +12 -0
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env bash
2
+ # shellcheck disable=SC2034 # config vars used by sourced scripts and subshells
2
3
  # ╔═══════════════════════════════════════════════════════════════════════════╗
3
4
  # ║ shipwright daemon — Autonomous GitHub Issue Watcher ║
4
5
  # ║ Polls for labeled issues · Spawns pipelines · Manages worktrees ║
@@ -12,7 +13,7 @@ unset CLAUDECODE 2>/dev/null || true
12
13
  trap '' HUP
13
14
  trap '' SIGPIPE
14
15
 
15
- VERSION="3.2.0"
16
+ VERSION="3.3.0"
16
17
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
17
18
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
18
19
 
@@ -202,6 +203,8 @@ ISSUE_LIMIT=$(_config_get_int "daemon.issue_limit" 100 2>/dev/null || echo 100)
202
203
  PIPELINE_TEMPLATE="autonomous"
203
204
  SKIP_GATES=true
204
205
  MODEL="opus"
206
+ EFFORT_LEVEL=""
207
+ FALLBACK_MODEL="sonnet"
205
208
  BASE_BRANCH="main"
206
209
  ON_SUCCESS_REMOVE_LABEL="shipwright"
207
210
  ON_SUCCESS_ADD_LABEL="pipeline/complete"
@@ -384,6 +387,8 @@ load_config() {
384
387
  PIPELINE_TEMPLATE=$(jq -r '.pipeline_template // "autonomous"' "$config_file")
385
388
  SKIP_GATES=$(jq -r '.skip_gates // true' "$config_file")
386
389
  MODEL=$(jq -r '.model // "opus"' "$config_file")
390
+ EFFORT_LEVEL=$(jq -r '.effort_level // ""' "$config_file")
391
+ FALLBACK_MODEL=$(jq -r '.fallback_model // "sonnet"' "$config_file")
387
392
  BASE_BRANCH=$(jq -r '.base_branch // "main"' "$config_file")
388
393
 
389
394
  # on_success settings
@@ -444,6 +449,7 @@ load_config() {
444
449
  # intelligence engine settings (default "auto" = enable when Claude CLI available)
445
450
  INTELLIGENCE_ENABLED=$(jq -r '.intelligence.enabled // "auto"' "$config_file")
446
451
  INTELLIGENCE_CACHE_TTL=$(jq -r '.intelligence.cache_ttl_seconds // 3600' "$config_file")
452
+ INTELLIGENCE_MODEL=$(jq -r '.intelligence.model // "haiku"' "$config_file")
447
453
  COMPOSER_ENABLED=$(jq -r '.intelligence.composer_enabled // "auto"' "$config_file")
448
454
 
449
455
  # Auto-enable intelligence when Claude is available (unless explicitly disabled)
@@ -474,14 +480,27 @@ load_config() {
474
480
  COMPOSER_ENABLED=false
475
481
  fi
476
482
  fi
477
- OPTIMIZATION_ENABLED=$(jq -r '.intelligence.optimization_enabled // false' "$config_file")
478
- PREDICTION_ENABLED=$(jq -r '.intelligence.prediction_enabled // false' "$config_file")
483
+ OPTIMIZATION_ENABLED=$(jq -r '.intelligence.optimization_enabled // "auto"' "$config_file")
484
+ PREDICTION_ENABLED=$(jq -r '.intelligence.prediction_enabled // "auto"' "$config_file")
479
485
  ANOMALY_THRESHOLD=$(jq -r '.intelligence.anomaly_threshold // 3.0' "$config_file")
480
486
 
481
487
  # adaptive thresholds (intelligence-driven operational tuning)
482
- ADAPTIVE_THRESHOLDS_ENABLED=$(jq -r '.intelligence.adaptive_enabled // false' "$config_file")
488
+ ADAPTIVE_THRESHOLDS_ENABLED=$(jq -r '.intelligence.adaptive_enabled // "auto"' "$config_file")
483
489
  PRIORITY_STRATEGY=$(jq -r '.intelligence.priority_strategy // "quick-wins-first"' "$config_file")
484
490
 
491
+ # Auto-resolve "auto" for prediction, optimization, adaptive (same pattern as intelligence/composer)
492
+ local _flag_val=""
493
+ for _flag_var in OPTIMIZATION_ENABLED PREDICTION_ENABLED ADAPTIVE_THRESHOLDS_ENABLED; do
494
+ eval "_flag_val=\"\${${_flag_var}}\""
495
+ if [[ "$_flag_val" == "auto" ]]; then
496
+ if command -v claude &>/dev/null; then
497
+ eval "${_flag_var}=true"
498
+ else
499
+ eval "${_flag_var}=false"
500
+ fi
501
+ fi
502
+ done
503
+
485
504
  # gh_retry: enable retry wrapper on critical GitHub API calls
486
505
  GH_RETRY_ENABLED=$(jq -r '.gh_retry // true' "$config_file")
487
506
 
@@ -547,11 +566,24 @@ setup_dirs() {
547
566
  STATE_FILE="$DAEMON_DIR/daemon-state.json"
548
567
  LOG_FILE="$DAEMON_DIR/daemon.log"
549
568
  LOG_DIR="$DAEMON_DIR/logs"
550
- WORKTREE_DIR=".worktrees"
569
+
570
+ # ── Worktree Directory (must be absolute for security) ──
571
+ # Always use repo-level .claude/worktrees (self-healing: create .claude if missing)
572
+ local _abs_cwd
573
+ _abs_cwd="$(cd "$(pwd)" && pwd)"
574
+ WORKTREE_DIR="${_abs_cwd}/.claude/worktrees"
575
+
551
576
  PAUSE_FLAG="${HOME}/.shipwright/daemon-pause.flag"
552
577
 
553
578
  mkdir -p "$LOG_DIR"
554
579
  mkdir -p "$HOME/.shipwright/progress"
580
+ mkdir -p "$WORKTREE_DIR"
581
+
582
+ # Ensure worktrees are gitignored
583
+ local _gitignore="${_abs_cwd}/.gitignore"
584
+ if [[ -f "$_gitignore" ]] && ! grep -q '\.claude/worktrees' "$_gitignore" 2>/dev/null; then
585
+ echo ".claude/worktrees/" >> "$_gitignore"
586
+ fi
555
587
  }
556
588
 
557
589
  # ─── Adaptive Threshold Helpers ──────────────────────────────────────────────
@@ -559,7 +591,20 @@ setup_dirs() {
559
591
  # from historical data instead of using fixed defaults.
560
592
  # Every function falls back to the config default when no data exists.
561
593
 
562
- ADAPTIVE_THRESHOLDS_ENABLED="${ADAPTIVE_THRESHOLDS_ENABLED:-false}"
594
+ # Auto-resolve intelligence defaults when no config file is loaded.
595
+ # All three default to "auto" → true when Claude CLI is available.
596
+ _auto_resolve_intelligence() {
597
+ local val="${1:-auto}"
598
+ if [[ "$val" == "auto" ]]; then
599
+ command -v claude &>/dev/null && echo "true" || echo "false"
600
+ else
601
+ echo "$val"
602
+ fi
603
+ }
604
+ INTELLIGENCE_MODEL="${INTELLIGENCE_MODEL:-haiku}"
605
+ OPTIMIZATION_ENABLED=$(_auto_resolve_intelligence "${OPTIMIZATION_ENABLED:-auto}")
606
+ PREDICTION_ENABLED=$(_auto_resolve_intelligence "${PREDICTION_ENABLED:-auto}")
607
+ ADAPTIVE_THRESHOLDS_ENABLED=$(_auto_resolve_intelligence "${ADAPTIVE_THRESHOLDS_ENABLED:-auto}")
563
608
  PRIORITY_STRATEGY="${PRIORITY_STRATEGY:-quick-wins-first}"
564
609
  EMPTY_QUEUE_CYCLES=0
565
610
 
@@ -672,7 +717,8 @@ daemon_start() {
672
717
  fi
673
718
 
674
719
  # Export current PATH so detached session finds claude, gh, etc.
675
- local tmux_cmd="export PATH='${PATH}'; ${cmd_args[*]}"
720
+ # Unset CLAUDECODE so daemon works when started from inside Claude Code
721
+ local tmux_cmd="unset CLAUDECODE 2>/dev/null; export PATH='${PATH}'; ${cmd_args[*]}"
676
722
  tmux new-session -d -s "sw-daemon" "$tmux_cmd" 2>/dev/null || {
677
723
  # Session may already exist — try killing and recreating
678
724
  tmux kill-session -t "sw-daemon" 2>/dev/null || true
@@ -937,6 +983,11 @@ daemon_init() {
937
983
 
938
984
  mkdir -p "$config_dir"
939
985
 
986
+ # Set restrictive umask for sensitive files (owner-only: 600)
987
+ local _old_umask
988
+ _old_umask=$(umask)
989
+ umask 0077
990
+
940
991
  cat > "$config_file" << 'CONFIGEOF'
941
992
  {
942
993
  "watch_label": "shipwright",
@@ -1002,10 +1053,12 @@ daemon_init() {
1002
1053
  "estimated_cost_per_job_usd": 5.0,
1003
1054
  "intelligence": {
1004
1055
  "enabled": "auto",
1056
+ "model": "haiku",
1005
1057
  "cache_ttl_seconds": 3600,
1006
1058
  "composer_enabled": "auto",
1007
- "optimization_enabled": true,
1008
- "prediction_enabled": true,
1059
+ "optimization_enabled": "auto",
1060
+ "prediction_enabled": "auto",
1061
+ "adaptive_enabled": "auto",
1009
1062
  "adversarial_enabled": false,
1010
1063
  "simulation_enabled": false,
1011
1064
  "architecture_enabled": false,
@@ -1015,6 +1068,10 @@ daemon_init() {
1015
1068
  }
1016
1069
  CONFIGEOF
1017
1070
 
1071
+ # Restore umask and ensure file has restricted permissions
1072
+ umask "$_old_umask"
1073
+ chmod 600 "$config_file"
1074
+
1018
1075
  success "Generated config: ${config_file}"
1019
1076
  echo ""
1020
1077
  echo -e "${DIM}Edit this file to customize the daemon behavior, then run:${RESET}"
@@ -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.2.0"
9
+ VERSION="3.3.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -29,6 +29,7 @@ fi
29
29
  if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
30
30
  emit_event() {
31
31
  local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
32
+ # shellcheck disable=SC2155
32
33
  local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
33
34
  while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
34
35
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
@@ -41,6 +42,7 @@ TEAMS_DIR="${HOME}/.shipwright"
41
42
  PID_FILE="${TEAMS_DIR}/dashboard.pid"
42
43
  LOG_DIR="${TEAMS_DIR}/logs"
43
44
  LOG_FILE="${LOG_DIR}/dashboard.log"
45
+ # shellcheck disable=SC2034
44
46
  EVENTS_FILE="${TEAMS_DIR}/events.jsonl"
45
47
  DEFAULT_PORT=$(_config_get_int "dashboard.port" 8767 2>/dev/null || echo 8767)
46
48
 
package/scripts/sw-db.sh CHANGED
@@ -14,9 +14,11 @@ if [[ -n "${_SW_DB_LOADED:-}" ]] && [[ "${BASH_SOURCE[0]}" != "$0" ]]; then
14
14
  fi
15
15
  _SW_DB_LOADED=1
16
16
 
17
- VERSION="3.2.0"
17
+ # shellcheck disable=SC2034
18
+ VERSION="3.3.0"
18
19
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
19
- REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
20
+ # shellcheck disable=SC2034
21
+ REPO_DIR="${REPO_DIR:-$(cd "$SCRIPT_DIR/.." && pwd)}"
20
22
 
21
23
  # ─── Cross-platform compatibility ──────────────────────────────────────────
22
24
  # shellcheck source=lib/compat.sh
@@ -37,7 +39,8 @@ fi
37
39
  if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
38
40
  emit_event() {
39
41
  local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
40
- local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
42
+ local payload
43
+ payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
41
44
  while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
42
45
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
43
46
  }
@@ -50,6 +53,7 @@ SCHEMA_VERSION=6
50
53
  # JSON fallback paths
51
54
  EVENTS_FILE="${DB_DIR}/events.jsonl"
52
55
  DAEMON_STATE_FILE="${DB_DIR}/daemon-state.json"
56
+ # shellcheck disable=SC2034
53
57
  DEVELOPER_REGISTRY_FILE="${DB_DIR}/developer-registry.json"
54
58
  COST_FILE_JSON="${DB_DIR}/costs.json"
55
59
  BUDGET_FILE_JSON="${DB_DIR}/budget.json"
@@ -107,12 +111,12 @@ ensure_db_dir() {
107
111
  # ─── SQL Execution Helper ──────────────────────────────────────────────────
108
112
  # Runs SQL with proper error handling. Silent on success.
109
113
  _db_exec() {
110
- sqlite3 "$DB_FILE" "$@" 2>/dev/null
114
+ sqlite3 -cmd ".timeout 5000" "$DB_FILE" "$@" 2>/dev/null
111
115
  }
112
116
 
113
117
  # Runs SQL and returns output. Returns 1 on failure.
114
118
  _db_query() {
115
- sqlite3 "$DB_FILE" "$@" 2>/dev/null || return 1
119
+ sqlite3 -cmd ".timeout 5000" "$DB_FILE" "$@" 2>/dev/null || return 1
116
120
  }
117
121
 
118
122
  # ─── Initialize Database Schema ──────────────────────────────────────────────
@@ -700,7 +704,10 @@ db_query_events() {
700
704
 
701
705
  if [[ -f "$db_file" ]] && command -v sqlite3 &>/dev/null; then
702
706
  local where_clause=""
703
- [[ -n "$filter" ]] && where_clause="WHERE type = '$filter'"
707
+ if [[ -n "$filter" ]]; then
708
+ filter="${filter//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
709
+ where_clause="WHERE type = '$filter'"
710
+ fi
704
711
  local result
705
712
  result=$(sqlite3 -json "$db_file" "SELECT ts, ts_epoch, type, job_id, stage, status, duration_secs, metadata FROM events $where_clause ORDER BY ts_epoch DESC LIMIT $limit" 2>/dev/null) || true
706
713
  if [[ -n "$result" ]]; then
@@ -728,13 +735,19 @@ db_query_events_since() {
728
735
  local since_epoch="$1"
729
736
  local event_type="${2:-}"
730
737
  local to_epoch="${3:-}"
738
+ # Validate numeric epoch values
739
+ [[ ! "$since_epoch" =~ ^[0-9]+$ ]] && { echo "[]"; return 0; }
731
740
  local db_file="${DB_FILE:-$HOME/.shipwright/shipwright.db}"
732
741
 
733
742
  if [[ -f "$db_file" ]] && command -v sqlite3 &>/dev/null; then
734
743
  local type_filter=""
735
- [[ -n "$event_type" ]] && type_filter="AND type = '$event_type'"
744
+ if [[ -n "$event_type" ]]; then
745
+ event_type="${event_type//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
746
+ type_filter="AND type = '$event_type'"
747
+ fi
736
748
  local to_filter=""
737
- [[ -n "$to_epoch" ]] && to_filter="AND ts_epoch <= $to_epoch"
749
+ # Numeric validation for epoch values
750
+ [[ -n "$to_epoch" && "$to_epoch" =~ ^[0-9]+$ ]] && to_filter="AND ts_epoch <= $to_epoch"
738
751
  local result
739
752
  result=$(sqlite3 -json "$db_file" "SELECT ts, ts_epoch, type, job_id, stage, status, duration_secs, metadata FROM events WHERE ts_epoch >= $since_epoch $type_filter $to_filter ORDER BY ts_epoch DESC" 2>/dev/null) || true
740
753
  if [[ -n "$result" ]]; then
@@ -1182,8 +1195,12 @@ db_save_pattern() {
1182
1195
  db_query_patterns() {
1183
1196
  local repo_hash="$1" pattern_type="${2:-}" limit="${3:-20}"
1184
1197
  if ! db_available; then echo "[]"; return 0; fi
1198
+ repo_hash="${repo_hash//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1185
1199
  local where="WHERE repo_hash = '$repo_hash'"
1186
- [[ -n "$pattern_type" ]] && where="$where AND pattern_type = '$pattern_type'"
1200
+ if [[ -n "$pattern_type" ]]; then
1201
+ pattern_type="${pattern_type//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1202
+ where="$where AND pattern_type = '$pattern_type'"
1203
+ fi
1187
1204
  _db_query -json "SELECT * FROM memory_patterns $where ORDER BY frequency DESC, last_seen_at DESC LIMIT $limit;" || echo "[]"
1188
1205
  }
1189
1206
 
@@ -1191,6 +1208,8 @@ db_query_patterns() {
1191
1208
  db_save_decision() {
1192
1209
  local repo_hash="$1" decision_type="$2" context="$3" decision="$4" metadata="${5:-}"
1193
1210
  if ! db_available; then return 1; fi
1211
+ repo_hash="${repo_hash//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1212
+ decision_type="${decision_type//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1194
1213
  context="${context//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1195
1214
  decision="${decision//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1196
1215
  metadata="${metadata//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
@@ -1201,17 +1220,24 @@ db_save_decision() {
1201
1220
  db_update_decision_outcome() {
1202
1221
  local decision_id="$1" outcome="$2" confidence="${3:-}"
1203
1222
  if ! db_available; then return 1; fi
1223
+ # Validate numeric IDs to prevent injection
1224
+ [[ ! "$decision_id" =~ ^[0-9]+$ ]] && return 1
1204
1225
  outcome="${outcome//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1205
- local set_clause="outcome = '$outcome', updated_at = '$(now_iso)'"
1206
- [[ -n "$confidence" ]] && set_clause="$set_clause, confidence = $confidence"
1226
+ local set_clause
1227
+ set_clause="outcome = '$outcome', updated_at = '$(now_iso)'"
1228
+ [[ -n "$confidence" && "$confidence" =~ ^[0-9.]+$ ]] && set_clause="$set_clause, confidence = $confidence"
1207
1229
  _db_exec "UPDATE memory_decisions SET $set_clause WHERE id = $decision_id;"
1208
1230
  }
1209
1231
 
1210
1232
  db_query_decisions() {
1211
1233
  local repo_hash="$1" decision_type="${2:-}" limit="${3:-20}"
1212
1234
  if ! db_available; then echo "[]"; return 0; fi
1235
+ repo_hash="${repo_hash//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1213
1236
  local where="WHERE repo_hash = '$repo_hash'"
1214
- [[ -n "$decision_type" ]] && where="$where AND decision_type = '$decision_type'"
1237
+ if [[ -n "$decision_type" ]]; then
1238
+ decision_type="${decision_type//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1239
+ where="$where AND decision_type = '$decision_type'"
1240
+ fi
1215
1241
  _db_query -json "SELECT * FROM memory_decisions $where ORDER BY updated_at DESC LIMIT $limit;" || echo "[]"
1216
1242
  }
1217
1243
 
@@ -1219,7 +1245,10 @@ db_query_decisions() {
1219
1245
  db_save_embedding() {
1220
1246
  local content_hash="$1" source_type="$2" content_text="$3" repo_hash="${4:-}"
1221
1247
  if ! db_available; then return 1; fi
1248
+ content_hash="${content_hash//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1249
+ source_type="${source_type//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1222
1250
  content_text="${content_text//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1251
+ repo_hash="${repo_hash//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1223
1252
  _db_exec "INSERT OR IGNORE INTO memory_embeddings (content_hash, source_type, content_text, repo_hash, created_at)
1224
1253
  VALUES ('$content_hash', '$source_type', '$content_text', '$repo_hash', '$(now_iso)');"
1225
1254
  }
@@ -1228,8 +1257,14 @@ db_query_embeddings() {
1228
1257
  local source_type="${1:-}" repo_hash="${2:-}" limit="${3:-50}"
1229
1258
  if ! db_available; then echo "[]"; return 0; fi
1230
1259
  local where="WHERE 1=1"
1231
- [[ -n "$source_type" ]] && where="$where AND source_type = '$source_type'"
1232
- [[ -n "$repo_hash" ]] && where="$where AND repo_hash = '$repo_hash'"
1260
+ if [[ -n "$source_type" ]]; then
1261
+ source_type="${source_type//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1262
+ where="$where AND source_type = '$source_type'"
1263
+ fi
1264
+ if [[ -n "$repo_hash" ]]; then
1265
+ repo_hash="${repo_hash//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1266
+ where="$where AND repo_hash = '$repo_hash'"
1267
+ fi
1233
1268
  _db_query -json "SELECT id, content_hash, source_type, content_text, repo_hash, created_at FROM memory_embeddings $where ORDER BY created_at DESC LIMIT $limit;" || echo "[]"
1234
1269
  }
1235
1270
 
@@ -1368,8 +1403,10 @@ db_sync_push() {
1368
1403
  # Push via HTTP
1369
1404
  local response
1370
1405
  local auth_header=""
1406
+ # shellcheck disable=SC2089
1371
1407
  [[ -n "${SYNC_TOKEN:-}" ]] && auth_header="-H 'Authorization: Bearer ${SYNC_TOKEN}'"
1372
1408
 
1409
+ # shellcheck disable=SC2090
1373
1410
  response=$(curl -s --connect-timeout 10 --max-time 30 -w "%{http_code}" -o /dev/null \
1374
1411
  -X POST "${SYNC_URL}/api/sync/push" \
1375
1412
  -H "Content-Type: application/json" \
@@ -1400,9 +1437,11 @@ db_sync_pull() {
1400
1437
  last_sync=$(_db_query "SELECT value FROM _sync_metadata WHERE key = 'last_pull_epoch';" || echo "0")
1401
1438
 
1402
1439
  local auth_header=""
1440
+ # shellcheck disable=SC2089
1403
1441
  [[ -n "${SYNC_TOKEN:-}" ]] && auth_header="-H 'Authorization: Bearer ${SYNC_TOKEN}'"
1404
1442
 
1405
1443
  local response_body
1444
+ # shellcheck disable=SC2090
1406
1445
  response_body=$(curl -s --connect-timeout 10 --max-time 30 \
1407
1446
  "${SYNC_URL}/api/sync/pull?since=${last_sync}" \
1408
1447
  -H "Accept: application/json" \
@@ -1451,6 +1490,7 @@ migrate_json_data() {
1451
1490
  info "Importing events from ${EVENTS_FILE}..."
1452
1491
  local evt_count=0
1453
1492
  local evt_skipped=0
1493
+ # shellcheck disable=SC2106
1454
1494
  while IFS= read -r line; do
1455
1495
  [[ -z "$line" ]] && continue
1456
1496
  local e_ts e_epoch e_type e_job e_stage e_status
@@ -1484,7 +1524,8 @@ migrate_json_data() {
1484
1524
  j_result=$(echo "$job" | jq -r '.result // ""')
1485
1525
  j_dur=$(echo "$job" | jq -r '.duration // ""')
1486
1526
  j_at=$(echo "$job" | jq -r '.completed_at // ""')
1487
- local j_id="migrated-${j_issue}-$(echo "$j_at" | tr -dc '0-9' | tail -c 10)"
1527
+ local j_id
1528
+ j_id="migrated-${j_issue}-$(echo "$j_at" | tr -dc '0-9' | tail -c 10)"
1488
1529
  _db_exec "INSERT OR IGNORE INTO daemon_state (job_id, issue_number, status, result, duration, completed_at, started_at, updated_at) VALUES ('${j_id}', ${j_issue}, 'completed', '${j_result}', '${j_dur}', '${j_at}', '${j_at}', '$(now_iso)');" 2>/dev/null && job_count=$((job_count + 1))
1489
1530
  done < <(jq -c '.completed[]' "$DAEMON_STATE_FILE" 2>/dev/null)
1490
1531
 
@@ -1583,7 +1624,7 @@ export_db() {
1583
1624
  costs_json=$(_db_query "SELECT json_group_array(json_object('model', model, 'stage', stage, 'cost_usd', cost_usd, 'ts', ts)) FROM (SELECT * FROM cost_entries ORDER BY ts_epoch DESC LIMIT 1000);" || echo "[]")
1584
1625
 
1585
1626
  local tmp_file
1586
- tmp_file=$(mktemp "${output_file}.tmp.XXXXXX")
1627
+ tmp_file=$(mktemp "${output_file}.tmp.XXXXXX") || { error "mktemp failed for db export"; return 1; }
1587
1628
  jq -n \
1588
1629
  --arg exported_at "$(now_iso)" \
1589
1630
  --argjson events "$events_json" \
@@ -1670,7 +1711,9 @@ show_status() {
1670
1711
  # Sync status
1671
1712
  local device_id last_push last_pull
1672
1713
  device_id=$(_db_query "SELECT value FROM _sync_metadata WHERE key = 'device_id';" || echo "not set")
1714
+ # shellcheck disable=SC2034
1673
1715
  last_push=$(_db_query "SELECT value FROM _sync_metadata WHERE key = 'last_push_epoch';" || echo "never")
1716
+ # shellcheck disable=SC2034
1674
1717
  last_pull=$(_db_query "SELECT value FROM _sync_metadata WHERE key = 'last_pull_epoch';" || echo "never")
1675
1718
  local unsynced_events unsynced_costs
1676
1719
  unsynced_events=$(_db_query "SELECT COUNT(*) FROM events WHERE synced = 0;" || echo "0")
@@ -5,7 +5,8 @@
5
5
  # ╚═══════════════════════════════════════════════════════════════════════════╝
6
6
  set -euo pipefail
7
7
 
8
- VERSION="3.2.0"
8
+ # shellcheck disable=SC2034
9
+ VERSION="3.3.0"
9
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
11
 
11
12
  # ─── Dependencies ─────────────────────────────────────────────────────────────
@@ -17,8 +18,10 @@ source "$SCRIPT_DIR/lib/decide-scoring.sh"
17
18
  source "$SCRIPT_DIR/lib/decide-autonomy.sh"
18
19
 
19
20
  # ─── Config ───────────────────────────────────────────────────────────────────
21
+ # shellcheck disable=SC2034
20
22
  DECISION_ENABLED=$(policy_get ".decision.enabled" "false")
21
23
  DEDUP_WINDOW_DAYS=$(policy_get ".decision.dedup_window_days" "7")
24
+ # shellcheck disable=SC2034
22
25
  OUTCOME_LEARNING=$(policy_get ".decision.outcome_learning_enabled" "true")
23
26
  OUTCOME_MIN_SAMPLES=$(policy_get ".decision.outcome_min_samples" "10")
24
27
 
@@ -227,7 +230,9 @@ decide_run() {
227
230
  while [[ $# -gt 0 ]]; do
228
231
  case "$1" in
229
232
  --dry-run) dry_run=true; shift ;;
230
- --once) once=true; shift ;;
233
+ --once)
234
+ # shellcheck disable=SC2034
235
+ once=true; shift ;;
231
236
  *) shift ;;
232
237
  esac
233
238
  done
@@ -432,6 +437,7 @@ decide_log() {
432
437
  local found=false
433
438
  for i in $(seq 0 $((days - 1))); do
434
439
  local date_str
440
+ # shellcheck disable=SC2106
435
441
  date_str=$(date -u -v-${i}d +%Y-%m-%d 2>/dev/null || date -u -d "${i} days ago" +%Y-%m-%d 2>/dev/null || continue)
436
442
  local log_file="${DECISIONS_DIR}/daily-log-${date_str}.jsonl"
437
443
  [[ ! -f "$log_file" ]] && continue