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
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.1.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
@@ -812,7 +825,9 @@ add_event() {
812
825
 
813
826
  # Try SQLite first
814
827
  if db_available; then
815
- _db_exec "INSERT OR IGNORE INTO events (ts, ts_epoch, type, job_id, stage, status, duration_secs, metadata, created_at, synced) VALUES ('${ts}', ${ts_epoch}, '${event_type}', '${job_id}', '${stage}', '${status}', ${duration_secs}, '${metadata}', '${ts}', 0);" || true
828
+ if ! _db_exec "INSERT OR IGNORE INTO events (ts, ts_epoch, type, job_id, stage, status, duration_secs, metadata, created_at, synced) VALUES ('${ts}', ${ts_epoch}, '${event_type}', '${job_id}', '${stage}', '${status}', ${duration_secs}, '${metadata}', '${ts}', 0);" 2>/dev/null; then
829
+ warn "db_add_event: SQLite insert failed for event type=${event_type}" >&2
830
+ fi
816
831
  fi
817
832
 
818
833
  # Always write to JSONL for backward compat (dual-write period)
@@ -934,7 +949,9 @@ db_dequeue_next() {
934
949
  next=$(_db_query "SELECT issue_key FROM daemon_queue ORDER BY added_at ASC LIMIT 1;" || echo "")
935
950
  if [[ -n "$next" ]]; then
936
951
  escaped="${next//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
937
- _db_exec "DELETE FROM daemon_queue WHERE issue_key = '${escaped}';" 2>/dev/null || true
952
+ if ! _db_exec "DELETE FROM daemon_queue WHERE issue_key = '${escaped}';" 2>/dev/null; then
953
+ warn "db_dequeue_next: failed to delete queue entry for ${next}" >&2
954
+ fi
938
955
  echo "$next"
939
956
  fi
940
957
  }
@@ -1178,8 +1195,12 @@ db_save_pattern() {
1178
1195
  db_query_patterns() {
1179
1196
  local repo_hash="$1" pattern_type="${2:-}" limit="${3:-20}"
1180
1197
  if ! db_available; then echo "[]"; return 0; fi
1198
+ repo_hash="${repo_hash//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1181
1199
  local where="WHERE repo_hash = '$repo_hash'"
1182
- [[ -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
1183
1204
  _db_query -json "SELECT * FROM memory_patterns $where ORDER BY frequency DESC, last_seen_at DESC LIMIT $limit;" || echo "[]"
1184
1205
  }
1185
1206
 
@@ -1187,6 +1208,8 @@ db_query_patterns() {
1187
1208
  db_save_decision() {
1188
1209
  local repo_hash="$1" decision_type="$2" context="$3" decision="$4" metadata="${5:-}"
1189
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}"
1190
1213
  context="${context//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1191
1214
  decision="${decision//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1192
1215
  metadata="${metadata//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
@@ -1197,17 +1220,24 @@ db_save_decision() {
1197
1220
  db_update_decision_outcome() {
1198
1221
  local decision_id="$1" outcome="$2" confidence="${3:-}"
1199
1222
  if ! db_available; then return 1; fi
1223
+ # Validate numeric IDs to prevent injection
1224
+ [[ ! "$decision_id" =~ ^[0-9]+$ ]] && return 1
1200
1225
  outcome="${outcome//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1201
- local set_clause="outcome = '$outcome', updated_at = '$(now_iso)'"
1202
- [[ -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"
1203
1229
  _db_exec "UPDATE memory_decisions SET $set_clause WHERE id = $decision_id;"
1204
1230
  }
1205
1231
 
1206
1232
  db_query_decisions() {
1207
1233
  local repo_hash="$1" decision_type="${2:-}" limit="${3:-20}"
1208
1234
  if ! db_available; then echo "[]"; return 0; fi
1235
+ repo_hash="${repo_hash//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1209
1236
  local where="WHERE repo_hash = '$repo_hash'"
1210
- [[ -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
1211
1241
  _db_query -json "SELECT * FROM memory_decisions $where ORDER BY updated_at DESC LIMIT $limit;" || echo "[]"
1212
1242
  }
1213
1243
 
@@ -1215,7 +1245,10 @@ db_query_decisions() {
1215
1245
  db_save_embedding() {
1216
1246
  local content_hash="$1" source_type="$2" content_text="$3" repo_hash="${4:-}"
1217
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}"
1218
1250
  content_text="${content_text//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1251
+ repo_hash="${repo_hash//$_SQL_SQ/$_SQL_SQ$_SQL_SQ}"
1219
1252
  _db_exec "INSERT OR IGNORE INTO memory_embeddings (content_hash, source_type, content_text, repo_hash, created_at)
1220
1253
  VALUES ('$content_hash', '$source_type', '$content_text', '$repo_hash', '$(now_iso)');"
1221
1254
  }
@@ -1224,8 +1257,14 @@ db_query_embeddings() {
1224
1257
  local source_type="${1:-}" repo_hash="${2:-}" limit="${3:-50}"
1225
1258
  if ! db_available; then echo "[]"; return 0; fi
1226
1259
  local where="WHERE 1=1"
1227
- [[ -n "$source_type" ]] && where="$where AND source_type = '$source_type'"
1228
- [[ -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
1229
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 "[]"
1230
1269
  }
1231
1270
 
@@ -1364,8 +1403,10 @@ db_sync_push() {
1364
1403
  # Push via HTTP
1365
1404
  local response
1366
1405
  local auth_header=""
1406
+ # shellcheck disable=SC2089
1367
1407
  [[ -n "${SYNC_TOKEN:-}" ]] && auth_header="-H 'Authorization: Bearer ${SYNC_TOKEN}'"
1368
1408
 
1409
+ # shellcheck disable=SC2090
1369
1410
  response=$(curl -s --connect-timeout 10 --max-time 30 -w "%{http_code}" -o /dev/null \
1370
1411
  -X POST "${SYNC_URL}/api/sync/push" \
1371
1412
  -H "Content-Type: application/json" \
@@ -1396,9 +1437,11 @@ db_sync_pull() {
1396
1437
  last_sync=$(_db_query "SELECT value FROM _sync_metadata WHERE key = 'last_pull_epoch';" || echo "0")
1397
1438
 
1398
1439
  local auth_header=""
1440
+ # shellcheck disable=SC2089
1399
1441
  [[ -n "${SYNC_TOKEN:-}" ]] && auth_header="-H 'Authorization: Bearer ${SYNC_TOKEN}'"
1400
1442
 
1401
1443
  local response_body
1444
+ # shellcheck disable=SC2090
1402
1445
  response_body=$(curl -s --connect-timeout 10 --max-time 30 \
1403
1446
  "${SYNC_URL}/api/sync/pull?since=${last_sync}" \
1404
1447
  -H "Accept: application/json" \
@@ -1447,6 +1490,7 @@ migrate_json_data() {
1447
1490
  info "Importing events from ${EVENTS_FILE}..."
1448
1491
  local evt_count=0
1449
1492
  local evt_skipped=0
1493
+ # shellcheck disable=SC2106
1450
1494
  while IFS= read -r line; do
1451
1495
  [[ -z "$line" ]] && continue
1452
1496
  local e_ts e_epoch e_type e_job e_stage e_status
@@ -1480,7 +1524,8 @@ migrate_json_data() {
1480
1524
  j_result=$(echo "$job" | jq -r '.result // ""')
1481
1525
  j_dur=$(echo "$job" | jq -r '.duration // ""')
1482
1526
  j_at=$(echo "$job" | jq -r '.completed_at // ""')
1483
- 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)"
1484
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))
1485
1530
  done < <(jq -c '.completed[]' "$DAEMON_STATE_FILE" 2>/dev/null)
1486
1531
 
@@ -1579,7 +1624,7 @@ export_db() {
1579
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 "[]")
1580
1625
 
1581
1626
  local tmp_file
1582
- tmp_file=$(mktemp "${output_file}.tmp.XXXXXX")
1627
+ tmp_file=$(mktemp "${output_file}.tmp.XXXXXX") || { error "mktemp failed for db export"; return 1; }
1583
1628
  jq -n \
1584
1629
  --arg exported_at "$(now_iso)" \
1585
1630
  --argjson events "$events_json" \
@@ -1666,7 +1711,9 @@ show_status() {
1666
1711
  # Sync status
1667
1712
  local device_id last_push last_pull
1668
1713
  device_id=$(_db_query "SELECT value FROM _sync_metadata WHERE key = 'device_id';" || echo "not set")
1714
+ # shellcheck disable=SC2034
1669
1715
  last_push=$(_db_query "SELECT value FROM _sync_metadata WHERE key = 'last_push_epoch';" || echo "never")
1716
+ # shellcheck disable=SC2034
1670
1717
  last_pull=$(_db_query "SELECT value FROM _sync_metadata WHERE key = 'last_pull_epoch';" || echo "never")
1671
1718
  local unsynced_events unsynced_costs
1672
1719
  unsynced_events=$(_db_query "SELECT COUNT(*) FROM events WHERE synced = 0;" || echo "0")
@@ -1829,8 +1876,12 @@ main() {
1829
1876
  ensure_db_dir
1830
1877
  init_schema
1831
1878
  # Set schema version
1832
- _db_exec "INSERT OR REPLACE INTO _schema (version, created_at, applied_at) VALUES (${SCHEMA_VERSION}, '$(now_iso)', '$(now_iso)');" 2>/dev/null || true
1833
- _db_exec "INSERT OR IGNORE INTO _sync_metadata (key, value, updated_at) VALUES ('device_id', '$(uname -n)-$$-$(now_epoch)', '$(now_iso)');" 2>/dev/null || true
1879
+ if ! _db_exec "INSERT OR REPLACE INTO _schema (version, created_at, applied_at) VALUES (${SCHEMA_VERSION}, '$(now_iso)', '$(now_iso)');" 2>/dev/null; then
1880
+ warn "db init: failed to write schema version ${SCHEMA_VERSION}" >&2
1881
+ fi
1882
+ if ! _db_exec "INSERT OR IGNORE INTO _sync_metadata (key, value, updated_at) VALUES ('device_id', '$(uname -n)-$$-$(now_epoch)', '$(now_iso)');" 2>/dev/null; then
1883
+ warn "db init: failed to write device_id metadata" >&2
1884
+ fi
1834
1885
  success "Database initialized at ${DB_FILE} (WAL mode, schema v${SCHEMA_VERSION})"
1835
1886
  ;;
1836
1887
  migrate)
@@ -5,7 +5,8 @@
5
5
  # ╚═══════════════════════════════════════════════════════════════════════════╝
6
6
  set -euo pipefail
7
7
 
8
- VERSION="3.1.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