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,682 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Module guard - prevent double-sourcing
|
|
3
|
+
[[ -n "${_LOOP_ITERATION_LOADED:-}" ]] && return 0
|
|
4
|
+
_LOOP_ITERATION_LOADED=1
|
|
5
|
+
|
|
6
|
+
# ─── Prompt Composition ──────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
manage_context_window() {
|
|
9
|
+
local prompt="$1"
|
|
10
|
+
local budget="${CONTEXT_BUDGET_CHARS:-200000}"
|
|
11
|
+
local current_len=${#prompt}
|
|
12
|
+
|
|
13
|
+
# Read trimming tunables from config (env > daemon-config > policy > defaults.json)
|
|
14
|
+
local trim_memory_chars trim_git_entries trim_hotspot_files trim_test_lines
|
|
15
|
+
trim_memory_chars=$(_config_get_int "loop.context_trim_memory_chars" 20000 2>/dev/null || echo 20000)
|
|
16
|
+
trim_git_entries=$(_config_get_int "loop.context_trim_git_entries" 10 2>/dev/null || echo 10)
|
|
17
|
+
trim_hotspot_files=$(_config_get_int "loop.context_trim_hotspot_files" 5 2>/dev/null || echo 5)
|
|
18
|
+
trim_test_lines=$(_config_get_int "loop.context_trim_test_lines" 50 2>/dev/null || echo 50)
|
|
19
|
+
|
|
20
|
+
if [[ "$current_len" -le "$budget" ]]; then
|
|
21
|
+
echo "$prompt"
|
|
22
|
+
return
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# Over budget — progressively trim sections (least important first)
|
|
26
|
+
local trimmed="$prompt"
|
|
27
|
+
|
|
28
|
+
# 1. Trim DORA/Performance baselines (least critical for code generation)
|
|
29
|
+
if [[ "${#trimmed}" -gt "$budget" ]]; then
|
|
30
|
+
trimmed=$(echo "$trimmed" | awk '/^## Performance Baselines/{skip=1; next} skip && /^## [^#]/{skip=0} !skip{print}')
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# 2. Trim file hotspots to top N
|
|
34
|
+
if [[ "${#trimmed}" -gt "$budget" ]]; then
|
|
35
|
+
trimmed=$(echo "$trimmed" | awk -v max="$trim_hotspot_files" '/## File Hotspots/{p=1; c=0} p && /^- /{c++; if(c>max) next} {print}')
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# 3. Trim git log to last N entries
|
|
39
|
+
if [[ "${#trimmed}" -gt "$budget" ]]; then
|
|
40
|
+
trimmed=$(echo "$trimmed" | awk -v max="$trim_git_entries" '/## Recent Git Activity/{p=1; c=0} p && /^[a-f0-9]/{c++; if(c>max) next} {print}')
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# 4. Truncate memory context to first N chars
|
|
44
|
+
if [[ "${#trimmed}" -gt "$budget" ]]; then
|
|
45
|
+
trimmed=$(echo "$trimmed" | awk -v max="$trim_memory_chars" '
|
|
46
|
+
/## Memory Context/{mem=1; skip_rest=0; chars=0; print; next}
|
|
47
|
+
mem && /^## [^#]/{mem=0; print; next}
|
|
48
|
+
mem{chars+=length($0)+1; if(chars>max){print "... (memory truncated for context budget)"; skip_rest=1; mem=0; next}}
|
|
49
|
+
skip_rest && /^## [^#]/{skip_rest=0; print; next}
|
|
50
|
+
skip_rest{next}
|
|
51
|
+
{print}
|
|
52
|
+
')
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# 5. Truncate test output to last N lines
|
|
56
|
+
if [[ "${#trimmed}" -gt "$budget" ]]; then
|
|
57
|
+
trimmed=$(echo "$trimmed" | awk -v max="$trim_test_lines" '
|
|
58
|
+
/## Test Results/{found=1; buf=""; print; next}
|
|
59
|
+
found && /^## [^#]/{found=0; n=split(buf,arr,"\n"); start=(n>max)?(n-max+1):1; for(i=start;i<=n;i++) if(arr[i]!="") print arr[i]; print; next}
|
|
60
|
+
found{buf=buf $0 "\n"; next}
|
|
61
|
+
{print}
|
|
62
|
+
')
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# 6. Last resort: hard truncate with notice
|
|
66
|
+
if [[ "${#trimmed}" -gt "$budget" ]]; then
|
|
67
|
+
trimmed="${trimmed:0:$budget}
|
|
68
|
+
|
|
69
|
+
... [CONTEXT TRUNCATED: prompt exceeded ${budget} char budget. Focus on the goal and most recent errors.]"
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# Log the trimming
|
|
73
|
+
local final_len=${#trimmed}
|
|
74
|
+
if [[ "$final_len" -lt "$current_len" ]]; then
|
|
75
|
+
warn "Context trimmed from ${current_len} to ${final_len} chars (budget: ${budget})"
|
|
76
|
+
emit_event "loop.context_trimmed" "original=$current_len" "trimmed=$final_len" "budget=$budget" 2>/dev/null || true
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
echo "$trimmed"
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
compose_prompt() {
|
|
83
|
+
local recent_log
|
|
84
|
+
# Get last 3 iteration summaries from log entries
|
|
85
|
+
recent_log="$(echo "$LOG_ENTRIES" | tail -15)"
|
|
86
|
+
if [[ -z "$recent_log" ]]; then
|
|
87
|
+
recent_log="(first iteration — no previous progress)"
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
local git_log
|
|
91
|
+
git_log="$(git_recent_log)"
|
|
92
|
+
|
|
93
|
+
local test_section
|
|
94
|
+
if [[ -z "$TEST_CMD" ]]; then
|
|
95
|
+
test_section="No test command configured."
|
|
96
|
+
elif [[ -z "$TEST_PASSED" ]]; then
|
|
97
|
+
test_section="No test results yet (first iteration). Test command: $TEST_CMD"
|
|
98
|
+
elif $TEST_PASSED; then
|
|
99
|
+
test_section="$TEST_OUTPUT"
|
|
100
|
+
else
|
|
101
|
+
test_section="TESTS FAILED — fix these before proceeding:
|
|
102
|
+
$TEST_OUTPUT"
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
# Structured error context (machine-readable)
|
|
106
|
+
local error_summary_section=""
|
|
107
|
+
local error_json="$LOG_DIR/error-summary.json"
|
|
108
|
+
if [[ -f "$error_json" ]]; then
|
|
109
|
+
local err_count err_lines
|
|
110
|
+
err_count=$(jq -r '.error_count // 0' "$error_json" 2>/dev/null || echo "0")
|
|
111
|
+
err_lines=$(jq -r '.error_lines[]? // empty' "$error_json" 2>/dev/null | head -10 || true)
|
|
112
|
+
if [[ "$err_count" -gt 0 ]] && [[ -n "$err_lines" ]]; then
|
|
113
|
+
error_summary_section="## Structured Error Summary (${err_count} errors detected)
|
|
114
|
+
${err_lines}
|
|
115
|
+
|
|
116
|
+
Fix these specific errors. Each line above is one distinct error from the test output."
|
|
117
|
+
fi
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
# Build audit sections (captured before heredoc to avoid nested heredoc issues)
|
|
121
|
+
local audit_section
|
|
122
|
+
audit_section="$(compose_audit_section)"
|
|
123
|
+
local audit_feedback_section
|
|
124
|
+
audit_feedback_section="$(compose_audit_feedback_section)"
|
|
125
|
+
local rejection_notice_section
|
|
126
|
+
rejection_notice_section="$(compose_rejection_notice_section)"
|
|
127
|
+
|
|
128
|
+
# Memory context injection (failure patterns + past learnings)
|
|
129
|
+
local memory_section=""
|
|
130
|
+
if type memory_inject_context >/dev/null 2>&1; then
|
|
131
|
+
memory_section="$(memory_inject_context "build" 2>/dev/null || true)"
|
|
132
|
+
elif [[ -f "$SCRIPT_DIR/sw-memory.sh" ]]; then
|
|
133
|
+
memory_section="$("$SCRIPT_DIR/sw-memory.sh" inject build 2>/dev/null || true)"
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# Cross-pipeline discovery injection (learnings from other pipeline runs)
|
|
137
|
+
local discovery_section=""
|
|
138
|
+
if type inject_discoveries >/dev/null 2>&1; then
|
|
139
|
+
local disc_output
|
|
140
|
+
disc_output="$(inject_discoveries "${GOAL:-}" 2>/dev/null || true)"
|
|
141
|
+
if [[ -n "$disc_output" ]]; then
|
|
142
|
+
discovery_section="$disc_output"
|
|
143
|
+
fi
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
# DORA baselines for context
|
|
147
|
+
local dora_section=""
|
|
148
|
+
if type memory_get_dora_baseline >/dev/null 2>&1; then
|
|
149
|
+
local dora_json
|
|
150
|
+
dora_json="$(memory_get_dora_baseline 7 2>/dev/null || echo "{}")"
|
|
151
|
+
local dora_total
|
|
152
|
+
dora_total=$(echo "$dora_json" | jq -r '.total // 0' 2>/dev/null || echo "0")
|
|
153
|
+
if [[ "$dora_total" -gt 0 ]]; then
|
|
154
|
+
local dora_df dora_cfr
|
|
155
|
+
dora_df=$(echo "$dora_json" | jq -r '.deploy_freq // 0' 2>/dev/null || echo "0")
|
|
156
|
+
dora_cfr=$(echo "$dora_json" | jq -r '.cfr // 0' 2>/dev/null || echo "0")
|
|
157
|
+
dora_section="## Performance Baselines (Last 7 Days)
|
|
158
|
+
- Deploy frequency: ${dora_df}/week
|
|
159
|
+
- Change failure rate: ${dora_cfr}%
|
|
160
|
+
- Total pipeline runs: ${dora_total}"
|
|
161
|
+
fi
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
# Append mid-loop memory refresh if available
|
|
165
|
+
local memory_refresh_file="$LOG_DIR/memory-refresh-$(( ITERATION - 1 )).txt"
|
|
166
|
+
if [[ -f "$memory_refresh_file" ]]; then
|
|
167
|
+
memory_section="${memory_section}
|
|
168
|
+
|
|
169
|
+
## Fresh Context (from iteration $(( ITERATION - 1 )) analysis)
|
|
170
|
+
$(cat "$memory_refresh_file")"
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
# GitHub intelligence context (gated by availability)
|
|
174
|
+
local intelligence_section=""
|
|
175
|
+
if [[ "${NO_GITHUB:-}" != "true" ]]; then
|
|
176
|
+
# File hotspots — top 5 most-changed files
|
|
177
|
+
if type gh_file_change_frequency >/dev/null 2>&1; then
|
|
178
|
+
local hotspots
|
|
179
|
+
hotspots=$(gh_file_change_frequency 2>/dev/null | head -5 || true)
|
|
180
|
+
if [[ -n "$hotspots" ]]; then
|
|
181
|
+
intelligence_section="${intelligence_section}
|
|
182
|
+
## File Hotspots (most frequently changed)
|
|
183
|
+
${hotspots}"
|
|
184
|
+
fi
|
|
185
|
+
fi
|
|
186
|
+
|
|
187
|
+
# CODEOWNERS context
|
|
188
|
+
if type gh_codeowners >/dev/null 2>&1; then
|
|
189
|
+
local owners
|
|
190
|
+
owners=$(gh_codeowners 2>/dev/null | head -10 || true)
|
|
191
|
+
if [[ -n "$owners" ]]; then
|
|
192
|
+
intelligence_section="${intelligence_section}
|
|
193
|
+
## Code Owners
|
|
194
|
+
${owners}"
|
|
195
|
+
fi
|
|
196
|
+
fi
|
|
197
|
+
|
|
198
|
+
# Active security alerts
|
|
199
|
+
if type gh_security_alerts >/dev/null 2>&1; then
|
|
200
|
+
local alerts
|
|
201
|
+
alerts=$(gh_security_alerts 2>/dev/null | head -5 || true)
|
|
202
|
+
if [[ -n "$alerts" ]]; then
|
|
203
|
+
intelligence_section="${intelligence_section}
|
|
204
|
+
## Active Security Alerts
|
|
205
|
+
${alerts}"
|
|
206
|
+
fi
|
|
207
|
+
fi
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
# Architecture rules (from intelligence layer)
|
|
211
|
+
local repo_hash
|
|
212
|
+
repo_hash=$(echo -n "$(pwd)" | shasum -a 256 2>/dev/null | cut -c1-12 || echo "unknown")
|
|
213
|
+
local arch_file="${HOME}/.shipwright/memory/${repo_hash}/architecture.json"
|
|
214
|
+
if [[ -f "$arch_file" ]]; then
|
|
215
|
+
local arch_rules
|
|
216
|
+
arch_rules=$(jq -r '.rules[]? // empty' "$arch_file" 2>/dev/null | head -10 || true)
|
|
217
|
+
if [[ -n "$arch_rules" ]]; then
|
|
218
|
+
intelligence_section="${intelligence_section}
|
|
219
|
+
## Architecture Rules
|
|
220
|
+
${arch_rules}"
|
|
221
|
+
fi
|
|
222
|
+
fi
|
|
223
|
+
|
|
224
|
+
# Coverage baseline
|
|
225
|
+
local coverage_file="${HOME}/.shipwright/baselines/${repo_hash}/coverage.json"
|
|
226
|
+
if [[ -f "$coverage_file" ]]; then
|
|
227
|
+
local coverage_pct
|
|
228
|
+
coverage_pct=$(jq -r '.coverage_percent // empty' "$coverage_file" 2>/dev/null || true)
|
|
229
|
+
if [[ -n "$coverage_pct" ]]; then
|
|
230
|
+
intelligence_section="${intelligence_section}
|
|
231
|
+
## Coverage Baseline
|
|
232
|
+
Current coverage: ${coverage_pct}% — do not decrease this."
|
|
233
|
+
fi
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
# Error classification from last failure
|
|
237
|
+
local error_log=".claude/pipeline-artifacts/error-log.jsonl"
|
|
238
|
+
if [[ -f "$error_log" ]]; then
|
|
239
|
+
local last_error
|
|
240
|
+
last_error=$(tail -1 "$error_log" 2>/dev/null | jq -r '"Type: \(.type), Exit: \(.exit_code), Error: \(.error | split("\n") | first)"' 2>/dev/null || true)
|
|
241
|
+
if [[ -n "$last_error" ]]; then
|
|
242
|
+
intelligence_section="${intelligence_section}
|
|
243
|
+
## Last Error Context
|
|
244
|
+
${last_error}"
|
|
245
|
+
fi
|
|
246
|
+
fi
|
|
247
|
+
|
|
248
|
+
# Stuckness detection — compare last 3 iteration outputs
|
|
249
|
+
local stuckness_section=""
|
|
250
|
+
stuckness_section="$(detect_stuckness)"
|
|
251
|
+
local _stuck_ret=$?
|
|
252
|
+
local stuckness_detected=false
|
|
253
|
+
[[ "$_stuck_ret" -eq 0 ]] && stuckness_detected=true
|
|
254
|
+
|
|
255
|
+
# Strategy exploration when stuck — append alternative strategy to GOAL
|
|
256
|
+
if [[ "$stuckness_detected" == "true" ]]; then
|
|
257
|
+
local last_error diagnosis
|
|
258
|
+
last_error=$(tail -1 "${ARTIFACTS_DIR:-${PROJECT_ROOT:-.}/.claude/pipeline-artifacts}/error-log.jsonl" 2>/dev/null | jq -r '"Type: \(.type), Exit: \(.exit_code), Error: \(.error | split("\n") | first)"' 2>/dev/null || true)
|
|
259
|
+
[[ -z "$last_error" || "$last_error" == "null" ]] && last_error="unknown"
|
|
260
|
+
diagnosis="${STUCKNESS_DIAGNOSIS:-}"
|
|
261
|
+
local alt_strategy
|
|
262
|
+
alt_strategy=$(explore_alternative_strategy "$last_error" "${ITERATION:-0}" "$diagnosis")
|
|
263
|
+
GOAL="${GOAL}
|
|
264
|
+
|
|
265
|
+
${alt_strategy}"
|
|
266
|
+
|
|
267
|
+
# Handle model escalation
|
|
268
|
+
if [[ "${ESCALATE_MODEL:-}" == "true" ]]; then
|
|
269
|
+
if [[ -f "$SCRIPT_DIR/sw-model-router.sh" ]]; then
|
|
270
|
+
source "$SCRIPT_DIR/sw-model-router.sh" 2>/dev/null || true
|
|
271
|
+
fi
|
|
272
|
+
if type escalate_model &>/dev/null; then
|
|
273
|
+
MODEL=$(escalate_model "${MODEL:-sonnet}")
|
|
274
|
+
info "Escalated to model: $MODEL"
|
|
275
|
+
fi
|
|
276
|
+
unset ESCALATE_MODEL
|
|
277
|
+
fi
|
|
278
|
+
fi
|
|
279
|
+
|
|
280
|
+
# Session restart context — inject intelligent briefing or fallback to progress
|
|
281
|
+
local restart_section=""
|
|
282
|
+
if [[ "$SESSION_RESTART" == "true" ]]; then
|
|
283
|
+
local briefing_file="${ARTIFACTS_DIR:-${LOG_DIR}}/restart-briefing.md"
|
|
284
|
+
if [[ -f "$briefing_file" ]]; then
|
|
285
|
+
# Inject intelligent briefing from session-restart.sh
|
|
286
|
+
restart_section="## Session Restart Briefing
|
|
287
|
+
$(cat "$briefing_file")
|
|
288
|
+
|
|
289
|
+
You are starting a FRESH session after the previous one exhausted its context.
|
|
290
|
+
Read the briefing above carefully and continue from where the previous session left off.
|
|
291
|
+
Do NOT repeat work already done. Focus on what's failing and what to try next."
|
|
292
|
+
elif [[ -f "$LOG_DIR/progress.md" ]]; then
|
|
293
|
+
# Fallback to basic progress.md
|
|
294
|
+
restart_section="## Previous Session Progress
|
|
295
|
+
$(cat "$LOG_DIR/progress.md")
|
|
296
|
+
|
|
297
|
+
You are starting a FRESH session after the previous one exhausted its iterations.
|
|
298
|
+
Read the progress above and continue from where it left off. Do NOT repeat work already done."
|
|
299
|
+
fi
|
|
300
|
+
fi
|
|
301
|
+
|
|
302
|
+
# Resume-from-checkpoint context — reconstruct Claude context for meaningful resume
|
|
303
|
+
local resume_section=""
|
|
304
|
+
if [[ -n "${RESUMED_FROM_ITERATION:-}" && "${RESUMED_FROM_ITERATION:-0}" -gt 0 ]]; then
|
|
305
|
+
local _test_tail=" (none recorded)"
|
|
306
|
+
[[ -n "${RESUMED_TEST_OUTPUT:-}" ]] && _test_tail="$(echo "$RESUMED_TEST_OUTPUT" | tail -20)"
|
|
307
|
+
resume_section="## RESUMING FROM ITERATION ${RESUMED_FROM_ITERATION}
|
|
308
|
+
|
|
309
|
+
Continue from where you left off. Do NOT repeat work already done.
|
|
310
|
+
|
|
311
|
+
Previous work modified these files:
|
|
312
|
+
${RESUMED_MODIFIED:- (none recorded)}
|
|
313
|
+
|
|
314
|
+
Previous findings/errors from earlier iterations:
|
|
315
|
+
${RESUMED_FINDINGS:- (none recorded)}
|
|
316
|
+
|
|
317
|
+
Last test output (fix any failures, tail):
|
|
318
|
+
${_test_tail}
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
"
|
|
322
|
+
# Clear after first use so we don't keep injecting on every iteration
|
|
323
|
+
RESUMED_FROM_ITERATION=""
|
|
324
|
+
RESUMED_MODIFIED=""
|
|
325
|
+
RESUMED_FINDINGS=""
|
|
326
|
+
RESUMED_TEST_OUTPUT=""
|
|
327
|
+
fi
|
|
328
|
+
|
|
329
|
+
# Build cumulative progress summary showing all iterations' work
|
|
330
|
+
local cumulative_section=""
|
|
331
|
+
if [[ -n "${LOOP_START_COMMIT:-}" ]] && [[ "$ITERATION" -gt 1 ]]; then
|
|
332
|
+
local cum_stat
|
|
333
|
+
cum_stat="$(git -C "$PROJECT_ROOT" diff --stat "${LOOP_START_COMMIT}..HEAD" 2>/dev/null | tail -1 || true)"
|
|
334
|
+
if [[ -n "$cum_stat" ]]; then
|
|
335
|
+
cumulative_section="## Cumulative Progress (all iterations combined)
|
|
336
|
+
${cum_stat}
|
|
337
|
+
"
|
|
338
|
+
fi
|
|
339
|
+
fi
|
|
340
|
+
|
|
341
|
+
# Process reward context injection (from process-reward.sh)
|
|
342
|
+
local reward_section=""
|
|
343
|
+
if type process_reward_inject_context >/dev/null 2>&1; then
|
|
344
|
+
reward_section="$(process_reward_inject_context 2>/dev/null || true)"
|
|
345
|
+
fi
|
|
346
|
+
|
|
347
|
+
# RL optimizer context injection (from rl-optimizer.sh, Phase 7)
|
|
348
|
+
local rl_section=""
|
|
349
|
+
if type rl_compose_prompt_section >/dev/null 2>&1; then
|
|
350
|
+
rl_section="$(rl_compose_prompt_section 2>/dev/null || true)"
|
|
351
|
+
fi
|
|
352
|
+
|
|
353
|
+
# Autoresearch RL Phase 8: policy and reward feedback injection
|
|
354
|
+
local policy_section=""
|
|
355
|
+
if type policy_inject_into_prompt >/dev/null 2>&1; then
|
|
356
|
+
policy_section="$(policy_inject_into_prompt 2>/dev/null || true)"
|
|
357
|
+
fi
|
|
358
|
+
local reward_feedback=""
|
|
359
|
+
if type reward_inject_feedback >/dev/null 2>&1; then
|
|
360
|
+
reward_feedback="$(reward_inject_feedback 2>/dev/null || true)"
|
|
361
|
+
fi
|
|
362
|
+
|
|
363
|
+
# Auto-recovery hint injection (from auto-recovery.sh)
|
|
364
|
+
local recovery_section=""
|
|
365
|
+
if [[ -n "${RECOVERY_HINT:-}" ]]; then
|
|
366
|
+
recovery_section="## Auto-Recovery Guidance
|
|
367
|
+
${RECOVERY_HINT}
|
|
368
|
+
"
|
|
369
|
+
fi
|
|
370
|
+
if [[ -n "${RECOVERY_ESCALATED_MODEL:-}" ]]; then
|
|
371
|
+
MODEL="${RECOVERY_ESCALATED_MODEL}"
|
|
372
|
+
info "Model escalated to: ${MODEL} (auto-recovery)"
|
|
373
|
+
fi
|
|
374
|
+
|
|
375
|
+
cat <<PROMPT
|
|
376
|
+
You are an autonomous coding agent on iteration ${ITERATION}/${MAX_ITERATIONS} of a continuous loop.
|
|
377
|
+
${resume_section}
|
|
378
|
+
${recovery_section}
|
|
379
|
+
## Your Goal
|
|
380
|
+
${GOAL}
|
|
381
|
+
|
|
382
|
+
${cumulative_section}
|
|
383
|
+
## Current Progress
|
|
384
|
+
${recent_log}
|
|
385
|
+
|
|
386
|
+
## Recent Git Activity
|
|
387
|
+
${git_log}
|
|
388
|
+
|
|
389
|
+
## Test Results (Previous Iteration)
|
|
390
|
+
${test_section}
|
|
391
|
+
|
|
392
|
+
${error_summary_section:+$error_summary_section
|
|
393
|
+
}
|
|
394
|
+
${memory_section:+## Memory Context
|
|
395
|
+
$memory_section
|
|
396
|
+
}
|
|
397
|
+
${discovery_section:+## Cross-Pipeline Learnings
|
|
398
|
+
$discovery_section
|
|
399
|
+
}
|
|
400
|
+
${reward_section:+$reward_section
|
|
401
|
+
}
|
|
402
|
+
${rl_section:+$rl_section
|
|
403
|
+
}
|
|
404
|
+
${policy_section:+$policy_section
|
|
405
|
+
}
|
|
406
|
+
${reward_feedback:+$reward_feedback
|
|
407
|
+
}
|
|
408
|
+
${dora_section:+$dora_section
|
|
409
|
+
}
|
|
410
|
+
${intelligence_section:+$intelligence_section
|
|
411
|
+
}
|
|
412
|
+
${restart_section:+$restart_section
|
|
413
|
+
}
|
|
414
|
+
## Instructions
|
|
415
|
+
1. Read the codebase and understand the current state
|
|
416
|
+
2. Identify the highest-priority remaining work toward the goal
|
|
417
|
+
3. Implement ONE meaningful chunk of progress
|
|
418
|
+
4. Run tests if a test command exists: ${TEST_CMD:-"(none)"}
|
|
419
|
+
5. Commit your work with a descriptive message
|
|
420
|
+
6. When the goal is FULLY achieved, output exactly: LOOP_COMPLETE
|
|
421
|
+
|
|
422
|
+
## Context Efficiency
|
|
423
|
+
- Batch independent tool calls in parallel — avoid sequential round-trips
|
|
424
|
+
- Use targeted file reads (offset/limit) instead of reading entire large files
|
|
425
|
+
- Delegate large searches to subagents — only import the summary
|
|
426
|
+
- Filter tool results with grep/jq before reasoning over them
|
|
427
|
+
- Keep working memory lean — summarize completed steps, don't preserve full outputs
|
|
428
|
+
|
|
429
|
+
${audit_section}
|
|
430
|
+
|
|
431
|
+
${audit_feedback_section}
|
|
432
|
+
|
|
433
|
+
${rejection_notice_section}
|
|
434
|
+
|
|
435
|
+
${stuckness_section}
|
|
436
|
+
|
|
437
|
+
## Rules
|
|
438
|
+
- Focus on ONE task per iteration — do it well
|
|
439
|
+
- Always commit with descriptive messages
|
|
440
|
+
- If tests fail, fix them before ending
|
|
441
|
+
- If stuck on the same issue for 2+ iterations, try a different approach
|
|
442
|
+
- Do NOT output LOOP_COMPLETE unless the goal is genuinely achieved
|
|
443
|
+
PROMPT
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
# ─── Alternative Strategy Exploration ─────────────────────────────────────────
|
|
447
|
+
|
|
448
|
+
explore_alternative_strategy() {
|
|
449
|
+
local last_error="${1:-unknown}"
|
|
450
|
+
local iteration="${2:-0}"
|
|
451
|
+
local diagnosis="${3:-}"
|
|
452
|
+
|
|
453
|
+
# Track attempted strategies to avoid repeating them
|
|
454
|
+
local strategy_file="${LOG_DIR:-/tmp}/strategy-attempts.txt"
|
|
455
|
+
local attempted
|
|
456
|
+
attempted=$(cat "$strategy_file" 2>/dev/null || true)
|
|
457
|
+
|
|
458
|
+
local strategy=""
|
|
459
|
+
|
|
460
|
+
# If quality gates are passing but evaluators disagree, suggest focusing on evaluator alignment
|
|
461
|
+
if [[ "${TEST_PASSED:-}" == "true" ]] && [[ "${QUALITY_GATE_PASSED:-}" == "true" || "${AUDIT_RESULT:-}" == "pass" ]]; then
|
|
462
|
+
if ! echo "$attempted" | grep -q "evaluator_alignment"; then
|
|
463
|
+
echo "evaluator_alignment" >> "$strategy_file"
|
|
464
|
+
strategy="## Alternative Strategy: Evaluator Alignment
|
|
465
|
+
The code appears functionally complete (tests pass). Focus on satisfying the remaining
|
|
466
|
+
quality gate evaluators. Check the DoD log and audit log for specific complaints, then
|
|
467
|
+
address those exact points rather than adding new features."
|
|
468
|
+
fi
|
|
469
|
+
fi
|
|
470
|
+
|
|
471
|
+
# If no code changes in last iteration, suggest verifying existing work
|
|
472
|
+
if echo "$last_error" | grep -qi "no code changes" || [[ "$diagnosis" == *"no code"* ]]; then
|
|
473
|
+
if ! echo "$attempted" | grep -q "verify_existing"; then
|
|
474
|
+
echo "verify_existing" >> "$strategy_file"
|
|
475
|
+
strategy="## Alternative Strategy: Verify Existing Work
|
|
476
|
+
Recent iterations made no code changes. The work may already be complete.
|
|
477
|
+
Run the full test suite, verify all features work, and if everything passes,
|
|
478
|
+
commit a verification message and declare LOOP_COMPLETE with evidence."
|
|
479
|
+
fi
|
|
480
|
+
fi
|
|
481
|
+
|
|
482
|
+
# Generic fallback: break the problem down
|
|
483
|
+
if [[ -z "$strategy" ]]; then
|
|
484
|
+
if ! echo "$attempted" | grep -q "decompose"; then
|
|
485
|
+
echo "decompose" >> "$strategy_file"
|
|
486
|
+
strategy="## Alternative Strategy: Decompose
|
|
487
|
+
Break the remaining work into smaller, independent steps. Focus on one specific
|
|
488
|
+
file or function at a time. Read error messages literally — the root cause may
|
|
489
|
+
differ from your assumption."
|
|
490
|
+
fi
|
|
491
|
+
fi
|
|
492
|
+
|
|
493
|
+
echo "$strategy"
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
# ─── Claude Execution ────────────────────────────────────────────────────────
|
|
497
|
+
|
|
498
|
+
build_claude_flags() {
|
|
499
|
+
local flags=()
|
|
500
|
+
flags+=("--model" "$MODEL")
|
|
501
|
+
flags+=("--output-format" "json")
|
|
502
|
+
|
|
503
|
+
if $SKIP_PERMISSIONS; then
|
|
504
|
+
flags+=("--dangerously-skip-permissions")
|
|
505
|
+
fi
|
|
506
|
+
|
|
507
|
+
if [[ -n "$MAX_TURNS" ]]; then
|
|
508
|
+
flags+=("--max-turns" "$MAX_TURNS")
|
|
509
|
+
fi
|
|
510
|
+
|
|
511
|
+
if [[ -n "${EFFORT_LEVEL:-}" ]]; then
|
|
512
|
+
flags+=("--effort" "$EFFORT_LEVEL")
|
|
513
|
+
fi
|
|
514
|
+
|
|
515
|
+
# Only add fallback-model if it differs from primary (Claude CLI rejects same model)
|
|
516
|
+
if [[ -n "${FALLBACK_MODEL:-}" ]] && [[ "${FALLBACK_MODEL}" != "$MODEL" ]]; then
|
|
517
|
+
flags+=("--fallback-model" "$FALLBACK_MODEL")
|
|
518
|
+
fi
|
|
519
|
+
|
|
520
|
+
echo "${flags[*]}"
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
run_claude_iteration() {
|
|
524
|
+
local log_file="$LOG_DIR/iteration-${ITERATION}.log"
|
|
525
|
+
local json_file="$LOG_DIR/iteration-${ITERATION}.json"
|
|
526
|
+
local prompt
|
|
527
|
+
prompt="$(compose_prompt)"
|
|
528
|
+
|
|
529
|
+
# Context budget monitoring and proactive trimming (issue #209)
|
|
530
|
+
local budget_estimate=""
|
|
531
|
+
local budget_status=""
|
|
532
|
+
if type context_budget_estimate >/dev/null 2>&1; then
|
|
533
|
+
budget_estimate=$(context_budget_estimate "$prompt" "${ARTIFACTS_DIR:-./.claude/pipeline-artifacts}" 2>/dev/null || echo "{}")
|
|
534
|
+
if [[ -n "$budget_estimate" ]]; then
|
|
535
|
+
budget_status=$(context_budget_check "$budget_estimate" 2>/dev/null || echo "{}")
|
|
536
|
+
local status_val=$(echo "$budget_status" | jq -r '.status // "unknown"' 2>/dev/null || echo "unknown")
|
|
537
|
+
|
|
538
|
+
# Log budget state
|
|
539
|
+
if type context_budget_log_state >/dev/null 2>&1; then
|
|
540
|
+
context_budget_log_state "$budget_estimate" "$budget_status" "${ARTIFACTS_DIR:-./.claude/pipeline-artifacts}" 2>/dev/null || true
|
|
541
|
+
fi
|
|
542
|
+
|
|
543
|
+
# Warn if approaching limits
|
|
544
|
+
if [[ "$status_val" == "yellow" ]] || [[ "$status_val" == "red" ]] || [[ "$status_val" == "critical" ]]; then
|
|
545
|
+
local msg=$(echo "$budget_status" | jq -r '.message // "context budget alert"' 2>/dev/null || echo "")
|
|
546
|
+
if [[ -n "$msg" ]]; then
|
|
547
|
+
warn "$msg"
|
|
548
|
+
fi
|
|
549
|
+
fi
|
|
550
|
+
fi
|
|
551
|
+
fi
|
|
552
|
+
|
|
553
|
+
local final_prompt
|
|
554
|
+
final_prompt=$(manage_context_window "$prompt")
|
|
555
|
+
|
|
556
|
+
# Apply proactive trimming if budget is tight
|
|
557
|
+
if [[ -n "$budget_status" ]]; then
|
|
558
|
+
local action=$(echo "$budget_status" | jq -r '.action // "continue"' 2>/dev/null || echo "continue")
|
|
559
|
+
if [[ "$action" != "continue" ]] && type context_budget_trim >/dev/null 2>&1; then
|
|
560
|
+
local trim_status=$(echo "$budget_status" | jq -r '.status // "green"' 2>/dev/null || echo "green")
|
|
561
|
+
final_prompt=$(context_budget_trim "$final_prompt" "$trim_status" 200000 2>/dev/null || echo "$final_prompt")
|
|
562
|
+
fi
|
|
563
|
+
fi
|
|
564
|
+
|
|
565
|
+
local raw_prompt_chars=${#prompt}
|
|
566
|
+
local prompt_chars=${#final_prompt}
|
|
567
|
+
local approx_tokens=$((prompt_chars / 4))
|
|
568
|
+
info "Prompt: ~${approx_tokens} tokens (${prompt_chars} chars)"
|
|
569
|
+
|
|
570
|
+
# Audit: save full prompt to disk for traceability
|
|
571
|
+
if type audit_save_prompt >/dev/null 2>&1; then
|
|
572
|
+
audit_save_prompt "$final_prompt" "$ITERATION" || true
|
|
573
|
+
fi
|
|
574
|
+
if type audit_emit >/dev/null 2>&1; then
|
|
575
|
+
audit_emit "loop.prompt" "iteration=$ITERATION" "chars=$prompt_chars" \
|
|
576
|
+
"raw_chars=$raw_prompt_chars" "path=iteration-${ITERATION}.prompt.txt" || true
|
|
577
|
+
fi
|
|
578
|
+
|
|
579
|
+
# Emit context efficiency metrics
|
|
580
|
+
if type emit_event >/dev/null 2>&1; then
|
|
581
|
+
local trim_ratio=0
|
|
582
|
+
local budget_utilization=0
|
|
583
|
+
if [[ "$raw_prompt_chars" -gt 0 ]]; then
|
|
584
|
+
trim_ratio=$(awk -v raw="$raw_prompt_chars" -v trimmed="$prompt_chars" \
|
|
585
|
+
'BEGIN { printf "%.1f", ((raw - trimmed) / raw) * 100 }')
|
|
586
|
+
fi
|
|
587
|
+
if [[ "${CONTEXT_BUDGET_CHARS:-0}" -gt 0 ]]; then
|
|
588
|
+
budget_utilization=$(awk -v used="$prompt_chars" -v budget="${CONTEXT_BUDGET_CHARS}" \
|
|
589
|
+
'BEGIN { printf "%.1f", (used / budget) * 100 }')
|
|
590
|
+
fi
|
|
591
|
+
emit_event "loop.context_efficiency" \
|
|
592
|
+
"iteration=$ITERATION" \
|
|
593
|
+
"raw_prompt_chars=$raw_prompt_chars" \
|
|
594
|
+
"trimmed_prompt_chars=$prompt_chars" \
|
|
595
|
+
"trim_ratio=$trim_ratio" \
|
|
596
|
+
"budget_utilization=$budget_utilization" \
|
|
597
|
+
"budget_chars=${CONTEXT_BUDGET_CHARS:-0}" \
|
|
598
|
+
"job_id=${PIPELINE_JOB_ID:-loop-$$}" 2>/dev/null || true
|
|
599
|
+
fi
|
|
600
|
+
|
|
601
|
+
local flags
|
|
602
|
+
flags="$(build_claude_flags)"
|
|
603
|
+
|
|
604
|
+
local iter_start
|
|
605
|
+
iter_start="$(now_epoch)"
|
|
606
|
+
|
|
607
|
+
echo -e "\n${CYAN}${BOLD}▸${RESET} ${BOLD}Iteration ${ITERATION}/${MAX_ITERATIONS}${RESET} — Starting..."
|
|
608
|
+
|
|
609
|
+
# Run Claude headless (with timeout + PID capture for signal handling)
|
|
610
|
+
# Output goes to .json first, then we extract text into .log for compat
|
|
611
|
+
local exit_code=0
|
|
612
|
+
# shellcheck disable=SC2086
|
|
613
|
+
local err_file="${json_file%.json}.stderr"
|
|
614
|
+
if [[ -n "$TIMEOUT_CMD" ]]; then
|
|
615
|
+
$TIMEOUT_CMD "$CLAUDE_TIMEOUT" claude -p "$final_prompt" $flags > "$json_file" 2>"$err_file" &
|
|
616
|
+
else
|
|
617
|
+
claude -p "$final_prompt" $flags > "$json_file" 2>"$err_file" &
|
|
618
|
+
fi
|
|
619
|
+
CHILD_PID=$!
|
|
620
|
+
wait "$CHILD_PID" 2>/dev/null || exit_code=$?
|
|
621
|
+
CHILD_PID=""
|
|
622
|
+
if [[ "$exit_code" -eq 124 ]]; then
|
|
623
|
+
warn "Claude CLI timed out after ${CLAUDE_TIMEOUT}s"
|
|
624
|
+
fi
|
|
625
|
+
|
|
626
|
+
# Extract text result from JSON into .log for backwards compatibility
|
|
627
|
+
# With --output-format json, stdout is a JSON array; .[-1].result has the text
|
|
628
|
+
_extract_text_from_json "$json_file" "$log_file" "$err_file"
|
|
629
|
+
|
|
630
|
+
local iter_end
|
|
631
|
+
iter_end="$(now_epoch)"
|
|
632
|
+
local iter_duration=$(( iter_end - iter_start ))
|
|
633
|
+
|
|
634
|
+
echo -e " ${GREEN}✓${RESET} Claude session completed ($(format_duration "$iter_duration"), exit $exit_code)"
|
|
635
|
+
|
|
636
|
+
# Accumulate token usage from this iteration's JSON output
|
|
637
|
+
accumulate_loop_tokens "$json_file"
|
|
638
|
+
|
|
639
|
+
# Audit: record response metadata
|
|
640
|
+
if type audit_emit >/dev/null 2>&1; then
|
|
641
|
+
local response_chars=0
|
|
642
|
+
[[ -f "$log_file" ]] && response_chars=$(wc -c < "$log_file" | tr -d ' ')
|
|
643
|
+
audit_emit "loop.response" "iteration=$ITERATION" "chars=$response_chars" \
|
|
644
|
+
"exit_code=$exit_code" "duration_s=$iter_duration" \
|
|
645
|
+
"path=iteration-${ITERATION}.json" || true
|
|
646
|
+
fi
|
|
647
|
+
|
|
648
|
+
# Context budget: record iteration summary for context compression (issue #209)
|
|
649
|
+
if type context_budget_summarize_iteration >/dev/null 2>&1; then
|
|
650
|
+
# Extract test result from log or TEST_PASSED variable
|
|
651
|
+
local test_result="${TEST_OUTPUT:-}"
|
|
652
|
+
[[ -z "$test_result" && -n "${TEST_PASSED:-}" ]] && test_result=$([ "$TEST_PASSED" = true ] && echo "PASSED" || echo "FAILED")
|
|
653
|
+
context_budget_summarize_iteration "$ITERATION" "$log_file" "$test_result" "${ARTIFACTS_DIR:-./.claude/pipeline-artifacts}" 2>/dev/null || true
|
|
654
|
+
fi
|
|
655
|
+
|
|
656
|
+
# Show verbose output if requested
|
|
657
|
+
if $VERBOSE; then
|
|
658
|
+
echo -e " ${DIM}─── Claude Output ───${RESET}"
|
|
659
|
+
sed 's/^/ /' "$log_file" | head -100
|
|
660
|
+
echo -e " ${DIM}─────────────────────${RESET}"
|
|
661
|
+
fi
|
|
662
|
+
|
|
663
|
+
return $exit_code
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
# ─── Iteration Summary Extraction ────────────────────────────────────────────
|
|
667
|
+
|
|
668
|
+
extract_summary() {
|
|
669
|
+
local log_file="$1"
|
|
670
|
+
# Grab last meaningful lines from Claude output, skipping empty lines
|
|
671
|
+
local summary
|
|
672
|
+
summary="$(grep -v '^$' "$log_file" | tail -5 | head -3 2>/dev/null || echo "(no output)")"
|
|
673
|
+
# Truncate long lines
|
|
674
|
+
summary="$(echo "$summary" | cut -c1-120)"
|
|
675
|
+
|
|
676
|
+
# Sanitize: if summary is just a CLI/API error, replace with generic text
|
|
677
|
+
if echo "$summary" | grep -qiE 'Invalid API key|authentication_error|rate_limit|API key expired|ANTHROPIC_API_KEY'; then
|
|
678
|
+
summary="(CLI error — no useful output this iteration)"
|
|
679
|
+
fi
|
|
680
|
+
|
|
681
|
+
echo "$summary"
|
|
682
|
+
}
|