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.
- package/.claude/agents/code-reviewer.md +2 -0
- package/.claude/agents/devops-engineer.md +2 -0
- package/.claude/agents/doc-fleet-agent.md +2 -0
- package/.claude/agents/pipeline-agent.md +2 -0
- package/.claude/agents/shell-script-specialist.md +2 -0
- package/.claude/agents/test-specialist.md +2 -0
- package/.claude/hooks/agent-crash-capture.sh +32 -0
- package/.claude/hooks/post-tool-use.sh +3 -2
- package/.claude/hooks/pre-tool-use.sh +35 -3
- package/README.md +22 -8
- package/claude-code/hooks/config-change.sh +18 -0
- package/claude-code/hooks/instructions-reloaded.sh +7 -0
- package/claude-code/hooks/worktree-create.sh +25 -0
- package/claude-code/hooks/worktree-remove.sh +20 -0
- package/config/code-constitution.json +130 -0
- package/config/defaults.json +25 -2
- package/config/policy.json +1 -1
- package/dashboard/middleware/auth.ts +134 -0
- package/dashboard/middleware/constants.ts +21 -0
- package/dashboard/public/index.html +8 -6
- package/dashboard/public/styles.css +176 -97
- package/dashboard/routes/auth.ts +38 -0
- package/dashboard/server.ts +117 -25
- package/dashboard/services/config.ts +26 -0
- package/dashboard/services/db.ts +118 -0
- package/dashboard/src/canvas/pixel-agent.ts +298 -0
- package/dashboard/src/canvas/pixel-sprites.ts +440 -0
- package/dashboard/src/canvas/shipyard-effects.ts +367 -0
- package/dashboard/src/canvas/shipyard-scene.ts +616 -0
- package/dashboard/src/canvas/submarine-layout.ts +267 -0
- package/dashboard/src/components/header.ts +8 -7
- package/dashboard/src/core/api.ts +5 -0
- package/dashboard/src/core/router.ts +1 -0
- package/dashboard/src/design/submarine-theme.ts +253 -0
- package/dashboard/src/main.ts +2 -0
- package/dashboard/src/types/api.ts +12 -1
- package/dashboard/src/views/activity.ts +2 -1
- package/dashboard/src/views/metrics.ts +69 -1
- package/dashboard/src/views/shipyard.ts +39 -0
- package/dashboard/types/index.ts +166 -0
- package/docs/plans/2026-02-28-compound-audit-and-shipyard-design.md +186 -0
- package/docs/plans/2026-02-28-skipper-shipwright-implementation-plan.md +1182 -0
- package/docs/plans/2026-02-28-skipper-shipwright-integration-design.md +531 -0
- package/docs/plans/2026-03-01-ai-powered-skill-injection-design.md +298 -0
- package/docs/plans/2026-03-01-ai-powered-skill-injection-plan.md +1109 -0
- package/docs/plans/2026-03-01-capabilities-cleanup-plan.md +658 -0
- package/docs/plans/2026-03-01-clean-architecture-plan.md +924 -0
- package/docs/plans/2026-03-01-compound-audit-cascade-design.md +191 -0
- package/docs/plans/2026-03-01-compound-audit-cascade-plan.md +921 -0
- package/docs/plans/2026-03-01-deep-integration-plan.md +851 -0
- package/docs/plans/2026-03-01-pipeline-audit-trail-design.md +145 -0
- package/docs/plans/2026-03-01-pipeline-audit-trail-plan.md +770 -0
- package/docs/plans/2026-03-01-refined-depths-brand-design.md +382 -0
- package/docs/plans/2026-03-01-refined-depths-implementation.md +599 -0
- package/docs/plans/2026-03-01-skipper-kernel-integration-design.md +203 -0
- package/docs/plans/2026-03-01-unified-platform-design.md +272 -0
- package/docs/plans/2026-03-07-claude-code-feature-integration-design.md +189 -0
- package/docs/plans/2026-03-07-claude-code-feature-integration-plan.md +1165 -0
- package/docs/research/BACKLOG_QUICK_REFERENCE.md +352 -0
- package/docs/research/CUTTING_EDGE_RESEARCH_2026.md +546 -0
- package/docs/research/RESEARCH_INDEX.md +439 -0
- package/docs/research/RESEARCH_SOURCES.md +440 -0
- package/docs/research/RESEARCH_SUMMARY.txt +275 -0
- package/docs/superpowers/specs/2026-03-10-pipeline-quality-revolution-design.md +341 -0
- package/package.json +2 -2
- package/scripts/lib/adaptive-model.sh +427 -0
- package/scripts/lib/adaptive-timeout.sh +316 -0
- package/scripts/lib/audit-trail.sh +309 -0
- package/scripts/lib/auto-recovery.sh +471 -0
- package/scripts/lib/bandit-selector.sh +431 -0
- package/scripts/lib/bootstrap.sh +104 -2
- package/scripts/lib/causal-graph.sh +455 -0
- package/scripts/lib/compat.sh +126 -0
- package/scripts/lib/compound-audit.sh +337 -0
- package/scripts/lib/constitutional.sh +454 -0
- package/scripts/lib/context-budget.sh +359 -0
- package/scripts/lib/convergence.sh +594 -0
- package/scripts/lib/cost-optimizer.sh +634 -0
- package/scripts/lib/daemon-adaptive.sh +14 -2
- package/scripts/lib/daemon-dispatch.sh +106 -17
- package/scripts/lib/daemon-failure.sh +34 -4
- package/scripts/lib/daemon-patrol.sh +25 -4
- package/scripts/lib/daemon-poll-github.sh +361 -0
- package/scripts/lib/daemon-poll-health.sh +299 -0
- package/scripts/lib/daemon-poll.sh +27 -611
- package/scripts/lib/daemon-state.sh +119 -66
- package/scripts/lib/daemon-triage.sh +10 -0
- package/scripts/lib/dod-scorecard.sh +442 -0
- package/scripts/lib/error-actionability.sh +300 -0
- package/scripts/lib/formal-spec.sh +461 -0
- package/scripts/lib/helpers.sh +180 -5
- package/scripts/lib/intent-analysis.sh +409 -0
- package/scripts/lib/loop-convergence.sh +350 -0
- package/scripts/lib/loop-iteration.sh +682 -0
- package/scripts/lib/loop-progress.sh +48 -0
- package/scripts/lib/loop-restart.sh +185 -0
- package/scripts/lib/memory-effectiveness.sh +506 -0
- package/scripts/lib/mutation-executor.sh +352 -0
- package/scripts/lib/outcome-feedback.sh +521 -0
- package/scripts/lib/pipeline-cli.sh +336 -0
- package/scripts/lib/pipeline-commands.sh +1216 -0
- package/scripts/lib/pipeline-detection.sh +101 -3
- package/scripts/lib/pipeline-execution.sh +897 -0
- package/scripts/lib/pipeline-github.sh +28 -3
- package/scripts/lib/pipeline-intelligence-compound.sh +431 -0
- package/scripts/lib/pipeline-intelligence-scoring.sh +407 -0
- package/scripts/lib/pipeline-intelligence-skip.sh +181 -0
- package/scripts/lib/pipeline-intelligence.sh +104 -1138
- package/scripts/lib/pipeline-quality-bash-compat.sh +182 -0
- package/scripts/lib/pipeline-quality-checks.sh +17 -711
- package/scripts/lib/pipeline-quality-gates.sh +563 -0
- package/scripts/lib/pipeline-stages-build.sh +730 -0
- package/scripts/lib/pipeline-stages-delivery.sh +965 -0
- package/scripts/lib/pipeline-stages-intake.sh +1133 -0
- package/scripts/lib/pipeline-stages-monitor.sh +407 -0
- package/scripts/lib/pipeline-stages-review.sh +1022 -0
- package/scripts/lib/pipeline-stages.sh +161 -2901
- package/scripts/lib/pipeline-state.sh +36 -5
- package/scripts/lib/pipeline-util.sh +487 -0
- package/scripts/lib/policy-learner.sh +438 -0
- package/scripts/lib/process-reward.sh +493 -0
- package/scripts/lib/project-detect.sh +649 -0
- package/scripts/lib/quality-profile.sh +334 -0
- package/scripts/lib/recruit-commands.sh +885 -0
- package/scripts/lib/recruit-learning.sh +739 -0
- package/scripts/lib/recruit-roles.sh +648 -0
- package/scripts/lib/reward-aggregator.sh +458 -0
- package/scripts/lib/rl-optimizer.sh +362 -0
- package/scripts/lib/root-cause.sh +427 -0
- package/scripts/lib/scope-enforcement.sh +445 -0
- package/scripts/lib/session-restart.sh +493 -0
- package/scripts/lib/skill-memory.sh +300 -0
- package/scripts/lib/skill-registry.sh +775 -0
- package/scripts/lib/spec-driven.sh +476 -0
- package/scripts/lib/test-helpers.sh +18 -7
- package/scripts/lib/test-holdout.sh +429 -0
- package/scripts/lib/test-optimizer.sh +511 -0
- package/scripts/shipwright-file-suggest.sh +45 -0
- package/scripts/skills/adversarial-quality.md +61 -0
- package/scripts/skills/api-design.md +44 -0
- package/scripts/skills/architecture-design.md +50 -0
- package/scripts/skills/brainstorming.md +43 -0
- package/scripts/skills/data-pipeline.md +44 -0
- package/scripts/skills/deploy-safety.md +64 -0
- package/scripts/skills/documentation.md +38 -0
- package/scripts/skills/frontend-design.md +45 -0
- package/scripts/skills/generated/.gitkeep +0 -0
- package/scripts/skills/generated/_refinements/.gitkeep +0 -0
- package/scripts/skills/generated/_refinements/adversarial-quality.patch.md +3 -0
- package/scripts/skills/generated/_refinements/architecture-design.patch.md +3 -0
- package/scripts/skills/generated/_refinements/brainstorming.patch.md +3 -0
- package/scripts/skills/generated/cli-version-management.md +29 -0
- package/scripts/skills/generated/collection-system-validation.md +99 -0
- package/scripts/skills/generated/large-scale-c-refactoring-coordination.md +97 -0
- package/scripts/skills/generated/pattern-matching-similarity-scoring.md +195 -0
- package/scripts/skills/generated/test-parallelization-detection.md +65 -0
- package/scripts/skills/observability.md +79 -0
- package/scripts/skills/performance.md +48 -0
- package/scripts/skills/pr-quality.md +49 -0
- package/scripts/skills/product-thinking.md +43 -0
- package/scripts/skills/security-audit.md +49 -0
- package/scripts/skills/systematic-debugging.md +40 -0
- package/scripts/skills/testing-strategy.md +47 -0
- package/scripts/skills/two-stage-review.md +52 -0
- package/scripts/skills/validation-thoroughness.md +55 -0
- package/scripts/sw +9 -3
- package/scripts/sw-activity.sh +9 -8
- package/scripts/sw-adaptive.sh +8 -7
- package/scripts/sw-adversarial.sh +2 -1
- package/scripts/sw-architecture-enforcer.sh +3 -1
- package/scripts/sw-auth.sh +12 -2
- package/scripts/sw-autonomous.sh +5 -1
- package/scripts/sw-changelog.sh +4 -1
- package/scripts/sw-checkpoint.sh +2 -1
- package/scripts/sw-ci.sh +15 -6
- package/scripts/sw-cleanup.sh +4 -26
- package/scripts/sw-code-review.sh +45 -20
- package/scripts/sw-connect.sh +2 -1
- package/scripts/sw-context.sh +2 -1
- package/scripts/sw-cost.sh +107 -5
- package/scripts/sw-daemon.sh +71 -11
- package/scripts/sw-dashboard.sh +3 -1
- package/scripts/sw-db.sh +71 -20
- package/scripts/sw-decide.sh +8 -2
- package/scripts/sw-decompose.sh +360 -17
- package/scripts/sw-deps.sh +4 -1
- package/scripts/sw-developer-simulation.sh +4 -1
- package/scripts/sw-discovery.sh +378 -5
- package/scripts/sw-doc-fleet.sh +4 -1
- package/scripts/sw-docs-agent.sh +3 -1
- package/scripts/sw-docs.sh +2 -1
- package/scripts/sw-doctor.sh +453 -2
- package/scripts/sw-dora.sh +4 -1
- package/scripts/sw-durable.sh +12 -7
- package/scripts/sw-e2e-orchestrator.sh +17 -16
- package/scripts/sw-eventbus.sh +13 -4
- package/scripts/sw-evidence.sh +364 -12
- package/scripts/sw-feedback.sh +550 -9
- package/scripts/sw-fix.sh +20 -1
- package/scripts/sw-fleet-discover.sh +6 -2
- package/scripts/sw-fleet-viz.sh +9 -4
- package/scripts/sw-fleet.sh +5 -1
- package/scripts/sw-github-app.sh +18 -4
- package/scripts/sw-github-checks.sh +3 -2
- package/scripts/sw-github-deploy.sh +3 -2
- package/scripts/sw-github-graphql.sh +18 -7
- package/scripts/sw-guild.sh +5 -1
- package/scripts/sw-heartbeat.sh +5 -30
- package/scripts/sw-hello.sh +67 -0
- package/scripts/sw-hygiene.sh +10 -3
- package/scripts/sw-incident.sh +273 -5
- package/scripts/sw-init.sh +18 -2
- package/scripts/sw-instrument.sh +10 -2
- package/scripts/sw-intelligence.sh +44 -7
- package/scripts/sw-jira.sh +5 -1
- package/scripts/sw-launchd.sh +2 -1
- package/scripts/sw-linear.sh +4 -1
- package/scripts/sw-logs.sh +4 -1
- package/scripts/sw-loop.sh +436 -1076
- package/scripts/sw-memory.sh +357 -3
- package/scripts/sw-mission-control.sh +6 -1
- package/scripts/sw-model-router.sh +483 -27
- package/scripts/sw-otel.sh +15 -4
- package/scripts/sw-oversight.sh +14 -5
- package/scripts/sw-patrol-meta.sh +334 -0
- package/scripts/sw-pipeline-composer.sh +7 -1
- package/scripts/sw-pipeline-vitals.sh +12 -6
- package/scripts/sw-pipeline.sh +54 -2653
- package/scripts/sw-pm.sh +16 -8
- package/scripts/sw-pr-lifecycle.sh +2 -1
- package/scripts/sw-predictive.sh +17 -5
- package/scripts/sw-prep.sh +185 -2
- package/scripts/sw-ps.sh +5 -25
- package/scripts/sw-public-dashboard.sh +17 -4
- package/scripts/sw-quality.sh +14 -6
- package/scripts/sw-reaper.sh +8 -25
- package/scripts/sw-recruit.sh +156 -2303
- package/scripts/sw-regression.sh +19 -12
- package/scripts/sw-release-manager.sh +3 -1
- package/scripts/sw-release.sh +4 -1
- package/scripts/sw-remote.sh +3 -1
- package/scripts/sw-replay.sh +7 -1
- package/scripts/sw-retro.sh +158 -1
- package/scripts/sw-review-rerun.sh +3 -1
- package/scripts/sw-scale.sh +14 -5
- package/scripts/sw-security-audit.sh +6 -1
- package/scripts/sw-self-optimize.sh +173 -6
- package/scripts/sw-session.sh +9 -3
- package/scripts/sw-setup.sh +3 -1
- package/scripts/sw-stall-detector.sh +406 -0
- package/scripts/sw-standup.sh +15 -7
- package/scripts/sw-status.sh +3 -1
- package/scripts/sw-strategic.sh +14 -6
- package/scripts/sw-stream.sh +13 -4
- package/scripts/sw-swarm.sh +20 -7
- package/scripts/sw-team-stages.sh +13 -6
- package/scripts/sw-templates.sh +7 -31
- package/scripts/sw-testgen.sh +17 -6
- package/scripts/sw-tmux-pipeline.sh +4 -1
- package/scripts/sw-tmux-role-color.sh +2 -0
- package/scripts/sw-tmux-status.sh +1 -1
- package/scripts/sw-tmux.sh +37 -1
- package/scripts/sw-trace.sh +3 -1
- package/scripts/sw-tracker-github.sh +3 -0
- package/scripts/sw-tracker-jira.sh +3 -0
- package/scripts/sw-tracker-linear.sh +3 -0
- package/scripts/sw-tracker.sh +3 -1
- package/scripts/sw-triage.sh +3 -2
- package/scripts/sw-upgrade.sh +3 -1
- package/scripts/sw-ux.sh +5 -2
- package/scripts/sw-webhook.sh +5 -2
- package/scripts/sw-widgets.sh +9 -4
- package/scripts/sw-worktree.sh +15 -3
- package/scripts/test-skill-injection.sh +1233 -0
- package/templates/pipelines/autonomous.json +27 -3
- package/templates/pipelines/cost-aware.json +34 -8
- package/templates/pipelines/deployed.json +12 -0
- package/templates/pipelines/enterprise.json +12 -0
- package/templates/pipelines/fast.json +6 -0
- package/templates/pipelines/full.json +27 -3
- package/templates/pipelines/hotfix.json +6 -0
- package/templates/pipelines/standard.json +12 -0
- 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
|
+
}
|