shipwright-cli 3.2.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +4 -4
- 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/dashboard/middleware/auth.ts +134 -0
- package/dashboard/middleware/constants.ts +21 -0
- package/dashboard/public/index.html +2 -6
- package/dashboard/public/styles.css +100 -97
- package/dashboard/routes/auth.ts +38 -0
- package/dashboard/server.ts +66 -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/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 +2 -1
- package/dashboard/src/views/activity.ts +2 -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 +10 -0
- package/scripts/lib/daemon-dispatch.sh +106 -17
- package/scripts/lib/daemon-failure.sh +34 -4
- package/scripts/lib/daemon-patrol.sh +23 -2
- 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 +112 -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 +177 -4
- 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 +100 -2
- 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 +100 -1136
- package/scripts/lib/pipeline-quality-bash-compat.sh +182 -0
- package/scripts/lib/pipeline-quality-checks.sh +17 -715
- 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 +59 -2929
- 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 -2
- package/scripts/sw-adaptive.sh +2 -1
- 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 +5 -1
- package/scripts/sw-cleanup.sh +4 -26
- package/scripts/sw-code-review.sh +10 -4
- package/scripts/sw-connect.sh +2 -1
- package/scripts/sw-context.sh +2 -1
- package/scripts/sw-cost.sh +48 -3
- package/scripts/sw-daemon.sh +66 -9
- package/scripts/sw-dashboard.sh +3 -1
- package/scripts/sw-db.sh +59 -16
- 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 +325 -2
- 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 +4 -3
- package/scripts/sw-e2e-orchestrator.sh +17 -16
- package/scripts/sw-eventbus.sh +7 -1
- 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 +4 -1
- package/scripts/sw-fleet.sh +5 -1
- package/scripts/sw-github-app.sh +16 -3
- 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 +6 -1
- package/scripts/sw-incident.sh +265 -1
- package/scripts/sw-init.sh +18 -2
- package/scripts/sw-instrument.sh +10 -2
- package/scripts/sw-intelligence.sh +42 -6
- 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 +432 -1128
- package/scripts/sw-memory.sh +356 -2
- package/scripts/sw-mission-control.sh +6 -1
- package/scripts/sw-model-router.sh +481 -26
- package/scripts/sw-otel.sh +13 -4
- package/scripts/sw-oversight.sh +14 -5
- package/scripts/sw-patrol-meta.sh +334 -0
- package/scripts/sw-pipeline-composer.sh +5 -1
- package/scripts/sw-pipeline-vitals.sh +2 -1
- package/scripts/sw-pipeline.sh +53 -2664
- package/scripts/sw-pm.sh +12 -5
- package/scripts/sw-pr-lifecycle.sh +2 -1
- package/scripts/sw-predictive.sh +7 -1
- package/scripts/sw-prep.sh +185 -2
- package/scripts/sw-ps.sh +5 -25
- package/scripts/sw-public-dashboard.sh +15 -3
- package/scripts/sw-quality.sh +2 -1
- 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 +10 -3
- package/scripts/sw-security-audit.sh +6 -1
- package/scripts/sw-self-optimize.sh +6 -3
- 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 +4 -1
- package/scripts/sw-stream.sh +7 -1
- package/scripts/sw-swarm.sh +18 -6
- package/scripts/sw-team-stages.sh +13 -6
- package/scripts/sw-templates.sh +5 -29
- package/scripts/sw-testgen.sh +7 -1
- 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 +3 -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 +2 -1
- package/scripts/sw-upgrade.sh +3 -1
- package/scripts/sw-ux.sh +5 -2
- package/scripts/sw-webhook.sh +3 -1
- package/scripts/sw-widgets.sh +3 -1
- 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,471 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Module guard - prevent double-sourcing
|
|
3
|
+
[[ -n "${_AUTO_RECOVERY_LOADED:-}" ]] && return 0
|
|
4
|
+
_AUTO_RECOVERY_LOADED=1
|
|
5
|
+
|
|
6
|
+
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
7
|
+
# ║ shipwright auto-recovery — Autonomous Error Recovery System ║
|
|
8
|
+
# ║ Classify errors → match recovery patterns → apply fix → verify ║
|
|
9
|
+
# ║ Model escalation ladder: same model → escalate → fresh session → human ║
|
|
10
|
+
# ║ Integrates with loop-convergence: called BEFORE circuit breaker abort ║
|
|
11
|
+
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
12
|
+
|
|
13
|
+
# shellcheck disable=SC2034
|
|
14
|
+
VERSION="3.3.0"
|
|
15
|
+
|
|
16
|
+
# ─── Output Helpers ──────────────────────────────────────────────────────────
|
|
17
|
+
[[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
|
|
18
|
+
[[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
|
|
19
|
+
[[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
|
|
20
|
+
[[ "$(type -t error 2>/dev/null)" == "function" ]] || error() { echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m $*" >&2; }
|
|
21
|
+
if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
|
|
22
|
+
now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
|
|
23
|
+
now_epoch() { date +%s; }
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# ─── Configuration ───────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
RECOVERY_MAX_ATTEMPTS="${RECOVERY_MAX_ATTEMPTS:-4}"
|
|
29
|
+
RECOVERY_STATE_FILE="${RECOVERY_STATE_FILE:-.claude/recovery-state.json}"
|
|
30
|
+
RECOVERY_PATTERNS_FILE="${RECOVERY_PATTERNS_FILE:-.claude/recovery-patterns.json}"
|
|
31
|
+
RECOVERY_LOG_FILE="${RECOVERY_LOG_FILE:-.claude/pipeline-artifacts/recovery-log.jsonl}"
|
|
32
|
+
|
|
33
|
+
# Model escalation ladder
|
|
34
|
+
RECOVERY_MODEL_LADDER="${RECOVERY_MODEL_LADDER:-haiku,sonnet,opus}"
|
|
35
|
+
|
|
36
|
+
# ─── Error Classification ───────────────────────────────────────────────────
|
|
37
|
+
# Classify an error into a category for pattern matching.
|
|
38
|
+
# Input: error text (from test output, build log, etc.)
|
|
39
|
+
# Output: error category string
|
|
40
|
+
|
|
41
|
+
recovery_classify_error() {
|
|
42
|
+
local error_text="${1:-}"
|
|
43
|
+
|
|
44
|
+
if [[ -z "$error_text" ]]; then
|
|
45
|
+
echo "unknown"
|
|
46
|
+
return 0
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# Normalize to lowercase for matching
|
|
50
|
+
local lower_text
|
|
51
|
+
lower_text=$(echo "$error_text" | tr '[:upper:]' '[:lower:]')
|
|
52
|
+
|
|
53
|
+
# Classification by pattern (order matters — most specific first)
|
|
54
|
+
if echo "$lower_text" | grep -qE '(syntax error|unexpected token|parse error|unterminated)'; then
|
|
55
|
+
echo "syntax_error"
|
|
56
|
+
elif echo "$lower_text" | grep -qE '(type error|cannot find name|is not assignable|type.*is not)'; then
|
|
57
|
+
echo "type_error"
|
|
58
|
+
elif echo "$lower_text" | grep -qE '(import|require|module not found|cannot find module|no such file)'; then
|
|
59
|
+
echo "import_error"
|
|
60
|
+
elif echo "$lower_text" | grep -qE '(timeout|timed out|deadline exceeded|took too long)'; then
|
|
61
|
+
echo "timeout"
|
|
62
|
+
elif echo "$lower_text" | grep -qE '(out of memory|heap|oom|memory limit|segfault|sigsegv|enomem)'; then
|
|
63
|
+
echo "resource_error"
|
|
64
|
+
elif echo "$lower_text" | grep -qE '(permission denied|eacces|forbidden|unauthorized|401|403)'; then
|
|
65
|
+
echo "permission_error"
|
|
66
|
+
elif echo "$lower_text" | grep -qE '(connection refused|econnrefused|network|dns|enotfound)'; then
|
|
67
|
+
echo "network_error"
|
|
68
|
+
elif echo "$lower_text" | grep -qE '(lock|deadlock|locked|busy|resource busy)'; then
|
|
69
|
+
echo "lock_error"
|
|
70
|
+
elif echo "$lower_text" | grep -qE '(assert|expect|should|to equal|to be|not equal|mismatch)'; then
|
|
71
|
+
echo "test_assertion"
|
|
72
|
+
elif echo "$lower_text" | grep -qE '(build|compile|linker|undefined reference|unresolved)'; then
|
|
73
|
+
echo "build_error"
|
|
74
|
+
elif echo "$lower_text" | grep -qE '(lint|eslint|prettier|format|style)'; then
|
|
75
|
+
echo "lint_error"
|
|
76
|
+
elif echo "$lower_text" | grep -qE '(deprecat|removed|obsolete|no longer supported)'; then
|
|
77
|
+
echo "deprecation_error"
|
|
78
|
+
elif echo "$lower_text" | grep -qE '(null|undefined|nil|none|reference error)'; then
|
|
79
|
+
echo "null_reference"
|
|
80
|
+
else
|
|
81
|
+
echo "unknown"
|
|
82
|
+
fi
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# ─── Recovery Pattern Matching ───────────────────────────────────────────────
|
|
86
|
+
# Match an error category to a recovery strategy.
|
|
87
|
+
|
|
88
|
+
recovery_get_strategy() {
|
|
89
|
+
local error_category="${1:-unknown}"
|
|
90
|
+
|
|
91
|
+
# Check custom patterns file first
|
|
92
|
+
if [[ -f "$RECOVERY_PATTERNS_FILE" ]] && command -v jq >/dev/null 2>&1; then
|
|
93
|
+
local custom_strategy
|
|
94
|
+
custom_strategy=$(jq -r --arg cat "$error_category" \
|
|
95
|
+
'.patterns[$cat].strategy // empty' "$RECOVERY_PATTERNS_FILE" 2>/dev/null || true)
|
|
96
|
+
if [[ -n "$custom_strategy" ]]; then
|
|
97
|
+
echo "$custom_strategy"
|
|
98
|
+
return 0
|
|
99
|
+
fi
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# Built-in strategies
|
|
103
|
+
case "$error_category" in
|
|
104
|
+
syntax_error)
|
|
105
|
+
echo "retry_with_context:Focus on fixing the syntax error. Read the error line carefully and fix the exact syntax issue."
|
|
106
|
+
;;
|
|
107
|
+
type_error)
|
|
108
|
+
echo "retry_with_context:Fix the type error. Add explicit type annotations, check function signatures, and ensure type compatibility."
|
|
109
|
+
;;
|
|
110
|
+
import_error)
|
|
111
|
+
echo "retry_with_context:Fix the import/module error. Check file paths, verify the module exists, and fix any incorrect import statements."
|
|
112
|
+
;;
|
|
113
|
+
timeout)
|
|
114
|
+
echo "adjust_config:Increase timeout values or simplify the operation that is timing out."
|
|
115
|
+
;;
|
|
116
|
+
resource_error)
|
|
117
|
+
echo "escalate_model:Resource exhaustion detected. Try with a model that uses less context or break the task into smaller pieces."
|
|
118
|
+
;;
|
|
119
|
+
permission_error)
|
|
120
|
+
echo "flag_human:Permission error requires human intervention to fix credentials or access."
|
|
121
|
+
;;
|
|
122
|
+
network_error)
|
|
123
|
+
echo "retry_simple:Network error may be transient. Wait briefly and retry."
|
|
124
|
+
;;
|
|
125
|
+
lock_error)
|
|
126
|
+
echo "retry_with_cleanup:Clean up lock files and retry. Check for orphaned processes."
|
|
127
|
+
;;
|
|
128
|
+
test_assertion)
|
|
129
|
+
echo "retry_with_context:Test assertion failed. Review the test expectation and the code behavior. Fix the code to match expected behavior, not the test."
|
|
130
|
+
;;
|
|
131
|
+
build_error)
|
|
132
|
+
echo "retry_with_context:Build error. Check for missing dependencies, incorrect imports, or compilation issues."
|
|
133
|
+
;;
|
|
134
|
+
lint_error)
|
|
135
|
+
echo "retry_with_context:Lint/formatting error. Apply the formatter or fix the lint violations."
|
|
136
|
+
;;
|
|
137
|
+
deprecation_error)
|
|
138
|
+
echo "retry_with_context:Deprecated API usage. Update to the current API version."
|
|
139
|
+
;;
|
|
140
|
+
null_reference)
|
|
141
|
+
echo "retry_with_context:Null/undefined reference. Add null checks, default values, or ensure the variable is initialized."
|
|
142
|
+
;;
|
|
143
|
+
*)
|
|
144
|
+
echo "retry_with_context:Unknown error. Read the error carefully and apply a targeted fix."
|
|
145
|
+
;;
|
|
146
|
+
esac
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
# ─── Recovery State Management ───────────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
_recovery_load_state() {
|
|
152
|
+
if [[ -f "$RECOVERY_STATE_FILE" ]] && command -v jq >/dev/null 2>&1; then
|
|
153
|
+
cat "$RECOVERY_STATE_FILE"
|
|
154
|
+
else
|
|
155
|
+
echo '{"attempts":0,"history":[],"current_model":"","escalation_level":0}'
|
|
156
|
+
fi
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
_recovery_save_state() {
|
|
160
|
+
local state_json="${1:-}"
|
|
161
|
+
if [[ -n "$state_json" ]]; then
|
|
162
|
+
mkdir -p "$(dirname "$RECOVERY_STATE_FILE")"
|
|
163
|
+
local tmp_file
|
|
164
|
+
tmp_file=$(mktemp 2>/dev/null || echo "${RECOVERY_STATE_FILE}.tmp")
|
|
165
|
+
echo "$state_json" > "$tmp_file"
|
|
166
|
+
mv "$tmp_file" "$RECOVERY_STATE_FILE"
|
|
167
|
+
fi
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
_recovery_log_attempt() {
|
|
171
|
+
local category="${1:-}" strategy="${2:-}" result="${3:-}" details="${4:-}"
|
|
172
|
+
mkdir -p "$(dirname "$RECOVERY_LOG_FILE")"
|
|
173
|
+
|
|
174
|
+
local entry
|
|
175
|
+
entry=$(printf '{"timestamp":"%s","category":"%s","strategy":"%s","result":"%s","details":"%s"}' \
|
|
176
|
+
"$(now_iso)" "$category" "$strategy" "$result" \
|
|
177
|
+
"$(echo "$details" | tr '"' "'" | head -c 200)")
|
|
178
|
+
|
|
179
|
+
echo "$entry" >> "$RECOVERY_LOG_FILE"
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
# ─── Core Recovery Function ─────────────────────────────────────────────────
|
|
183
|
+
# Attempt autonomous recovery from an error.
|
|
184
|
+
# Returns 0 if recovery succeeded (caller should continue), 1 if failed.
|
|
185
|
+
#
|
|
186
|
+
# This is the main integration point with loop-convergence.sh.
|
|
187
|
+
# Call this BEFORE the circuit breaker trips.
|
|
188
|
+
|
|
189
|
+
recovery_attempt() {
|
|
190
|
+
local error_text="${1:-}"
|
|
191
|
+
local project_dir="${2:-.}"
|
|
192
|
+
local test_cmd="${3:-}"
|
|
193
|
+
|
|
194
|
+
if [[ -z "$error_text" ]]; then
|
|
195
|
+
warn "No error text provided for recovery"
|
|
196
|
+
return 1
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
# Load state
|
|
200
|
+
local state
|
|
201
|
+
state=$(_recovery_load_state)
|
|
202
|
+
local attempts
|
|
203
|
+
attempts=$(echo "$state" | jq -r '.attempts // 0' 2>/dev/null || echo "0")
|
|
204
|
+
|
|
205
|
+
if [[ "$attempts" -ge "$RECOVERY_MAX_ATTEMPTS" ]]; then
|
|
206
|
+
error "Recovery exhausted: ${attempts}/${RECOVERY_MAX_ATTEMPTS} attempts used"
|
|
207
|
+
_recovery_log_attempt "exhausted" "none" "failed" "Max attempts reached"
|
|
208
|
+
if type emit_event >/dev/null 2>&1; then
|
|
209
|
+
emit_event "recovery_exhausted" "attempts=${attempts}"
|
|
210
|
+
fi
|
|
211
|
+
return 1
|
|
212
|
+
fi
|
|
213
|
+
|
|
214
|
+
# Classify error
|
|
215
|
+
local category
|
|
216
|
+
category=$(recovery_classify_error "$error_text")
|
|
217
|
+
info "Error classified as: ${category}"
|
|
218
|
+
|
|
219
|
+
# Get recovery strategy
|
|
220
|
+
local strategy_raw strategy_type strategy_hint
|
|
221
|
+
strategy_raw=$(recovery_get_strategy "$category")
|
|
222
|
+
strategy_type=$(echo "$strategy_raw" | cut -d: -f1)
|
|
223
|
+
strategy_hint=$(echo "$strategy_raw" | cut -d: -f2-)
|
|
224
|
+
|
|
225
|
+
info "Recovery strategy: ${strategy_type}"
|
|
226
|
+
|
|
227
|
+
# Increment attempt counter
|
|
228
|
+
attempts=$((attempts + 1))
|
|
229
|
+
local escalation_level
|
|
230
|
+
escalation_level=$(echo "$state" | jq -r '.escalation_level // 0' 2>/dev/null || echo "0")
|
|
231
|
+
|
|
232
|
+
# Execute strategy
|
|
233
|
+
local recovery_result="failed"
|
|
234
|
+
|
|
235
|
+
case "$strategy_type" in
|
|
236
|
+
retry_simple)
|
|
237
|
+
info "Retrying (attempt ${attempts}/${RECOVERY_MAX_ATTEMPTS})..."
|
|
238
|
+
recovery_result="retry"
|
|
239
|
+
;;
|
|
240
|
+
|
|
241
|
+
retry_with_context)
|
|
242
|
+
info "Retrying with focused context (attempt ${attempts}/${RECOVERY_MAX_ATTEMPTS})..."
|
|
243
|
+
# The hint will be injected into the next iteration prompt
|
|
244
|
+
RECOVERY_HINT="${strategy_hint}"
|
|
245
|
+
recovery_result="retry_with_hint"
|
|
246
|
+
;;
|
|
247
|
+
|
|
248
|
+
retry_with_cleanup)
|
|
249
|
+
info "Cleaning up and retrying..."
|
|
250
|
+
# Clean common lock/temp files
|
|
251
|
+
find "$project_dir" -name "*.lock" -newer "$project_dir/.git/HEAD" -delete 2>/dev/null || true
|
|
252
|
+
find "$project_dir" -name ".cache" -type d -newer "$project_dir/.git/HEAD" -exec rm -rf {} + 2>/dev/null || true
|
|
253
|
+
RECOVERY_HINT="${strategy_hint}"
|
|
254
|
+
recovery_result="retry_with_hint"
|
|
255
|
+
;;
|
|
256
|
+
|
|
257
|
+
adjust_config)
|
|
258
|
+
info "Adjusting configuration..."
|
|
259
|
+
RECOVERY_HINT="${strategy_hint}"
|
|
260
|
+
recovery_result="retry_with_hint"
|
|
261
|
+
;;
|
|
262
|
+
|
|
263
|
+
escalate_model)
|
|
264
|
+
# Move up the model ladder
|
|
265
|
+
escalation_level=$((escalation_level + 1))
|
|
266
|
+
# Count models in ladder (Bash 3.2 compat — no read -a)
|
|
267
|
+
local model_count=0
|
|
268
|
+
local m
|
|
269
|
+
for m in $(echo "$RECOVERY_MODEL_LADDER" | tr ',' ' '); do
|
|
270
|
+
model_count=$((model_count + 1))
|
|
271
|
+
done
|
|
272
|
+
|
|
273
|
+
if [[ "$escalation_level" -lt "$model_count" ]]; then
|
|
274
|
+
local target_model
|
|
275
|
+
target_model=$(echo "$RECOVERY_MODEL_LADDER" | cut -d',' -f$((escalation_level + 1)))
|
|
276
|
+
info "Escalating model to: ${target_model}"
|
|
277
|
+
RECOVERY_ESCALATED_MODEL="$target_model"
|
|
278
|
+
RECOVERY_HINT="${strategy_hint}"
|
|
279
|
+
recovery_result="model_escalated"
|
|
280
|
+
|
|
281
|
+
if type emit_event >/dev/null 2>&1; then
|
|
282
|
+
emit_event "recovery_escalated" \
|
|
283
|
+
"from_level=${escalation_level}" \
|
|
284
|
+
"to_model=${target_model}" \
|
|
285
|
+
"category=${category}"
|
|
286
|
+
fi
|
|
287
|
+
else
|
|
288
|
+
warn "No higher model available for escalation"
|
|
289
|
+
recovery_result="failed"
|
|
290
|
+
fi
|
|
291
|
+
;;
|
|
292
|
+
|
|
293
|
+
flag_human)
|
|
294
|
+
warn "Error requires human intervention: ${category}"
|
|
295
|
+
RECOVERY_HINT="HUMAN INTERVENTION REQUIRED: ${strategy_hint}"
|
|
296
|
+
recovery_result="needs_human"
|
|
297
|
+
|
|
298
|
+
if type emit_event >/dev/null 2>&1; then
|
|
299
|
+
emit_event "recovery_needs_human" \
|
|
300
|
+
"category=${category}" \
|
|
301
|
+
"hint=${strategy_hint}"
|
|
302
|
+
fi
|
|
303
|
+
return 1
|
|
304
|
+
;;
|
|
305
|
+
|
|
306
|
+
*)
|
|
307
|
+
warn "Unknown recovery strategy: ${strategy_type}"
|
|
308
|
+
recovery_result="failed"
|
|
309
|
+
;;
|
|
310
|
+
esac
|
|
311
|
+
|
|
312
|
+
# Update state
|
|
313
|
+
local history_entry
|
|
314
|
+
history_entry=$(printf '{"attempt":%d,"category":"%s","strategy":"%s","result":"%s","timestamp":"%s"}' \
|
|
315
|
+
"$attempts" "$category" "$strategy_type" "$recovery_result" "$(now_iso)")
|
|
316
|
+
|
|
317
|
+
local new_state
|
|
318
|
+
new_state=$(echo "$state" | jq \
|
|
319
|
+
--argjson attempt "$attempts" \
|
|
320
|
+
--argjson level "$escalation_level" \
|
|
321
|
+
--argjson entry "$history_entry" \
|
|
322
|
+
'.attempts = $attempt | .escalation_level = $level | .history += [$entry]' \
|
|
323
|
+
2>/dev/null || echo "$state")
|
|
324
|
+
|
|
325
|
+
_recovery_save_state "$new_state"
|
|
326
|
+
_recovery_log_attempt "$category" "$strategy_type" "$recovery_result" "$error_text"
|
|
327
|
+
|
|
328
|
+
if type emit_event >/dev/null 2>&1; then
|
|
329
|
+
emit_event "recovery_attempted" \
|
|
330
|
+
"attempt=${attempts}" \
|
|
331
|
+
"category=${category}" \
|
|
332
|
+
"strategy=${strategy_type}" \
|
|
333
|
+
"result=${recovery_result}"
|
|
334
|
+
fi
|
|
335
|
+
|
|
336
|
+
# Return based on result
|
|
337
|
+
case "$recovery_result" in
|
|
338
|
+
retry|retry_with_hint|model_escalated)
|
|
339
|
+
success "Recovery action applied (attempt ${attempts}/${RECOVERY_MAX_ATTEMPTS})"
|
|
340
|
+
return 0
|
|
341
|
+
;;
|
|
342
|
+
*)
|
|
343
|
+
error "Recovery failed (attempt ${attempts}/${RECOVERY_MAX_ATTEMPTS})"
|
|
344
|
+
return 1
|
|
345
|
+
;;
|
|
346
|
+
esac
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
# ─── Reset Recovery State ───────────────────────────────────────────────────
|
|
350
|
+
# Call when tests pass or iteration succeeds to reset the recovery counter.
|
|
351
|
+
|
|
352
|
+
recovery_reset() {
|
|
353
|
+
if [[ -f "$RECOVERY_STATE_FILE" ]]; then
|
|
354
|
+
local state
|
|
355
|
+
state=$(_recovery_load_state)
|
|
356
|
+
local prev_attempts
|
|
357
|
+
prev_attempts=$(echo "$state" | jq -r '.attempts // 0' 2>/dev/null || echo "0")
|
|
358
|
+
|
|
359
|
+
if [[ "$prev_attempts" -gt 0 ]]; then
|
|
360
|
+
success "Recovery succeeded after ${prev_attempts} attempts — resetting counter"
|
|
361
|
+
_recovery_log_attempt "reset" "success" "recovered" "Tests passing, resetting recovery state"
|
|
362
|
+
fi
|
|
363
|
+
fi
|
|
364
|
+
|
|
365
|
+
_recovery_save_state '{"attempts":0,"history":[],"current_model":"","escalation_level":0}'
|
|
366
|
+
RECOVERY_HINT=""
|
|
367
|
+
RECOVERY_ESCALATED_MODEL=""
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
# ─── Recovery Status ────────────────────────────────────────────────────────
|
|
371
|
+
|
|
372
|
+
recovery_status() {
|
|
373
|
+
if [[ -f "$RECOVERY_STATE_FILE" ]] && command -v jq >/dev/null 2>&1; then
|
|
374
|
+
local attempts escalation_level
|
|
375
|
+
attempts=$(jq -r '.attempts // 0' "$RECOVERY_STATE_FILE" 2>/dev/null || echo "0")
|
|
376
|
+
escalation_level=$(jq -r '.escalation_level // 0' "$RECOVERY_STATE_FILE" 2>/dev/null || echo "0")
|
|
377
|
+
echo "recovery_active=true attempts=${attempts}/${RECOVERY_MAX_ATTEMPTS} escalation=${escalation_level}"
|
|
378
|
+
else
|
|
379
|
+
echo "recovery_active=false"
|
|
380
|
+
fi
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
# ─── Recovery Success Rate ──────────────────────────────────────────────────
|
|
384
|
+
# Calculate historical success rate from recovery log.
|
|
385
|
+
|
|
386
|
+
recovery_success_rate() {
|
|
387
|
+
if [[ ! -f "$RECOVERY_LOG_FILE" ]]; then
|
|
388
|
+
echo "0"
|
|
389
|
+
return 0
|
|
390
|
+
fi
|
|
391
|
+
|
|
392
|
+
local total succeeded
|
|
393
|
+
total=$(wc -l < "$RECOVERY_LOG_FILE" 2>/dev/null | tr -d ' ') || total=0
|
|
394
|
+
succeeded=$(grep -c '"result":"recovered"' "$RECOVERY_LOG_FILE" 2>/dev/null) || succeeded=0
|
|
395
|
+
|
|
396
|
+
if [[ "$total" -gt 0 ]]; then
|
|
397
|
+
echo $(( succeeded * 100 / total ))
|
|
398
|
+
else
|
|
399
|
+
echo "0"
|
|
400
|
+
fi
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
# ─── Integration Point: Pre-Circuit-Breaker Hook ────────────────────────────
|
|
404
|
+
# Called by loop-convergence.sh before the circuit breaker trips.
|
|
405
|
+
# If recovery succeeds, the caller should reset the circuit breaker counter.
|
|
406
|
+
|
|
407
|
+
recovery_before_circuit_breaker() {
|
|
408
|
+
local error_log="${1:-}"
|
|
409
|
+
local project_dir="${2:-.}"
|
|
410
|
+
local test_cmd="${3:-}"
|
|
411
|
+
|
|
412
|
+
# Extract the most recent error
|
|
413
|
+
local recent_error=""
|
|
414
|
+
if [[ -n "$error_log" && -f "$error_log" ]]; then
|
|
415
|
+
recent_error=$(tail -1 "$error_log" 2>/dev/null | jq -r '.error // .message // empty' 2>/dev/null || true)
|
|
416
|
+
fi
|
|
417
|
+
|
|
418
|
+
if [[ -z "$recent_error" ]]; then
|
|
419
|
+
# Try to get error from last test output
|
|
420
|
+
local last_log="${LOG_DIR:-/tmp}/iteration-${ITERATION:-0}.log"
|
|
421
|
+
if [[ -f "$last_log" ]]; then
|
|
422
|
+
recent_error=$(grep -iE '(error|fail|exception)' "$last_log" 2>/dev/null | tail -5 | head -c 500 || true)
|
|
423
|
+
fi
|
|
424
|
+
fi
|
|
425
|
+
|
|
426
|
+
if [[ -z "$recent_error" ]]; then
|
|
427
|
+
warn "No error context available for recovery"
|
|
428
|
+
return 1
|
|
429
|
+
fi
|
|
430
|
+
|
|
431
|
+
recovery_attempt "$recent_error" "$project_dir" "$test_cmd"
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
# ─── Custom Patterns Management ─────────────────────────────────────────────
|
|
435
|
+
# Add a custom recovery pattern learned from experience.
|
|
436
|
+
|
|
437
|
+
recovery_add_pattern() {
|
|
438
|
+
local category="${1:-}"
|
|
439
|
+
local strategy="${2:-}"
|
|
440
|
+
local description="${3:-}"
|
|
441
|
+
|
|
442
|
+
if [[ -z "$category" || -z "$strategy" ]]; then
|
|
443
|
+
error "Usage: recovery_add_pattern <category> <strategy> [description]"
|
|
444
|
+
return 1
|
|
445
|
+
fi
|
|
446
|
+
|
|
447
|
+
mkdir -p "$(dirname "$RECOVERY_PATTERNS_FILE")"
|
|
448
|
+
|
|
449
|
+
if [[ ! -f "$RECOVERY_PATTERNS_FILE" ]]; then
|
|
450
|
+
echo '{"patterns":{}}' > "$RECOVERY_PATTERNS_FILE"
|
|
451
|
+
fi
|
|
452
|
+
|
|
453
|
+
local updated
|
|
454
|
+
updated=$(jq --arg cat "$category" --arg strat "$strategy" --arg desc "$description" \
|
|
455
|
+
'.patterns[$cat] = {"strategy": $strat, "description": $desc, "added": (now | todate)}' \
|
|
456
|
+
"$RECOVERY_PATTERNS_FILE" 2>/dev/null)
|
|
457
|
+
|
|
458
|
+
if [[ -n "$updated" ]]; then
|
|
459
|
+
echo "$updated" > "$RECOVERY_PATTERNS_FILE"
|
|
460
|
+
success "Recovery pattern added: ${category} → ${strategy}"
|
|
461
|
+
else
|
|
462
|
+
error "Failed to add recovery pattern"
|
|
463
|
+
return 1
|
|
464
|
+
fi
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
# ─── Exported Variables ──────────────────────────────────────────────────────
|
|
468
|
+
# These are set by recovery_attempt and read by the loop iteration logic.
|
|
469
|
+
|
|
470
|
+
RECOVERY_HINT="" # Injected into next iteration prompt
|
|
471
|
+
RECOVERY_ESCALATED_MODEL="" # Model to switch to (if escalated)
|