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
@@ -0,0 +1,454 @@
1
+ #!/usr/bin/env bash
2
+ # Module guard - prevent double-sourcing
3
+ [[ -n "${_CONSTITUTIONAL_LOADED:-}" ]] && return 0
4
+ _CONSTITUTIONAL_LOADED=1
5
+
6
+ # ╔═══════════════════════════════════════════════════════════════════════════╗
7
+ # ║ shipwright constitutional — Principle-Based Code Self-Critique ║
8
+ # ║ Deterministic constitutional checks against defined code principles ║
9
+ # ║ Checks files and diffs against security, quality, testing rules ║
10
+ # ╚═══════════════════════════════════════════════════════════════════════════╝
11
+
12
+ # shellcheck disable=SC2034
13
+ VERSION="3.3.0"
14
+
15
+ # ─── Output Helpers ──────────────────────────────────────────────────────────
16
+ [[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
17
+ [[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
18
+ [[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
19
+ [[ "$(type -t error 2>/dev/null)" == "function" ]] || error() { echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m $*" >&2; }
20
+
21
+ # ─── Configuration ───────────────────────────────────────────────────────────
22
+
23
+ # Default constitution path (project override takes precedence)
24
+ CONSTITUTIONAL_DEFAULT_PATH="${CONSTITUTIONAL_DEFAULT_PATH:-config/code-constitution.json}"
25
+ CONSTITUTIONAL_PROJECT_OVERRIDE="${CONSTITUTIONAL_PROJECT_OVERRIDE:-.claude/code-constitution.json}"
26
+ CONSTITUTIONAL_REPORT_FILE="${CONSTITUTIONAL_REPORT_FILE:-.claude/pipeline-artifacts/constitution-report.json}"
27
+
28
+ # Loaded constitution cache (module-level)
29
+ _CONSTITUTIONAL_JSON=""
30
+ _CONSTITUTIONAL_SOURCE=""
31
+
32
+ # ─── constitutional_load ─────────────────────────────────────────────────────
33
+ # Load constitution from project override or default config.
34
+ # Sets _CONSTITUTIONAL_JSON with the parsed content.
35
+ # $1: optional explicit path to constitution file
36
+ # Returns: 0 on success, 1 on failure
37
+
38
+ constitutional_load() {
39
+ local explicit_path="${1:-}"
40
+ local constitution_path=""
41
+
42
+ # Priority: explicit > project override > default
43
+ if [[ -n "$explicit_path" && -f "$explicit_path" ]]; then
44
+ constitution_path="$explicit_path"
45
+ elif [[ -f "$CONSTITUTIONAL_PROJECT_OVERRIDE" ]]; then
46
+ constitution_path="$CONSTITUTIONAL_PROJECT_OVERRIDE"
47
+ elif [[ -f "$CONSTITUTIONAL_DEFAULT_PATH" ]]; then
48
+ constitution_path="$CONSTITUTIONAL_DEFAULT_PATH"
49
+ else
50
+ # Try relative to SCRIPT_DIR (for when sourced from pipeline)
51
+ local script_dir_path="${SCRIPT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
52
+ local repo_root
53
+ repo_root=$(cd "$script_dir_path/.." 2>/dev/null && pwd)
54
+ if [[ -f "$repo_root/$CONSTITUTIONAL_DEFAULT_PATH" ]]; then
55
+ constitution_path="$repo_root/$CONSTITUTIONAL_DEFAULT_PATH"
56
+ fi
57
+ fi
58
+
59
+ if [[ -z "$constitution_path" || ! -f "$constitution_path" ]]; then
60
+ warn "No constitution file found"
61
+ return 1
62
+ fi
63
+
64
+ if ! command -v jq >/dev/null 2>&1; then
65
+ error "jq required for constitutional checks"
66
+ return 1
67
+ fi
68
+
69
+ # Validate JSON
70
+ if ! jq empty "$constitution_path" 2>/dev/null; then
71
+ error "Invalid JSON in constitution: $constitution_path"
72
+ return 1
73
+ fi
74
+
75
+ _CONSTITUTIONAL_JSON=$(cat "$constitution_path")
76
+ _CONSTITUTIONAL_SOURCE="$constitution_path"
77
+ return 0
78
+ }
79
+
80
+ # ─── constitutional_get_principles ───────────────────────────────────────────
81
+ # Get all principles, optionally filtered by category or severity.
82
+ # $1: category filter (optional, e.g., "security")
83
+ # $2: severity filter (optional, e.g., "critical")
84
+ # Outputs: JSON array of matching principles
85
+
86
+ constitutional_get_principles() {
87
+ local category="${1:-}"
88
+ local severity="${2:-}"
89
+
90
+ if [[ -z "$_CONSTITUTIONAL_JSON" ]]; then
91
+ echo "[]"
92
+ return 1
93
+ fi
94
+
95
+ local filter=".principles"
96
+
97
+ if [[ -n "$category" ]]; then
98
+ filter="${filter}.${category} // []"
99
+ else
100
+ # Flatten all categories into a single array
101
+ filter="[${filter} | to_entries[] | .value[]]"
102
+ fi
103
+
104
+ local result
105
+ result=$(echo "$_CONSTITUTIONAL_JSON" | jq "$filter" 2>/dev/null || echo "[]")
106
+
107
+ if [[ -n "$severity" ]]; then
108
+ result=$(echo "$result" | jq --arg sev "$severity" '[.[] | select(.severity == $sev)]' 2>/dev/null || echo "[]")
109
+ fi
110
+
111
+ echo "$result"
112
+ }
113
+
114
+ # ─── constitutional_check_file ───────────────────────────────────────────────
115
+ # Check a single file against all principles with automated checks.
116
+ # $1: file path to check
117
+ # $2: optional category filter
118
+ # Outputs: JSON array of violations
119
+ # Returns: 0 (always — violations in output, not exit code)
120
+
121
+ constitutional_check_file() {
122
+ local file_path="${1:-}"
123
+ local category="${2:-}"
124
+
125
+ if [[ -z "$file_path" || ! -f "$file_path" ]]; then
126
+ echo "[]"
127
+ return 0
128
+ fi
129
+
130
+ if [[ -z "$_CONSTITUTIONAL_JSON" ]]; then
131
+ constitutional_load || { echo "[]"; return 0; }
132
+ fi
133
+
134
+ local violations="[]"
135
+ local principles
136
+ principles=$(constitutional_get_principles "$category")
137
+
138
+ local count
139
+ count=$(echo "$principles" | jq 'length' 2>/dev/null || echo "0")
140
+
141
+ local i=0
142
+ while [[ "$i" -lt "$count" ]]; do
143
+ local principle
144
+ principle=$(echo "$principles" | jq ".[$i]" 2>/dev/null)
145
+
146
+ local has_check
147
+ has_check=$(echo "$principle" | jq -r 'has("check")' 2>/dev/null || echo "false")
148
+
149
+ if [[ "$has_check" == "true" ]]; then
150
+ local check_cmd
151
+ check_cmd=$(echo "$principle" | jq -r '.check' 2>/dev/null)
152
+
153
+ # Replace {file} placeholder with actual file path
154
+ check_cmd="${check_cmd//\{file\}/$file_path}"
155
+
156
+ local check_output=""
157
+ # Safety: only allow grep-based checks to prevent code injection
158
+ # from untrusted constitution override files (CRITIC-001)
159
+ if echo "$check_cmd" | grep -qE '^grep\b'; then
160
+ check_output=$(bash -c "$check_cmd" 2>/dev/null) || true
161
+ else
162
+ # Non-grep check commands are logged and skipped
163
+ if type emit_event >/dev/null 2>&1; then
164
+ emit_event "constitutional.unsafe_check_skipped" "cmd=$(echo "$check_cmd" | head -c 50)"
165
+ fi
166
+ fi
167
+
168
+ if [[ -n "$check_output" ]]; then
169
+ local id sev rule
170
+ id=$(echo "$principle" | jq -r '.id' 2>/dev/null)
171
+ sev=$(echo "$principle" | jq -r '.severity' 2>/dev/null)
172
+ rule=$(echo "$principle" | jq -r '.rule' 2>/dev/null)
173
+
174
+ # Parse each matching line
175
+ while IFS= read -r match_line; do
176
+ [[ -z "$match_line" ]] && continue
177
+ local line_num=""
178
+ # Extract line number if present (grep -n format: "N:content")
179
+ if echo "$match_line" | grep -qE '^[0-9]+:'; then
180
+ line_num=$(echo "$match_line" | cut -d: -f1)
181
+ fi
182
+
183
+ violations=$(echo "$violations" | jq \
184
+ --arg id "$id" \
185
+ --arg sev "$sev" \
186
+ --arg rule "$rule" \
187
+ --arg file "$file_path" \
188
+ --arg line "${line_num:-0}" \
189
+ --arg match "$match_line" \
190
+ '. + [{
191
+ "principle_id": $id,
192
+ "severity": $sev,
193
+ "rule": $rule,
194
+ "file": $file,
195
+ "line": ($line | tonumber),
196
+ "match": $match,
197
+ "type": "automated"
198
+ }]' 2>/dev/null || echo "$violations")
199
+ done <<< "$check_output"
200
+ fi
201
+ fi
202
+
203
+ i=$((i + 1))
204
+ done
205
+
206
+ echo "$violations"
207
+ }
208
+
209
+ # ─── constitutional_check_diff ───────────────────────────────────────────────
210
+ # Check only changed lines from a git diff against constitutional principles.
211
+ # $1: base branch or commit (default: main)
212
+ # $2: optional category filter
213
+ # Outputs: JSON array of violations in changed code only
214
+
215
+ constitutional_check_diff() {
216
+ local base="${1:-main}"
217
+ local category="${2:-}"
218
+
219
+ if [[ -z "$_CONSTITUTIONAL_JSON" ]]; then
220
+ constitutional_load || { echo "[]"; return 0; }
221
+ fi
222
+
223
+ # Get list of changed files (added/modified only)
224
+ local changed_files
225
+ changed_files=$(git diff --name-only --diff-filter=AM "$base" 2>/dev/null || true)
226
+
227
+ if [[ -z "$changed_files" ]]; then
228
+ echo "[]"
229
+ return 0
230
+ fi
231
+
232
+ local all_violations="[]"
233
+
234
+ while IFS= read -r file; do
235
+ [[ -z "$file" || ! -f "$file" ]] && continue
236
+
237
+ # Get added line numbers for this file
238
+ local added_lines
239
+ added_lines=$(git diff "$base" -- "$file" 2>/dev/null | \
240
+ grep -nE '^\+' | grep -v '^\+\+\+' | \
241
+ sed 's/^\([0-9]*\):.*/\1/' || true)
242
+
243
+ # Check the file
244
+ local file_violations
245
+ file_violations=$(constitutional_check_file "$file" "$category")
246
+
247
+ local vcount
248
+ vcount=$(echo "$file_violations" | jq 'length' 2>/dev/null || echo "0")
249
+
250
+ # Filter to only violations on changed lines (if we have line info)
251
+ if [[ -n "$added_lines" && "$vcount" -gt 0 ]]; then
252
+ local vi=0
253
+ while [[ "$vi" -lt "$vcount" ]]; do
254
+ local v_line
255
+ v_line=$(echo "$file_violations" | jq -r ".[$vi].line" 2>/dev/null || echo "0")
256
+ # Include violation if line is 0 (no line info) or in changed lines
257
+ if [[ "$v_line" == "0" ]] || echo "$added_lines" | grep -qx "$v_line" 2>/dev/null; then
258
+ local violation
259
+ violation=$(echo "$file_violations" | jq ".[$vi]" 2>/dev/null)
260
+ all_violations=$(echo "$all_violations" | jq --argjson v "$violation" '. + [$v]' 2>/dev/null || echo "$all_violations")
261
+ fi
262
+ vi=$((vi + 1))
263
+ done
264
+ fi
265
+ done <<< "$changed_files"
266
+
267
+ echo "$all_violations"
268
+ }
269
+
270
+ # ─── constitutional_self_critique ────────────────────────────────────────────
271
+ # Generate a full self-critique report with violations and fix suggestions.
272
+ # $1: base branch or commit (default: main)
273
+ # $2: output file (default: CONSTITUTIONAL_REPORT_FILE)
274
+ # Returns: number of violations found
275
+
276
+ constitutional_self_critique() {
277
+ local base="${1:-main}"
278
+ local report_file="${2:-$CONSTITUTIONAL_REPORT_FILE}"
279
+
280
+ if [[ -z "$_CONSTITUTIONAL_JSON" ]]; then
281
+ constitutional_load || return 1
282
+ fi
283
+
284
+ # Ensure artifacts directory exists
285
+ local report_dir
286
+ report_dir=$(dirname "$report_file")
287
+ mkdir -p "$report_dir" 2>/dev/null || true
288
+
289
+ # Run diff-based checks
290
+ local violations
291
+ violations=$(constitutional_check_diff "$base")
292
+
293
+ local total
294
+ total=$(echo "$violations" | jq 'length' 2>/dev/null || echo "0")
295
+
296
+ # Count by severity
297
+ local critical high medium low
298
+ critical=$(echo "$violations" | jq '[.[] | select(.severity == "critical")] | length' 2>/dev/null || echo "0")
299
+ high=$(echo "$violations" | jq '[.[] | select(.severity == "high")] | length' 2>/dev/null || echo "0")
300
+ medium=$(echo "$violations" | jq '[.[] | select(.severity == "medium")] | length' 2>/dev/null || echo "0")
301
+ low=$(echo "$violations" | jq '[.[] | select(.severity == "low")] | length' 2>/dev/null || echo "0")
302
+
303
+ # Build report
304
+ local report
305
+ report=$(jq -n \
306
+ --arg ts "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
307
+ --arg src "${_CONSTITUTIONAL_SOURCE:-unknown}" \
308
+ --arg base "$base" \
309
+ --argjson violations "$violations" \
310
+ --argjson total "$total" \
311
+ --argjson critical "$critical" \
312
+ --argjson high "$high" \
313
+ --argjson medium "$medium" \
314
+ --argjson low "$low" \
315
+ '{
316
+ "timestamp": $ts,
317
+ "constitution_source": $src,
318
+ "base_branch": $base,
319
+ "summary": {
320
+ "total_violations": $total,
321
+ "by_severity": {
322
+ "critical": $critical,
323
+ "high": $high,
324
+ "medium": $medium,
325
+ "low": $low
326
+ },
327
+ "verdict": (if $critical > 0 then "fail" elif $high > 0 then "review_needed" else "pass" end)
328
+ },
329
+ "violations": $violations
330
+ }' 2>/dev/null)
331
+
332
+ # Write report atomically
333
+ local tmp_file
334
+ tmp_file=$(mktemp 2>/dev/null || echo "${report_file}.tmp")
335
+ echo "$report" > "$tmp_file"
336
+ mv "$tmp_file" "$report_file" 2>/dev/null || cp "$tmp_file" "$report_file"
337
+ rm -f "$tmp_file" 2>/dev/null || true
338
+
339
+ # Log results
340
+ if [[ "$total" -gt 0 ]]; then
341
+ if [[ "$critical" -gt 0 ]]; then
342
+ error "Constitutional review: $total violation(s) ($critical critical, $high high)"
343
+ elif [[ "$high" -gt 0 ]]; then
344
+ warn "Constitutional review: $total violation(s) ($high high, $medium medium)"
345
+ else
346
+ info "Constitutional review: $total violation(s) ($medium medium, $low low)"
347
+ fi
348
+ else
349
+ success "Constitutional review: clean — no violations"
350
+ fi
351
+
352
+ # Emit event if available
353
+ if type emit_event >/dev/null 2>&1; then
354
+ emit_event "constitutional.review_complete" \
355
+ "total=$total" \
356
+ "critical=$critical" \
357
+ "high=$high" \
358
+ "verdict=$(echo "$report" | jq -r '.summary.verdict' 2>/dev/null || echo "unknown")"
359
+ fi
360
+
361
+ echo "$total"
362
+ }
363
+
364
+ # ─── constitutional_inject_prompt ────────────────────────────────────────────
365
+ # Format constitutional principles for injection into agent prompts.
366
+ # $1: optional category filter (e.g., "security")
367
+ # $2: optional severity minimum ("critical", "high", "medium", "low")
368
+ # Outputs: formatted text block for prompt injection
369
+
370
+ constitutional_inject_prompt() {
371
+ local category="${1:-}"
372
+ local min_severity="${2:-}"
373
+
374
+ if [[ -z "$_CONSTITUTIONAL_JSON" ]]; then
375
+ constitutional_load || return 0
376
+ fi
377
+
378
+ local principles
379
+ principles=$(constitutional_get_principles "$category")
380
+
381
+ # Filter by minimum severity if specified
382
+ if [[ -n "$min_severity" ]]; then
383
+ local sev_filter
384
+ case "$min_severity" in
385
+ critical) sev_filter='select(.severity == "critical")' ;;
386
+ high) sev_filter='select(.severity == "critical" or .severity == "high")' ;;
387
+ medium) sev_filter='select(.severity == "critical" or .severity == "high" or .severity == "medium")' ;;
388
+ *) sev_filter='.' ;;
389
+ esac
390
+ principles=$(echo "$principles" | jq "[.[] | $sev_filter]" 2>/dev/null || echo "$principles")
391
+ fi
392
+
393
+ local count
394
+ count=$(echo "$principles" | jq 'length' 2>/dev/null || echo "0")
395
+
396
+ if [[ "$count" -eq 0 ]]; then
397
+ return 0
398
+ fi
399
+
400
+ echo "## Code Constitution — Mandatory Principles"
401
+ echo "You MUST follow these principles. Violations will be caught by automated review."
402
+ echo ""
403
+
404
+ local i=0
405
+ while [[ "$i" -lt "$count" ]]; do
406
+ local id sev rule
407
+ id=$(echo "$principles" | jq -r ".[$i].id" 2>/dev/null)
408
+ sev=$(echo "$principles" | jq -r ".[$i].severity" 2>/dev/null)
409
+ rule=$(echo "$principles" | jq -r ".[$i].rule" 2>/dev/null)
410
+ echo "- **[$sev]** $id: $rule"
411
+ i=$((i + 1))
412
+ done
413
+
414
+ # Inject violation history if report exists
415
+ if [[ -f "$CONSTITUTIONAL_REPORT_FILE" ]]; then
416
+ local prev_total
417
+ prev_total=$(jq -r '.summary.total_violations // 0' "$CONSTITUTIONAL_REPORT_FILE" 2>/dev/null || echo "0")
418
+ if [[ "$prev_total" -gt 0 ]]; then
419
+ echo ""
420
+ echo "### Previous Violations (fix these first)"
421
+ jq -r '.violations[]? | "- [\(.severity)] \(.principle_id): \(.rule) — \(.file):\(.line)"' \
422
+ "$CONSTITUTIONAL_REPORT_FILE" 2>/dev/null | head -20
423
+ fi
424
+ fi
425
+ }
426
+
427
+ # ─── constitutional_format_violations ────────────────────────────────────────
428
+ # Format violations as human-readable text.
429
+ # $1: JSON violations array
430
+ # Outputs: formatted violation list
431
+
432
+ constitutional_format_violations() {
433
+ local violations="${1:-[]}"
434
+
435
+ local count
436
+ count=$(echo "$violations" | jq 'length' 2>/dev/null || echo "0")
437
+
438
+ if [[ "$count" -eq 0 ]]; then
439
+ echo "No violations found."
440
+ return 0
441
+ fi
442
+
443
+ local i=0
444
+ while [[ "$i" -lt "$count" ]]; do
445
+ local id sev rule file line
446
+ id=$(echo "$violations" | jq -r ".[$i].principle_id" 2>/dev/null)
447
+ sev=$(echo "$violations" | jq -r ".[$i].severity" 2>/dev/null)
448
+ rule=$(echo "$violations" | jq -r ".[$i].rule" 2>/dev/null)
449
+ file=$(echo "$violations" | jq -r ".[$i].file" 2>/dev/null)
450
+ line=$(echo "$violations" | jq -r ".[$i].line" 2>/dev/null)
451
+ echo "VIOLATION [$sev] $id: $rule — $file:$line"
452
+ i=$((i + 1))
453
+ done
454
+ }