bmalph 1.0.0 → 2.2.1
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/LICENSE +21 -0
- package/README.md +398 -217
- package/bmad/bmm/agents/analyst.agent.yaml +43 -36
- package/bmad/bmm/agents/architect.agent.yaml +29 -28
- package/bmad/bmm/agents/dev.agent.yaml +38 -38
- package/bmad/bmm/agents/pm.agent.yaml +44 -46
- package/bmad/bmm/agents/qa.agent.yaml +58 -0
- package/bmad/bmm/agents/quick-flow-solo-dev.agent.yaml +32 -32
- package/bmad/bmm/agents/sm.agent.yaml +37 -36
- package/bmad/bmm/agents/tech-writer/tech-writer-sidecar/documentation-standards.md +223 -223
- package/bmad/bmm/agents/tech-writer/tech-writer.agent.yaml +46 -45
- package/bmad/bmm/agents/ux-designer.agent.yaml +27 -26
- package/bmad/bmm/data/project-context-template.md +26 -26
- package/bmad/bmm/module-help.csv +31 -31
- package/bmad/bmm/module.yaml +50 -44
- package/bmad/bmm/teams/default-party.csv +20 -21
- package/bmad/bmm/teams/team-fullstack.yaml +12 -12
- package/bmad/bmm/workflows/1-analysis/create-product-brief/product-brief.template.md +10 -10
- package/bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-01-init.md +177 -177
- package/bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-01b-continue.md +161 -161
- package/bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-02-vision.md +199 -199
- package/bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-03-users.md +202 -202
- package/bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-04-metrics.md +205 -205
- package/bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-05-scope.md +219 -219
- package/bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-06-complete.md +162 -162
- package/bmad/bmm/workflows/1-analysis/create-product-brief/workflow.md +57 -58
- package/bmad/bmm/workflows/1-analysis/research/domain-steps/step-01-init.md +137 -137
- package/bmad/bmm/workflows/1-analysis/research/domain-steps/step-02-domain-analysis.md +229 -229
- package/bmad/bmm/workflows/1-analysis/research/domain-steps/step-03-competitive-landscape.md +238 -238
- package/bmad/bmm/workflows/1-analysis/research/domain-steps/step-04-regulatory-focus.md +206 -206
- package/bmad/bmm/workflows/1-analysis/research/domain-steps/step-05-technical-trends.md +234 -234
- package/bmad/bmm/workflows/1-analysis/research/domain-steps/step-06-research-synthesis.md +443 -443
- package/bmad/bmm/workflows/1-analysis/research/market-steps/step-01-init.md +182 -182
- package/bmad/bmm/workflows/1-analysis/research/market-steps/step-02-customer-behavior.md +237 -237
- package/bmad/bmm/workflows/1-analysis/research/market-steps/step-03-customer-pain-points.md +249 -249
- package/bmad/bmm/workflows/1-analysis/research/market-steps/step-04-customer-decisions.md +259 -259
- package/bmad/bmm/workflows/1-analysis/research/market-steps/step-05-competitive-analysis.md +177 -177
- package/bmad/bmm/workflows/1-analysis/research/market-steps/step-06-research-completion.md +475 -475
- package/bmad/bmm/workflows/1-analysis/research/research.template.md +29 -29
- package/bmad/bmm/workflows/1-analysis/research/technical-steps/step-01-init.md +137 -137
- package/bmad/bmm/workflows/1-analysis/research/technical-steps/step-02-technical-overview.md +239 -239
- package/bmad/bmm/workflows/1-analysis/research/technical-steps/step-03-integration-patterns.md +248 -248
- package/bmad/bmm/workflows/1-analysis/research/technical-steps/step-04-architectural-patterns.md +202 -202
- package/bmad/bmm/workflows/1-analysis/research/technical-steps/step-05-implementation-research.md +233 -239
- package/bmad/bmm/workflows/1-analysis/research/technical-steps/step-06-research-synthesis.md +486 -486
- package/bmad/bmm/workflows/1-analysis/research/workflow-domain-research.md +54 -0
- package/bmad/bmm/workflows/1-analysis/research/workflow-market-research.md +54 -0
- package/bmad/bmm/workflows/1-analysis/research/workflow-technical-research.md +54 -0
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/data/domain-complexity.csv +14 -12
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/data/prd-purpose.md +197 -197
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/data/project-types.csv +10 -10
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-c/step-01-init.md +191 -191
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-c/step-01b-continue.md +153 -153
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-c/step-02-discovery.md +224 -224
- package/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02b-vision.md +154 -0
- package/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02c-executive-summary.md +170 -0
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-c/step-03-success.md +226 -226
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-c/step-04-journeys.md +213 -213
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-c/step-05-domain.md +207 -207
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-c/step-06-innovation.md +226 -226
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-c/step-07-project-type.md +237 -237
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-c/step-08-scoping.md +228 -228
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-c/step-09-functional.md +231 -231
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-c/step-10-nonfunctional.md +242 -242
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-c/step-11-polish.md +217 -217
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-c/step-12-complete.md +124 -124
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-e/step-e-01-discovery.md +247 -247
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-e/step-e-01b-legacy-conversion.md +208 -208
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-e/step-e-02-review.md +249 -249
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-e/step-e-03-edit.md +253 -253
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-e/step-e-04-complete.md +168 -168
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-v/step-v-01-discovery.md +226 -218
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-v/step-v-02-format-detection.md +191 -191
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-v/step-v-02b-parity-check.md +209 -209
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-v/step-v-03-density-validation.md +174 -174
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-v/step-v-04-brief-coverage-validation.md +214 -214
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-v/step-v-05-measurability-validation.md +228 -228
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-v/step-v-06-traceability-validation.md +217 -217
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-v/step-v-07-implementation-leakage-validation.md +205 -205
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-v/step-v-08-domain-compliance-validation.md +243 -243
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-v/step-v-09-project-type-validation.md +263 -263
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-v/step-v-10-smart-validation.md +209 -209
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-v/step-v-11-holistic-quality-validation.md +264 -264
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-v/step-v-12-completeness-validation.md +242 -242
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/steps-v/step-v-13-report-complete.md +231 -231
- package/bmad/bmm/workflows/2-plan-workflows/{prd → create-prd}/templates/prd-template.md +10 -10
- package/bmad/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md +63 -0
- package/bmad/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md +65 -0
- package/bmad/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md +63 -0
- package/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01-init.md +135 -135
- package/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01b-continue.md +127 -127
- package/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +190 -190
- package/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +216 -216
- package/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +219 -219
- package/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +234 -234
- package/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +252 -252
- package/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +254 -254
- package/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +224 -224
- package/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +224 -224
- package/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +241 -241
- package/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +248 -248
- package/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +237 -237
- package/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +264 -264
- package/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-14-complete.md +171 -171
- package/bmad/bmm/workflows/2-plan-workflows/create-ux-design/ux-design-template.md +13 -13
- package/bmad/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +42 -43
- package/bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-01-document-discovery.md +184 -190
- package/bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-02-prd-analysis.md +172 -178
- package/bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-03-epic-coverage-validation.md +173 -179
- package/bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-04-ux-alignment.md +133 -139
- package/bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-05-epic-quality-review.md +245 -252
- package/bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md +129 -135
- package/bmad/bmm/workflows/3-solutioning/check-implementation-readiness/templates/readiness-report-template.md +4 -4
- package/bmad/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +54 -55
- package/bmad/bmm/workflows/3-solutioning/create-architecture/architecture-decision-template.md +12 -12
- package/bmad/bmm/workflows/3-solutioning/create-architecture/data/domain-complexity.csv +12 -10
- package/bmad/bmm/workflows/3-solutioning/create-architecture/data/project-types.csv +6 -6
- package/bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-01-init.md +153 -153
- package/bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-01b-continue.md +164 -164
- package/bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-02-context.md +224 -224
- package/bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-03-starter.md +331 -331
- package/bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md +318 -318
- package/bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-05-patterns.md +359 -359
- package/bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-06-structure.md +379 -379
- package/bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-07-validation.md +359 -359
- package/bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-08-complete.md +76 -76
- package/bmad/bmm/workflows/3-solutioning/create-architecture/workflow.md +49 -50
- package/bmad/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-01-validate-prerequisites.md +259 -259
- package/bmad/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +233 -233
- package/bmad/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +272 -272
- package/bmad/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +149 -149
- package/bmad/bmm/workflows/3-solutioning/create-epics-and-stories/templates/epics-template.md +57 -57
- package/bmad/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +58 -59
- package/bmad/bmm/workflows/4-implementation/code-review/checklist.md +23 -23
- package/bmad/bmm/workflows/4-implementation/code-review/instructions.xml +226 -226
- package/bmad/bmm/workflows/4-implementation/code-review/workflow.yaml +44 -51
- package/bmad/bmm/workflows/4-implementation/correct-course/checklist.md +288 -288
- package/bmad/bmm/workflows/4-implementation/correct-course/instructions.md +207 -206
- package/bmad/bmm/workflows/4-implementation/correct-course/workflow.yaml +54 -60
- package/bmad/bmm/workflows/4-implementation/create-story/checklist.md +358 -358
- package/bmad/bmm/workflows/4-implementation/create-story/instructions.xml +346 -345
- package/bmad/bmm/workflows/4-implementation/create-story/template.md +49 -49
- package/bmad/bmm/workflows/4-implementation/create-story/workflow.yaml +53 -61
- package/bmad/bmm/workflows/4-implementation/dev-story/checklist.md +80 -80
- package/bmad/bmm/workflows/4-implementation/dev-story/instructions.xml +410 -410
- package/bmad/bmm/workflows/4-implementation/dev-story/workflow.yaml +21 -27
- package/bmad/bmm/workflows/4-implementation/retrospective/instructions.md +1444 -1443
- package/bmad/bmm/workflows/4-implementation/retrospective/workflow.yaml +53 -58
- package/bmad/bmm/workflows/4-implementation/sprint-planning/checklist.md +33 -33
- package/bmad/bmm/workflows/4-implementation/sprint-planning/instructions.md +226 -225
- package/bmad/bmm/workflows/4-implementation/sprint-planning/sprint-status-template.yaml +55 -55
- package/bmad/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +47 -54
- package/bmad/bmm/workflows/4-implementation/sprint-status/instructions.md +230 -229
- package/bmad/bmm/workflows/4-implementation/sprint-status/workflow.yaml +25 -36
- package/bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-01-mode-detection.md +174 -156
- package/bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-02-context-gathering.md +118 -120
- package/bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-03-execute.md +111 -113
- package/bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-04-self-check.md +111 -113
- package/bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-05-adversarial-review.md +104 -106
- package/bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-06-resolve-findings.md +146 -140
- package/bmad/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md +50 -50
- package/bmad/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-01-understand.md +191 -189
- package/bmad/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-02-investigate.md +144 -144
- package/bmad/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-03-generate.md +127 -128
- package/bmad/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md +200 -191
- package/bmad/bmm/workflows/bmad-quick-flow/quick-spec/tech-spec-template.md +74 -74
- package/bmad/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +79 -79
- package/bmad/bmm/workflows/document-project/checklist.md +245 -245
- package/bmad/bmm/workflows/document-project/documentation-requirements.csv +12 -12
- package/bmad/bmm/workflows/document-project/instructions.md +130 -221
- package/bmad/bmm/workflows/document-project/templates/deep-dive-template.md +345 -345
- package/bmad/bmm/workflows/document-project/templates/index-template.md +169 -169
- package/bmad/bmm/workflows/document-project/templates/project-overview-template.md +103 -103
- package/bmad/bmm/workflows/document-project/templates/project-scan-report-schema.json +160 -160
- package/bmad/bmm/workflows/document-project/templates/source-tree-template.md +135 -135
- package/bmad/bmm/workflows/document-project/workflow.yaml +22 -30
- package/bmad/bmm/workflows/document-project/workflows/deep-dive-instructions.md +298 -298
- package/bmad/bmm/workflows/document-project/workflows/deep-dive.yaml +31 -31
- package/bmad/bmm/workflows/document-project/workflows/full-scan-instructions.md +1106 -1106
- package/bmad/bmm/workflows/document-project/workflows/full-scan.yaml +31 -31
- package/bmad/bmm/workflows/generate-project-context/project-context-template.md +21 -0
- package/bmad/bmm/workflows/generate-project-context/steps/step-01-discover.md +184 -0
- package/bmad/bmm/workflows/generate-project-context/steps/step-02-generate.md +318 -0
- package/bmad/bmm/workflows/generate-project-context/steps/step-03-complete.md +278 -0
- package/bmad/bmm/workflows/generate-project-context/workflow.md +49 -0
- package/bmad/bmm/workflows/qa/automate/checklist.md +33 -0
- package/bmad/bmm/workflows/qa/automate/instructions.md +110 -0
- package/bmad/bmm/workflows/qa/automate/workflow.yaml +44 -0
- package/bmad/core/agents/bmad-master.agent.yaml +30 -30
- package/bmad/core/module-help.csv +9 -11
- package/bmad/core/module.yaml +25 -25
- package/bmad/core/tasks/editorial-review-prose.xml +102 -91
- package/bmad/core/tasks/editorial-review-structure.xml +209 -198
- package/bmad/core/tasks/help.md +85 -0
- package/bmad/core/tasks/index-docs.xml +64 -64
- package/bmad/core/tasks/review-adversarial-general.xml +48 -48
- package/bmad/core/tasks/shard-doc.xml +107 -108
- package/bmad/core/tasks/workflow.xml +234 -234
- package/bmad/core/workflows/advanced-elicitation/methods.csv +51 -51
- package/bmad/core/workflows/advanced-elicitation/workflow.xml +116 -116
- package/bmad/core/workflows/brainstorming/brain-methods.csv +61 -61
- package/bmad/core/workflows/brainstorming/steps/step-01-session-setup.md +197 -197
- package/bmad/core/workflows/brainstorming/steps/step-01b-continue.md +122 -122
- package/bmad/core/workflows/brainstorming/steps/step-02a-user-selected.md +225 -225
- package/bmad/core/workflows/brainstorming/steps/step-02b-ai-recommended.md +237 -237
- package/bmad/core/workflows/brainstorming/steps/step-02c-random-selection.md +209 -209
- package/bmad/core/workflows/brainstorming/steps/step-02d-progressive-flow.md +264 -264
- package/bmad/core/workflows/brainstorming/steps/step-03-technique-execution.md +399 -399
- package/bmad/core/workflows/brainstorming/steps/step-04-idea-organization.md +303 -303
- package/bmad/core/workflows/brainstorming/template.md +15 -15
- package/bmad/core/workflows/brainstorming/workflow.md +58 -58
- package/bmad/core/workflows/party-mode/steps/step-01-agent-loading.md +138 -138
- package/bmad/core/workflows/party-mode/steps/step-02-discussion-orchestration.md +187 -187
- package/bmad/core/workflows/party-mode/steps/step-03-graceful-exit.md +168 -157
- package/bmad/core/workflows/party-mode/workflow.md +194 -194
- package/bundled-versions.json +3 -0
- package/dist/cli.js +61 -6
- package/dist/commands/check-updates.d.ts +5 -0
- package/dist/commands/check-updates.js +63 -0
- package/dist/commands/doctor.d.ts +39 -1
- package/dist/commands/doctor.js +348 -79
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +41 -15
- package/dist/commands/status.d.ts +7 -1
- package/dist/commands/status.js +111 -42
- package/dist/commands/upgrade.d.ts +7 -1
- package/dist/commands/upgrade.js +43 -12
- package/dist/installer.d.ts +19 -2
- package/dist/installer.js +305 -66
- package/dist/transition/artifacts.d.ts +2 -0
- package/dist/transition/artifacts.js +46 -0
- package/dist/transition/context.d.ts +19 -0
- package/dist/transition/context.js +261 -0
- package/dist/transition/fix-plan.d.ts +15 -0
- package/dist/transition/fix-plan.js +94 -0
- package/dist/transition/index.d.ts +9 -0
- package/dist/transition/index.js +16 -0
- package/dist/transition/orchestration.d.ts +2 -0
- package/dist/transition/orchestration.js +243 -0
- package/dist/transition/specs-changelog.d.ts +3 -0
- package/dist/transition/specs-changelog.js +75 -0
- package/dist/transition/specs-index.d.ts +22 -0
- package/dist/transition/specs-index.js +157 -0
- package/dist/transition/story-parsing.d.ts +7 -0
- package/dist/transition/story-parsing.js +124 -0
- package/dist/transition/tech-stack.d.ts +3 -0
- package/dist/transition/tech-stack.js +79 -0
- package/dist/transition/types.d.ts +60 -0
- package/dist/transition/types.js +1 -0
- package/dist/utils/config.d.ts +4 -0
- package/dist/utils/config.js +14 -4
- package/dist/utils/constants.d.ts +70 -0
- package/dist/utils/constants.js +97 -0
- package/dist/utils/dryrun.d.ts +7 -0
- package/dist/utils/dryrun.js +48 -0
- package/dist/utils/errors.d.ts +63 -0
- package/dist/utils/errors.js +86 -0
- package/dist/utils/file-system.d.ts +24 -0
- package/dist/utils/file-system.js +99 -0
- package/dist/utils/github.d.ts +83 -0
- package/dist/utils/github.js +230 -0
- package/dist/utils/json.js +3 -3
- package/dist/utils/logger.d.ts +6 -0
- package/dist/utils/logger.js +27 -0
- package/dist/utils/state.d.ts +4 -7
- package/dist/utils/state.js +147 -26
- package/dist/utils/validate.d.ts +40 -0
- package/dist/utils/validate.js +175 -1
- package/package.json +75 -59
- package/ralph/RALPH-REFERENCE.md +412 -0
- package/ralph/lib/circuit_breaker.sh +463 -330
- package/ralph/lib/date_utils.sh +104 -53
- package/ralph/lib/enable_core.sh +815 -0
- package/ralph/lib/response_analyzer.sh +884 -768
- package/ralph/lib/task_sources.sh +577 -0
- package/ralph/lib/timeout_utils.sh +145 -145
- package/ralph/lib/wizard_utils.sh +547 -0
- package/ralph/ralph_import.sh +636 -0
- package/ralph/ralph_loop.sh +1793 -1391
- package/ralph/ralph_monitor.sh +125 -0
- package/ralph/templates/AGENT.md +158 -158
- package/ralph/templates/PROMPT.md +285 -292
- package/ralph/templates/fix_plan.md +27 -27
- package/ralph/templates/ralphrc.template +102 -0
- package/ralph/templates/specs/.gitkeep +1 -1
- package/slash-commands/advanced-elicitation.md +1 -1
- package/slash-commands/adversarial-review.md +1 -1
- package/slash-commands/analyst.md +1 -1
- package/slash-commands/architect.md +1 -1
- package/slash-commands/bmad-help.md +1 -1
- package/slash-commands/bmalph-implement.md +152 -152
- package/slash-commands/brainstorm-project.md +1 -1
- package/slash-commands/brainstorming.md +1 -1
- package/slash-commands/correct-course.md +1 -1
- package/slash-commands/create-architecture.md +1 -1
- package/slash-commands/create-brief.md +1 -1
- package/slash-commands/create-epics-stories.md +1 -1
- package/slash-commands/create-prd.md +1 -1
- package/slash-commands/create-story.md +1 -1
- package/slash-commands/create-ux.md +1 -1
- package/slash-commands/dev.md +1 -1
- package/slash-commands/document-project.md +1 -1
- package/slash-commands/domain-research.md +1 -1
- package/slash-commands/editorial-prose.md +1 -1
- package/slash-commands/editorial-structure.md +1 -1
- package/slash-commands/execute-workflow.md +1 -1
- package/slash-commands/generate-project-context.md +1 -0
- package/slash-commands/implementation-readiness.md +1 -1
- package/slash-commands/index-docs.md +1 -1
- package/slash-commands/market-research.md +1 -1
- package/slash-commands/party-mode.md +1 -1
- package/slash-commands/pm.md +1 -1
- package/slash-commands/qa-automate.md +1 -0
- package/slash-commands/qa.md +1 -0
- package/slash-commands/quick-dev.md +1 -1
- package/slash-commands/quick-flow-solo-dev.md +1 -1
- package/slash-commands/retrospective.md +1 -1
- package/slash-commands/shard-doc.md +1 -1
- package/slash-commands/sm.md +1 -1
- package/slash-commands/sprint-planning.md +1 -1
- package/slash-commands/sprint-status.md +1 -1
- package/slash-commands/tech-spec.md +1 -1
- package/slash-commands/tech-writer.md +1 -0
- package/slash-commands/technical-research.md +1 -1
- package/slash-commands/ux-designer.md +1 -1
- package/slash-commands/validate-architecture.md +1 -1
- package/slash-commands/validate-brief.md +1 -1
- package/slash-commands/validate-epics-stories.md +1 -1
- package/slash-commands/validate-prd.md +1 -1
- package/slash-commands/validate-story.md +1 -1
- package/slash-commands/validate-ux.md +1 -1
- package/bmad/bmm/agents/tea.agent.yaml +0 -63
- package/bmad/bmm/sub-modules/claude-code/config.yaml +0 -4
- package/bmad/bmm/sub-modules/claude-code/injections.yaml +0 -242
- package/bmad/bmm/sub-modules/claude-code/readme.md +0 -87
- package/bmad/bmm/testarch/knowledge/adr-quality-readiness-checklist.md +0 -350
- package/bmad/bmm/testarch/knowledge/api-request.md +0 -442
- package/bmad/bmm/testarch/knowledge/api-testing-patterns.md +0 -843
- package/bmad/bmm/testarch/knowledge/auth-session.md +0 -552
- package/bmad/bmm/testarch/knowledge/burn-in.md +0 -273
- package/bmad/bmm/testarch/knowledge/ci-burn-in.md +0 -675
- package/bmad/bmm/testarch/knowledge/component-tdd.md +0 -486
- package/bmad/bmm/testarch/knowledge/contract-testing.md +0 -957
- package/bmad/bmm/testarch/knowledge/data-factories.md +0 -500
- package/bmad/bmm/testarch/knowledge/email-auth.md +0 -721
- package/bmad/bmm/testarch/knowledge/error-handling.md +0 -725
- package/bmad/bmm/testarch/knowledge/feature-flags.md +0 -750
- package/bmad/bmm/testarch/knowledge/file-utils.md +0 -463
- package/bmad/bmm/testarch/knowledge/fixture-architecture.md +0 -401
- package/bmad/bmm/testarch/knowledge/fixtures-composition.md +0 -382
- package/bmad/bmm/testarch/knowledge/intercept-network-call.md +0 -430
- package/bmad/bmm/testarch/knowledge/log.md +0 -429
- package/bmad/bmm/testarch/knowledge/network-error-monitor.md +0 -405
- package/bmad/bmm/testarch/knowledge/network-first.md +0 -486
- package/bmad/bmm/testarch/knowledge/network-recorder.md +0 -527
- package/bmad/bmm/testarch/knowledge/nfr-criteria.md +0 -670
- package/bmad/bmm/testarch/knowledge/overview.md +0 -286
- package/bmad/bmm/testarch/knowledge/playwright-config.md +0 -730
- package/bmad/bmm/testarch/knowledge/probability-impact.md +0 -601
- package/bmad/bmm/testarch/knowledge/recurse.md +0 -421
- package/bmad/bmm/testarch/knowledge/risk-governance.md +0 -615
- package/bmad/bmm/testarch/knowledge/selective-testing.md +0 -732
- package/bmad/bmm/testarch/knowledge/selector-resilience.md +0 -527
- package/bmad/bmm/testarch/knowledge/test-healing-patterns.md +0 -644
- package/bmad/bmm/testarch/knowledge/test-levels-framework.md +0 -473
- package/bmad/bmm/testarch/knowledge/test-priorities-matrix.md +0 -373
- package/bmad/bmm/testarch/knowledge/test-quality.md +0 -664
- package/bmad/bmm/testarch/knowledge/timing-debugging.md +0 -372
- package/bmad/bmm/testarch/knowledge/visual-debugging.md +0 -524
- package/bmad/bmm/testarch/tea-index.csv +0 -35
- package/bmad/bmm/workflows/1-analysis/research/market-steps/step-02-customer-insights.md +0 -200
- package/bmad/bmm/workflows/1-analysis/research/workflow.md +0 -173
- package/bmad/bmm/workflows/2-plan-workflows/prd/validation-report-prd-workflow.md +0 -433
- package/bmad/bmm/workflows/2-plan-workflows/prd/workflow.md +0 -150
- package/bmad/bmm/workflows/bmad-quick-flow/quick-dev/data/project-levels.yaml +0 -59
- package/bmad/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-library.json +0 -90
- package/bmad/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-templates.yaml +0 -127
- package/bmad/bmm/workflows/excalidraw-diagrams/create-dataflow/checklist.md +0 -39
- package/bmad/bmm/workflows/excalidraw-diagrams/create-dataflow/instructions.md +0 -130
- package/bmad/bmm/workflows/excalidraw-diagrams/create-dataflow/workflow.yaml +0 -27
- package/bmad/bmm/workflows/excalidraw-diagrams/create-diagram/checklist.md +0 -43
- package/bmad/bmm/workflows/excalidraw-diagrams/create-diagram/instructions.md +0 -141
- package/bmad/bmm/workflows/excalidraw-diagrams/create-diagram/workflow.yaml +0 -27
- package/bmad/bmm/workflows/excalidraw-diagrams/create-flowchart/checklist.md +0 -49
- package/bmad/bmm/workflows/excalidraw-diagrams/create-flowchart/instructions.md +0 -241
- package/bmad/bmm/workflows/excalidraw-diagrams/create-flowchart/workflow.yaml +0 -27
- package/bmad/bmm/workflows/excalidraw-diagrams/create-wireframe/checklist.md +0 -38
- package/bmad/bmm/workflows/excalidraw-diagrams/create-wireframe/instructions.md +0 -133
- package/bmad/bmm/workflows/excalidraw-diagrams/create-wireframe/workflow.yaml +0 -27
- package/bmad/bmm/workflows/testarch/atdd/atdd-checklist-template.md +0 -363
- package/bmad/bmm/workflows/testarch/atdd/checklist.md +0 -374
- package/bmad/bmm/workflows/testarch/atdd/instructions.md +0 -806
- package/bmad/bmm/workflows/testarch/atdd/workflow.yaml +0 -47
- package/bmad/bmm/workflows/testarch/automate/checklist.md +0 -582
- package/bmad/bmm/workflows/testarch/automate/instructions.md +0 -1324
- package/bmad/bmm/workflows/testarch/automate/workflow.yaml +0 -54
- package/bmad/bmm/workflows/testarch/ci/checklist.md +0 -247
- package/bmad/bmm/workflows/testarch/ci/github-actions-template.yaml +0 -198
- package/bmad/bmm/workflows/testarch/ci/gitlab-ci-template.yaml +0 -149
- package/bmad/bmm/workflows/testarch/ci/instructions.md +0 -536
- package/bmad/bmm/workflows/testarch/ci/workflow.yaml +0 -47
- package/bmad/bmm/workflows/testarch/framework/checklist.md +0 -320
- package/bmad/bmm/workflows/testarch/framework/instructions.md +0 -481
- package/bmad/bmm/workflows/testarch/framework/workflow.yaml +0 -49
- package/bmad/bmm/workflows/testarch/nfr-assess/checklist.md +0 -407
- package/bmad/bmm/workflows/testarch/nfr-assess/instructions.md +0 -726
- package/bmad/bmm/workflows/testarch/nfr-assess/nfr-report-template.md +0 -461
- package/bmad/bmm/workflows/testarch/nfr-assess/workflow.yaml +0 -49
- package/bmad/bmm/workflows/testarch/test-design/checklist.md +0 -407
- package/bmad/bmm/workflows/testarch/test-design/instructions.md +0 -1158
- package/bmad/bmm/workflows/testarch/test-design/test-design-architecture-template.md +0 -213
- package/bmad/bmm/workflows/testarch/test-design/test-design-qa-template.md +0 -286
- package/bmad/bmm/workflows/testarch/test-design/test-design-template.md +0 -294
- package/bmad/bmm/workflows/testarch/test-design/workflow.yaml +0 -71
- package/bmad/bmm/workflows/testarch/test-review/checklist.md +0 -472
- package/bmad/bmm/workflows/testarch/test-review/instructions.md +0 -628
- package/bmad/bmm/workflows/testarch/test-review/test-review-template.md +0 -390
- package/bmad/bmm/workflows/testarch/test-review/workflow.yaml +0 -48
- package/bmad/bmm/workflows/testarch/trace/checklist.md +0 -642
- package/bmad/bmm/workflows/testarch/trace/instructions.md +0 -1030
- package/bmad/bmm/workflows/testarch/trace/trace-template.md +0 -675
- package/bmad/bmm/workflows/testarch/trace/workflow.yaml +0 -57
- package/bmad/core/resources/excalidraw/README.md +0 -160
- package/bmad/core/resources/excalidraw/excalidraw-helpers.md +0 -127
- package/bmad/core/resources/excalidraw/library-loader.md +0 -50
- package/bmad/core/resources/excalidraw/validate-json-instructions.md +0 -79
- package/bmad/core/tasks/bmad-help.md +0 -62
- package/dist/commands/guide.d.ts +0 -1
- package/dist/commands/guide.js +0 -19
- package/dist/commands/implement.d.ts +0 -1
- package/dist/commands/implement.js +0 -83
- package/dist/commands/plan.d.ts +0 -5
- package/dist/commands/plan.js +0 -44
- package/dist/commands/reset.d.ts +0 -5
- package/dist/commands/reset.js +0 -35
- package/dist/commands/resume.d.ts +0 -1
- package/dist/commands/resume.js +0 -44
- package/dist/commands/start.d.ts +0 -5
- package/dist/commands/start.js +0 -54
- package/dist/transition.d.ts +0 -52
- package/dist/transition.js +0 -656
- package/slash-commands/atdd.md +0 -1
- package/slash-commands/continuous-integration.md +0 -1
- package/slash-commands/create-dataflow.md +0 -1
- package/slash-commands/create-diagram.md +0 -1
- package/slash-commands/create-flowchart.md +0 -1
- package/slash-commands/create-wireframe.md +0 -1
- package/slash-commands/nfr-assess.md +0 -1
- package/slash-commands/tea.md +0 -1
- package/slash-commands/test-automate.md +0 -1
- package/slash-commands/test-design.md +0 -1
- package/slash-commands/test-framework.md +0 -1
- package/slash-commands/test-review.md +0 -1
- package/slash-commands/test-trace.md +0 -1
- package/slash-commands/validate-test-design.md +0 -1
package/ralph/ralph_loop.sh
CHANGED
|
@@ -1,1391 +1,1793 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# Claude Code Ralph Loop with Rate Limiting and Documentation
|
|
4
|
-
# Adaptation of the Ralph technique for Claude Code with usage management
|
|
5
|
-
|
|
6
|
-
set -e # Exit on any error
|
|
7
|
-
|
|
8
|
-
#
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
#
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
#
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
local
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
"
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
"
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
"
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
#
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
log_status "INFO" "
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
#
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
echo "
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
#
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
local
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
#
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
if [[ -
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
local
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
#
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
fi
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
#
|
|
525
|
-
|
|
526
|
-
#
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
#
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
#
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
'
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
#
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
#
|
|
658
|
-
#
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
#
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
#
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
fi
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
#
|
|
780
|
-
local
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
#
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
local
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
"
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
#
|
|
961
|
-
#
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
log_status "
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
fi
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
#
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
#
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
#
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
$
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Claude Code Ralph Loop with Rate Limiting and Documentation
|
|
4
|
+
# Adaptation of the Ralph technique for Claude Code with usage management
|
|
5
|
+
|
|
6
|
+
set -e # Exit on any error
|
|
7
|
+
|
|
8
|
+
# Note: CLAUDE_CODE_ENABLE_DANGEROUS_PERMISSIONS_IN_SANDBOX and IS_SANDBOX
|
|
9
|
+
# environment variables are NOT exported here. Tool restrictions are handled
|
|
10
|
+
# via --allowedTools flag in CLAUDE_CMD_ARGS, which is the proper approach.
|
|
11
|
+
# Exporting sandbox variables without a verified sandbox would be misleading.
|
|
12
|
+
|
|
13
|
+
# Source library components
|
|
14
|
+
SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
|
|
15
|
+
source "$SCRIPT_DIR/lib/date_utils.sh"
|
|
16
|
+
source "$SCRIPT_DIR/lib/timeout_utils.sh"
|
|
17
|
+
source "$SCRIPT_DIR/lib/response_analyzer.sh"
|
|
18
|
+
source "$SCRIPT_DIR/lib/circuit_breaker.sh"
|
|
19
|
+
|
|
20
|
+
# Configuration
|
|
21
|
+
# Ralph-specific files live in .ralph/ subfolder
|
|
22
|
+
RALPH_DIR=".ralph"
|
|
23
|
+
PROMPT_FILE="$RALPH_DIR/PROMPT.md"
|
|
24
|
+
LOG_DIR="$RALPH_DIR/logs"
|
|
25
|
+
DOCS_DIR="$RALPH_DIR/docs/generated"
|
|
26
|
+
STATUS_FILE="$RALPH_DIR/status.json"
|
|
27
|
+
PROGRESS_FILE="$RALPH_DIR/progress.json"
|
|
28
|
+
CLAUDE_CODE_CMD="claude"
|
|
29
|
+
SLEEP_DURATION=3600 # 1 hour in seconds
|
|
30
|
+
LIVE_OUTPUT=false # Show Claude Code output in real-time (streaming)
|
|
31
|
+
LIVE_LOG_FILE="$RALPH_DIR/live.log" # Fixed file for live output monitoring
|
|
32
|
+
CALL_COUNT_FILE="$RALPH_DIR/.call_count"
|
|
33
|
+
TIMESTAMP_FILE="$RALPH_DIR/.last_reset"
|
|
34
|
+
USE_TMUX=false
|
|
35
|
+
|
|
36
|
+
# Save environment variable state BEFORE setting defaults
|
|
37
|
+
# These are used by load_ralphrc() to determine which values came from environment
|
|
38
|
+
_env_MAX_CALLS_PER_HOUR="${MAX_CALLS_PER_HOUR:-}"
|
|
39
|
+
_env_CLAUDE_TIMEOUT_MINUTES="${CLAUDE_TIMEOUT_MINUTES:-}"
|
|
40
|
+
_env_CLAUDE_OUTPUT_FORMAT="${CLAUDE_OUTPUT_FORMAT:-}"
|
|
41
|
+
_env_CLAUDE_ALLOWED_TOOLS="${CLAUDE_ALLOWED_TOOLS:-}"
|
|
42
|
+
_env_CLAUDE_USE_CONTINUE="${CLAUDE_USE_CONTINUE:-}"
|
|
43
|
+
_env_CLAUDE_SESSION_EXPIRY_HOURS="${CLAUDE_SESSION_EXPIRY_HOURS:-}"
|
|
44
|
+
_env_VERBOSE_PROGRESS="${VERBOSE_PROGRESS:-}"
|
|
45
|
+
_env_CB_COOLDOWN_MINUTES="${CB_COOLDOWN_MINUTES:-}"
|
|
46
|
+
_env_CB_AUTO_RESET="${CB_AUTO_RESET:-}"
|
|
47
|
+
|
|
48
|
+
# Now set defaults (only if not already set by environment)
|
|
49
|
+
MAX_CALLS_PER_HOUR="${MAX_CALLS_PER_HOUR:-100}"
|
|
50
|
+
VERBOSE_PROGRESS="${VERBOSE_PROGRESS:-false}"
|
|
51
|
+
CLAUDE_TIMEOUT_MINUTES="${CLAUDE_TIMEOUT_MINUTES:-15}"
|
|
52
|
+
|
|
53
|
+
# Modern Claude CLI configuration (Phase 1.1)
|
|
54
|
+
CLAUDE_OUTPUT_FORMAT="${CLAUDE_OUTPUT_FORMAT:-json}"
|
|
55
|
+
CLAUDE_ALLOWED_TOOLS="${CLAUDE_ALLOWED_TOOLS:-Write,Read,Edit,Bash(git *),Bash(npm *),Bash(pytest)}"
|
|
56
|
+
CLAUDE_USE_CONTINUE="${CLAUDE_USE_CONTINUE:-true}"
|
|
57
|
+
CLAUDE_SESSION_FILE="$RALPH_DIR/.claude_session_id" # Session ID persistence file
|
|
58
|
+
CLAUDE_MIN_VERSION="2.0.76" # Minimum required Claude CLI version
|
|
59
|
+
|
|
60
|
+
# Session management configuration (Phase 1.2)
|
|
61
|
+
# Note: SESSION_EXPIRATION_SECONDS is defined in lib/response_analyzer.sh (86400 = 24 hours)
|
|
62
|
+
RALPH_SESSION_FILE="$RALPH_DIR/.ralph_session" # Ralph-specific session tracking (lifecycle)
|
|
63
|
+
RALPH_SESSION_HISTORY_FILE="$RALPH_DIR/.ralph_session_history" # Session transition history
|
|
64
|
+
# Session expiration: 24 hours default balances project continuity with fresh context
|
|
65
|
+
# Too short = frequent context loss; Too long = stale context causes unpredictable behavior
|
|
66
|
+
CLAUDE_SESSION_EXPIRY_HOURS=${CLAUDE_SESSION_EXPIRY_HOURS:-24}
|
|
67
|
+
|
|
68
|
+
# Valid tool patterns for --allowed-tools validation
|
|
69
|
+
# Tools can be exact matches or pattern matches with wildcards in parentheses
|
|
70
|
+
VALID_TOOL_PATTERNS=(
|
|
71
|
+
"Write"
|
|
72
|
+
"Read"
|
|
73
|
+
"Edit"
|
|
74
|
+
"MultiEdit"
|
|
75
|
+
"Glob"
|
|
76
|
+
"Grep"
|
|
77
|
+
"Task"
|
|
78
|
+
"TodoWrite"
|
|
79
|
+
"WebFetch"
|
|
80
|
+
"WebSearch"
|
|
81
|
+
"Bash"
|
|
82
|
+
"Bash(git *)"
|
|
83
|
+
"Bash(npm *)"
|
|
84
|
+
"Bash(bats *)"
|
|
85
|
+
"Bash(python *)"
|
|
86
|
+
"Bash(node *)"
|
|
87
|
+
"NotebookEdit"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Exit detection configuration
|
|
91
|
+
EXIT_SIGNALS_FILE="$RALPH_DIR/.exit_signals"
|
|
92
|
+
RESPONSE_ANALYSIS_FILE="$RALPH_DIR/.response_analysis"
|
|
93
|
+
MAX_CONSECUTIVE_TEST_LOOPS=3
|
|
94
|
+
MAX_CONSECUTIVE_DONE_SIGNALS=2
|
|
95
|
+
TEST_PERCENTAGE_THRESHOLD=30 # If more than 30% of recent loops are test-only, flag it
|
|
96
|
+
|
|
97
|
+
# .ralphrc configuration file
|
|
98
|
+
RALPHRC_FILE=".ralphrc"
|
|
99
|
+
RALPHRC_LOADED=false
|
|
100
|
+
|
|
101
|
+
# load_ralphrc - Load project-specific configuration from .ralphrc
|
|
102
|
+
#
|
|
103
|
+
# This function sources .ralphrc if it exists, applying project-specific
|
|
104
|
+
# settings. Environment variables take precedence over .ralphrc values.
|
|
105
|
+
#
|
|
106
|
+
# Configuration values that can be overridden:
|
|
107
|
+
# - MAX_CALLS_PER_HOUR
|
|
108
|
+
# - CLAUDE_TIMEOUT_MINUTES
|
|
109
|
+
# - CLAUDE_OUTPUT_FORMAT
|
|
110
|
+
# - ALLOWED_TOOLS (mapped to CLAUDE_ALLOWED_TOOLS)
|
|
111
|
+
# - SESSION_CONTINUITY (mapped to CLAUDE_USE_CONTINUE)
|
|
112
|
+
# - SESSION_EXPIRY_HOURS (mapped to CLAUDE_SESSION_EXPIRY_HOURS)
|
|
113
|
+
# - CB_NO_PROGRESS_THRESHOLD
|
|
114
|
+
# - CB_SAME_ERROR_THRESHOLD
|
|
115
|
+
# - CB_OUTPUT_DECLINE_THRESHOLD
|
|
116
|
+
# - RALPH_VERBOSE
|
|
117
|
+
#
|
|
118
|
+
load_ralphrc() {
|
|
119
|
+
if [[ ! -f "$RALPHRC_FILE" ]]; then
|
|
120
|
+
return 0
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
# Source .ralphrc (this may override default values)
|
|
124
|
+
# shellcheck source=/dev/null
|
|
125
|
+
source "$RALPHRC_FILE"
|
|
126
|
+
|
|
127
|
+
# Map .ralphrc variable names to internal names
|
|
128
|
+
if [[ -n "${ALLOWED_TOOLS:-}" ]]; then
|
|
129
|
+
CLAUDE_ALLOWED_TOOLS="$ALLOWED_TOOLS"
|
|
130
|
+
fi
|
|
131
|
+
if [[ -n "${SESSION_CONTINUITY:-}" ]]; then
|
|
132
|
+
CLAUDE_USE_CONTINUE="$SESSION_CONTINUITY"
|
|
133
|
+
fi
|
|
134
|
+
if [[ -n "${SESSION_EXPIRY_HOURS:-}" ]]; then
|
|
135
|
+
CLAUDE_SESSION_EXPIRY_HOURS="$SESSION_EXPIRY_HOURS"
|
|
136
|
+
fi
|
|
137
|
+
if [[ -n "${RALPH_VERBOSE:-}" ]]; then
|
|
138
|
+
VERBOSE_PROGRESS="$RALPH_VERBOSE"
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
# Restore ONLY values that were explicitly set via environment variables
|
|
142
|
+
# (not script defaults). The _env_* variables were captured BEFORE defaults were set.
|
|
143
|
+
# If _env_* is non-empty, the user explicitly set it in their environment.
|
|
144
|
+
[[ -n "$_env_MAX_CALLS_PER_HOUR" ]] && MAX_CALLS_PER_HOUR="$_env_MAX_CALLS_PER_HOUR"
|
|
145
|
+
[[ -n "$_env_CLAUDE_TIMEOUT_MINUTES" ]] && CLAUDE_TIMEOUT_MINUTES="$_env_CLAUDE_TIMEOUT_MINUTES"
|
|
146
|
+
[[ -n "$_env_CLAUDE_OUTPUT_FORMAT" ]] && CLAUDE_OUTPUT_FORMAT="$_env_CLAUDE_OUTPUT_FORMAT"
|
|
147
|
+
[[ -n "$_env_CLAUDE_ALLOWED_TOOLS" ]] && CLAUDE_ALLOWED_TOOLS="$_env_CLAUDE_ALLOWED_TOOLS"
|
|
148
|
+
[[ -n "$_env_CLAUDE_USE_CONTINUE" ]] && CLAUDE_USE_CONTINUE="$_env_CLAUDE_USE_CONTINUE"
|
|
149
|
+
[[ -n "$_env_CLAUDE_SESSION_EXPIRY_HOURS" ]] && CLAUDE_SESSION_EXPIRY_HOURS="$_env_CLAUDE_SESSION_EXPIRY_HOURS"
|
|
150
|
+
[[ -n "$_env_VERBOSE_PROGRESS" ]] && VERBOSE_PROGRESS="$_env_VERBOSE_PROGRESS"
|
|
151
|
+
[[ -n "$_env_CB_COOLDOWN_MINUTES" ]] && CB_COOLDOWN_MINUTES="$_env_CB_COOLDOWN_MINUTES"
|
|
152
|
+
[[ -n "$_env_CB_AUTO_RESET" ]] && CB_AUTO_RESET="$_env_CB_AUTO_RESET"
|
|
153
|
+
|
|
154
|
+
RALPHRC_LOADED=true
|
|
155
|
+
return 0
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
# Colors for terminal output
|
|
159
|
+
RED='\033[0;31m'
|
|
160
|
+
GREEN='\033[0;32m'
|
|
161
|
+
YELLOW='\033[1;33m'
|
|
162
|
+
BLUE='\033[0;34m'
|
|
163
|
+
PURPLE='\033[0;35m'
|
|
164
|
+
NC='\033[0m' # No Color
|
|
165
|
+
|
|
166
|
+
# Initialize directories
|
|
167
|
+
mkdir -p "$LOG_DIR" "$DOCS_DIR"
|
|
168
|
+
|
|
169
|
+
# Check if tmux is available
|
|
170
|
+
check_tmux_available() {
|
|
171
|
+
if ! command -v tmux &> /dev/null; then
|
|
172
|
+
log_status "ERROR" "tmux is not installed. Please install tmux or run without --monitor flag."
|
|
173
|
+
echo "Install tmux:"
|
|
174
|
+
echo " Ubuntu/Debian: sudo apt-get install tmux"
|
|
175
|
+
echo " macOS: brew install tmux"
|
|
176
|
+
echo " CentOS/RHEL: sudo yum install tmux"
|
|
177
|
+
exit 1
|
|
178
|
+
fi
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# Get the tmux base-index for windows (handles custom tmux configurations)
|
|
182
|
+
# Returns: the base window index (typically 0 or 1)
|
|
183
|
+
get_tmux_base_index() {
|
|
184
|
+
local base_index
|
|
185
|
+
base_index=$(tmux show-options -gv base-index 2>/dev/null)
|
|
186
|
+
# Default to 0 if not set or tmux command fails
|
|
187
|
+
echo "${base_index:-0}"
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
# Setup tmux session with monitor
|
|
191
|
+
setup_tmux_session() {
|
|
192
|
+
local session_name="ralph-$(date +%s)"
|
|
193
|
+
local ralph_home="${RALPH_HOME:-$HOME/.ralph}"
|
|
194
|
+
local project_dir="$(pwd)"
|
|
195
|
+
|
|
196
|
+
# Get the tmux base-index to handle custom configurations (e.g., base-index 1)
|
|
197
|
+
local base_win
|
|
198
|
+
base_win=$(get_tmux_base_index)
|
|
199
|
+
|
|
200
|
+
log_status "INFO" "Setting up tmux session: $session_name"
|
|
201
|
+
|
|
202
|
+
# Initialize live.log file
|
|
203
|
+
echo "=== Ralph Live Output - Waiting for first loop... ===" > "$LIVE_LOG_FILE"
|
|
204
|
+
|
|
205
|
+
# Create new tmux session detached (left pane - Ralph loop)
|
|
206
|
+
tmux new-session -d -s "$session_name" -c "$project_dir"
|
|
207
|
+
|
|
208
|
+
# Split window vertically (right side)
|
|
209
|
+
tmux split-window -h -t "$session_name" -c "$project_dir"
|
|
210
|
+
|
|
211
|
+
# Split right pane horizontally (top: Claude output, bottom: status)
|
|
212
|
+
tmux split-window -v -t "$session_name:${base_win}.1" -c "$project_dir"
|
|
213
|
+
|
|
214
|
+
# Right-top pane (pane 1): Live Claude Code output
|
|
215
|
+
tmux send-keys -t "$session_name:${base_win}.1" "tail -f '$project_dir/$LIVE_LOG_FILE'" Enter
|
|
216
|
+
|
|
217
|
+
# Right-bottom pane (pane 2): Ralph status monitor
|
|
218
|
+
if command -v ralph-monitor &> /dev/null; then
|
|
219
|
+
tmux send-keys -t "$session_name:${base_win}.2" "ralph-monitor" Enter
|
|
220
|
+
else
|
|
221
|
+
tmux send-keys -t "$session_name:${base_win}.2" "'$ralph_home/ralph_monitor.sh'" Enter
|
|
222
|
+
fi
|
|
223
|
+
|
|
224
|
+
# Start ralph loop in the left pane (exclude tmux flag to avoid recursion)
|
|
225
|
+
# Forward all CLI parameters that were set by the user
|
|
226
|
+
local ralph_cmd
|
|
227
|
+
if command -v ralph &> /dev/null; then
|
|
228
|
+
ralph_cmd="ralph"
|
|
229
|
+
else
|
|
230
|
+
ralph_cmd="'$ralph_home/ralph_loop.sh'"
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
# Always use --live mode in tmux for real-time streaming
|
|
234
|
+
ralph_cmd="$ralph_cmd --live"
|
|
235
|
+
|
|
236
|
+
# Forward --calls if non-default
|
|
237
|
+
if [[ "$MAX_CALLS_PER_HOUR" != "100" ]]; then
|
|
238
|
+
ralph_cmd="$ralph_cmd --calls $MAX_CALLS_PER_HOUR"
|
|
239
|
+
fi
|
|
240
|
+
# Forward --prompt if non-default
|
|
241
|
+
if [[ "$PROMPT_FILE" != "$RALPH_DIR/PROMPT.md" ]]; then
|
|
242
|
+
ralph_cmd="$ralph_cmd --prompt '$PROMPT_FILE'"
|
|
243
|
+
fi
|
|
244
|
+
# Forward --output-format if non-default (default is json)
|
|
245
|
+
if [[ "$CLAUDE_OUTPUT_FORMAT" != "json" ]]; then
|
|
246
|
+
ralph_cmd="$ralph_cmd --output-format $CLAUDE_OUTPUT_FORMAT"
|
|
247
|
+
fi
|
|
248
|
+
# Forward --verbose if enabled
|
|
249
|
+
if [[ "$VERBOSE_PROGRESS" == "true" ]]; then
|
|
250
|
+
ralph_cmd="$ralph_cmd --verbose"
|
|
251
|
+
fi
|
|
252
|
+
# Forward --timeout if non-default (default is 15)
|
|
253
|
+
if [[ "$CLAUDE_TIMEOUT_MINUTES" != "15" ]]; then
|
|
254
|
+
ralph_cmd="$ralph_cmd --timeout $CLAUDE_TIMEOUT_MINUTES"
|
|
255
|
+
fi
|
|
256
|
+
# Forward --allowed-tools if non-default
|
|
257
|
+
if [[ "$CLAUDE_ALLOWED_TOOLS" != "Write,Read,Edit,Bash(git *),Bash(npm *),Bash(pytest)" ]]; then
|
|
258
|
+
ralph_cmd="$ralph_cmd --allowed-tools '$CLAUDE_ALLOWED_TOOLS'"
|
|
259
|
+
fi
|
|
260
|
+
# Forward --no-continue if session continuity disabled
|
|
261
|
+
if [[ "$CLAUDE_USE_CONTINUE" == "false" ]]; then
|
|
262
|
+
ralph_cmd="$ralph_cmd --no-continue"
|
|
263
|
+
fi
|
|
264
|
+
# Forward --session-expiry if non-default (default is 24)
|
|
265
|
+
if [[ "$CLAUDE_SESSION_EXPIRY_HOURS" != "24" ]]; then
|
|
266
|
+
ralph_cmd="$ralph_cmd --session-expiry $CLAUDE_SESSION_EXPIRY_HOURS"
|
|
267
|
+
fi
|
|
268
|
+
# Forward --auto-reset-circuit if enabled
|
|
269
|
+
if [[ "$CB_AUTO_RESET" == "true" ]]; then
|
|
270
|
+
ralph_cmd="$ralph_cmd --auto-reset-circuit"
|
|
271
|
+
fi
|
|
272
|
+
|
|
273
|
+
tmux send-keys -t "$session_name:${base_win}.0" "$ralph_cmd" Enter
|
|
274
|
+
|
|
275
|
+
# Focus on left pane (main ralph loop)
|
|
276
|
+
tmux select-pane -t "$session_name:${base_win}.0"
|
|
277
|
+
|
|
278
|
+
# Set pane titles (requires tmux 2.6+)
|
|
279
|
+
tmux select-pane -t "$session_name:${base_win}.0" -T "Ralph Loop"
|
|
280
|
+
tmux select-pane -t "$session_name:${base_win}.1" -T "Claude Output"
|
|
281
|
+
tmux select-pane -t "$session_name:${base_win}.2" -T "Status"
|
|
282
|
+
|
|
283
|
+
# Set window title
|
|
284
|
+
tmux rename-window -t "$session_name:${base_win}" "Ralph: Loop | Output | Status"
|
|
285
|
+
|
|
286
|
+
log_status "SUCCESS" "Tmux session created with 3 panes:"
|
|
287
|
+
log_status "INFO" " Left: Ralph loop"
|
|
288
|
+
log_status "INFO" " Right-top: Claude Code live output"
|
|
289
|
+
log_status "INFO" " Right-bottom: Status monitor"
|
|
290
|
+
log_status "INFO" ""
|
|
291
|
+
log_status "INFO" "Use Ctrl+B then D to detach from session"
|
|
292
|
+
log_status "INFO" "Use 'tmux attach -t $session_name' to reattach"
|
|
293
|
+
|
|
294
|
+
# Attach to session (this will block until session ends)
|
|
295
|
+
tmux attach-session -t "$session_name"
|
|
296
|
+
|
|
297
|
+
exit 0
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
# Initialize call tracking
|
|
301
|
+
init_call_tracking() {
|
|
302
|
+
# Debug logging removed for cleaner output
|
|
303
|
+
local current_hour=$(date +%Y%m%d%H)
|
|
304
|
+
local last_reset_hour=""
|
|
305
|
+
|
|
306
|
+
if [[ -f "$TIMESTAMP_FILE" ]]; then
|
|
307
|
+
last_reset_hour=$(cat "$TIMESTAMP_FILE")
|
|
308
|
+
fi
|
|
309
|
+
|
|
310
|
+
# Reset counter if it's a new hour
|
|
311
|
+
if [[ "$current_hour" != "$last_reset_hour" ]]; then
|
|
312
|
+
echo "0" > "$CALL_COUNT_FILE"
|
|
313
|
+
echo "$current_hour" > "$TIMESTAMP_FILE"
|
|
314
|
+
log_status "INFO" "Call counter reset for new hour: $current_hour"
|
|
315
|
+
fi
|
|
316
|
+
|
|
317
|
+
# Initialize exit signals tracking if it doesn't exist
|
|
318
|
+
if [[ ! -f "$EXIT_SIGNALS_FILE" ]]; then
|
|
319
|
+
echo '{"test_only_loops": [], "done_signals": [], "completion_indicators": []}' > "$EXIT_SIGNALS_FILE"
|
|
320
|
+
fi
|
|
321
|
+
|
|
322
|
+
# Initialize circuit breaker
|
|
323
|
+
init_circuit_breaker
|
|
324
|
+
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
# Log function with timestamps and colors
|
|
328
|
+
log_status() {
|
|
329
|
+
local level=$1
|
|
330
|
+
local message=$2
|
|
331
|
+
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
332
|
+
local color=""
|
|
333
|
+
|
|
334
|
+
case $level in
|
|
335
|
+
"INFO") color=$BLUE ;;
|
|
336
|
+
"WARN") color=$YELLOW ;;
|
|
337
|
+
"ERROR") color=$RED ;;
|
|
338
|
+
"SUCCESS") color=$GREEN ;;
|
|
339
|
+
"LOOP") color=$PURPLE ;;
|
|
340
|
+
esac
|
|
341
|
+
|
|
342
|
+
# Write to stderr so log messages don't interfere with function return values
|
|
343
|
+
echo -e "${color}[$timestamp] [$level] $message${NC}" >&2
|
|
344
|
+
echo "[$timestamp] [$level] $message" >> "$LOG_DIR/ralph.log"
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
# Update status JSON for external monitoring
|
|
348
|
+
update_status() {
|
|
349
|
+
local loop_count=$1
|
|
350
|
+
local calls_made=$2
|
|
351
|
+
local last_action=$3
|
|
352
|
+
local status=$4
|
|
353
|
+
local exit_reason=${5:-""}
|
|
354
|
+
|
|
355
|
+
cat > "$STATUS_FILE" << STATUSEOF
|
|
356
|
+
{
|
|
357
|
+
"timestamp": "$(get_iso_timestamp)",
|
|
358
|
+
"loop_count": $loop_count,
|
|
359
|
+
"calls_made_this_hour": $calls_made,
|
|
360
|
+
"max_calls_per_hour": $MAX_CALLS_PER_HOUR,
|
|
361
|
+
"last_action": "$last_action",
|
|
362
|
+
"status": "$status",
|
|
363
|
+
"exit_reason": "$exit_reason",
|
|
364
|
+
"next_reset": "$(get_next_hour_time)"
|
|
365
|
+
}
|
|
366
|
+
STATUSEOF
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
# Check if we can make another call
|
|
370
|
+
can_make_call() {
|
|
371
|
+
local calls_made=0
|
|
372
|
+
if [[ -f "$CALL_COUNT_FILE" ]]; then
|
|
373
|
+
calls_made=$(cat "$CALL_COUNT_FILE")
|
|
374
|
+
fi
|
|
375
|
+
|
|
376
|
+
if [[ $calls_made -ge $MAX_CALLS_PER_HOUR ]]; then
|
|
377
|
+
return 1 # Cannot make call
|
|
378
|
+
else
|
|
379
|
+
return 0 # Can make call
|
|
380
|
+
fi
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
# Increment call counter
|
|
384
|
+
increment_call_counter() {
|
|
385
|
+
local calls_made=0
|
|
386
|
+
if [[ -f "$CALL_COUNT_FILE" ]]; then
|
|
387
|
+
calls_made=$(cat "$CALL_COUNT_FILE")
|
|
388
|
+
fi
|
|
389
|
+
|
|
390
|
+
((calls_made++))
|
|
391
|
+
echo "$calls_made" > "$CALL_COUNT_FILE"
|
|
392
|
+
echo "$calls_made"
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
# Wait for rate limit reset with countdown
|
|
396
|
+
wait_for_reset() {
|
|
397
|
+
local calls_made=$(cat "$CALL_COUNT_FILE" 2>/dev/null || echo "0")
|
|
398
|
+
log_status "WARN" "Rate limit reached ($calls_made/$MAX_CALLS_PER_HOUR). Waiting for reset..."
|
|
399
|
+
|
|
400
|
+
# Calculate time until next hour
|
|
401
|
+
local current_minute=$(date +%M)
|
|
402
|
+
local current_second=$(date +%S)
|
|
403
|
+
local wait_time=$(((60 - current_minute - 1) * 60 + (60 - current_second)))
|
|
404
|
+
|
|
405
|
+
log_status "INFO" "Sleeping for $wait_time seconds until next hour..."
|
|
406
|
+
|
|
407
|
+
# Countdown display
|
|
408
|
+
while [[ $wait_time -gt 0 ]]; do
|
|
409
|
+
local hours=$((wait_time / 3600))
|
|
410
|
+
local minutes=$(((wait_time % 3600) / 60))
|
|
411
|
+
local seconds=$((wait_time % 60))
|
|
412
|
+
|
|
413
|
+
printf "\r${YELLOW}Time until reset: %02d:%02d:%02d${NC}" $hours $minutes $seconds
|
|
414
|
+
sleep 1
|
|
415
|
+
((wait_time--))
|
|
416
|
+
done
|
|
417
|
+
printf "\n"
|
|
418
|
+
|
|
419
|
+
# Reset counter
|
|
420
|
+
echo "0" > "$CALL_COUNT_FILE"
|
|
421
|
+
echo "$(date +%Y%m%d%H)" > "$TIMESTAMP_FILE"
|
|
422
|
+
log_status "SUCCESS" "Rate limit reset! Ready for new calls."
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
# Check if we should gracefully exit
|
|
426
|
+
should_exit_gracefully() {
|
|
427
|
+
|
|
428
|
+
if [[ ! -f "$EXIT_SIGNALS_FILE" ]]; then
|
|
429
|
+
return 1 # Don't exit, file doesn't exist
|
|
430
|
+
fi
|
|
431
|
+
|
|
432
|
+
local signals=$(cat "$EXIT_SIGNALS_FILE")
|
|
433
|
+
|
|
434
|
+
# Count recent signals (last 5 loops) - with error handling
|
|
435
|
+
local recent_test_loops
|
|
436
|
+
local recent_done_signals
|
|
437
|
+
local recent_completion_indicators
|
|
438
|
+
|
|
439
|
+
recent_test_loops=$(echo "$signals" | jq '.test_only_loops | length' 2>/dev/null || echo "0")
|
|
440
|
+
recent_done_signals=$(echo "$signals" | jq '.done_signals | length' 2>/dev/null || echo "0")
|
|
441
|
+
recent_completion_indicators=$(echo "$signals" | jq '.completion_indicators | length' 2>/dev/null || echo "0")
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
# Check for exit conditions
|
|
445
|
+
|
|
446
|
+
# 0. Permission denials (highest priority - Issue #101)
|
|
447
|
+
# When Claude Code is denied permission to run commands, halt immediately
|
|
448
|
+
# to allow user to update .ralphrc ALLOWED_TOOLS configuration
|
|
449
|
+
if [[ -f "$RESPONSE_ANALYSIS_FILE" ]]; then
|
|
450
|
+
local has_permission_denials=$(jq -r '.analysis.has_permission_denials // false' "$RESPONSE_ANALYSIS_FILE" 2>/dev/null || echo "false")
|
|
451
|
+
if [[ "$has_permission_denials" == "true" ]]; then
|
|
452
|
+
local denied_count=$(jq -r '.analysis.permission_denial_count // 0' "$RESPONSE_ANALYSIS_FILE" 2>/dev/null || echo "0")
|
|
453
|
+
local denied_cmds=$(jq -r '.analysis.denied_commands | join(", ")' "$RESPONSE_ANALYSIS_FILE" 2>/dev/null || echo "unknown")
|
|
454
|
+
log_status "WARN" "🚫 Permission denied for $denied_count command(s): $denied_cmds"
|
|
455
|
+
log_status "WARN" "Update ALLOWED_TOOLS in .ralphrc to include the required tools"
|
|
456
|
+
echo "permission_denied"
|
|
457
|
+
return 0
|
|
458
|
+
fi
|
|
459
|
+
fi
|
|
460
|
+
|
|
461
|
+
# 1. Too many consecutive test-only loops
|
|
462
|
+
if [[ $recent_test_loops -ge $MAX_CONSECUTIVE_TEST_LOOPS ]]; then
|
|
463
|
+
log_status "WARN" "Exit condition: Too many test-focused loops ($recent_test_loops >= $MAX_CONSECUTIVE_TEST_LOOPS)"
|
|
464
|
+
echo "test_saturation"
|
|
465
|
+
return 0
|
|
466
|
+
fi
|
|
467
|
+
|
|
468
|
+
# 2. Multiple "done" signals
|
|
469
|
+
if [[ $recent_done_signals -ge $MAX_CONSECUTIVE_DONE_SIGNALS ]]; then
|
|
470
|
+
log_status "WARN" "Exit condition: Multiple completion signals ($recent_done_signals >= $MAX_CONSECUTIVE_DONE_SIGNALS)"
|
|
471
|
+
echo "completion_signals"
|
|
472
|
+
return 0
|
|
473
|
+
fi
|
|
474
|
+
|
|
475
|
+
# 3. Safety circuit breaker - force exit after 5 consecutive EXIT_SIGNAL=true responses
|
|
476
|
+
# Note: completion_indicators only accumulates when Claude explicitly sets EXIT_SIGNAL=true
|
|
477
|
+
# (not based on confidence score). This safety breaker catches cases where Claude signals
|
|
478
|
+
# completion 5+ times but the normal exit path (completion_indicators >= 2 + EXIT_SIGNAL=true)
|
|
479
|
+
# didn't trigger for some reason. Threshold of 5 prevents API waste while being higher than
|
|
480
|
+
# the normal threshold (2) to avoid false positives.
|
|
481
|
+
if [[ $recent_completion_indicators -ge 5 ]]; then
|
|
482
|
+
log_status "WARN" "🚨 SAFETY CIRCUIT BREAKER: Force exit after 5 consecutive EXIT_SIGNAL=true responses ($recent_completion_indicators)" >&2
|
|
483
|
+
echo "safety_circuit_breaker"
|
|
484
|
+
return 0
|
|
485
|
+
fi
|
|
486
|
+
|
|
487
|
+
# 4. Strong completion indicators (only if Claude's EXIT_SIGNAL is true)
|
|
488
|
+
# This prevents premature exits when heuristics detect completion patterns
|
|
489
|
+
# but Claude explicitly indicates work is still in progress via RALPH_STATUS block.
|
|
490
|
+
# The exit_signal in .response_analysis represents Claude's explicit intent.
|
|
491
|
+
local claude_exit_signal="false"
|
|
492
|
+
if [[ -f "$RESPONSE_ANALYSIS_FILE" ]]; then
|
|
493
|
+
claude_exit_signal=$(jq -r '.analysis.exit_signal // false' "$RESPONSE_ANALYSIS_FILE" 2>/dev/null || echo "false")
|
|
494
|
+
fi
|
|
495
|
+
|
|
496
|
+
if [[ $recent_completion_indicators -ge 2 ]] && [[ "$claude_exit_signal" == "true" ]]; then
|
|
497
|
+
log_status "WARN" "Exit condition: Strong completion indicators ($recent_completion_indicators) with EXIT_SIGNAL=true" >&2
|
|
498
|
+
echo "project_complete"
|
|
499
|
+
return 0
|
|
500
|
+
fi
|
|
501
|
+
|
|
502
|
+
# 5. Check @fix_plan.md for completion
|
|
503
|
+
# Fix #144: Only match valid markdown checkboxes, not date entries like [2026-01-29]
|
|
504
|
+
# Valid patterns: "- [ ]" (uncompleted) and "- [x]" or "- [X]" (completed)
|
|
505
|
+
if [[ -f "$RALPH_DIR/@fix_plan.md" ]]; then
|
|
506
|
+
local uncompleted_items=$(grep -cE "^[[:space:]]*- \[ \]" "$RALPH_DIR/@fix_plan.md" 2>/dev/null || true)
|
|
507
|
+
[[ -z "$uncompleted_items" ]] && uncompleted_items=0
|
|
508
|
+
local completed_items=$(grep -cE "^[[:space:]]*- \[[xX]\]" "$RALPH_DIR/@fix_plan.md" 2>/dev/null || true)
|
|
509
|
+
[[ -z "$completed_items" ]] && completed_items=0
|
|
510
|
+
local total_items=$((uncompleted_items + completed_items))
|
|
511
|
+
|
|
512
|
+
if [[ $total_items -gt 0 ]] && [[ $completed_items -eq $total_items ]]; then
|
|
513
|
+
log_status "WARN" "Exit condition: All @fix_plan.md items completed ($completed_items/$total_items)" >&2
|
|
514
|
+
echo "plan_complete"
|
|
515
|
+
return 0
|
|
516
|
+
fi
|
|
517
|
+
fi
|
|
518
|
+
|
|
519
|
+
echo "" # Return empty string instead of using return code
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
# =============================================================================
|
|
523
|
+
# MODERN CLI HELPER FUNCTIONS (Phase 1.1)
|
|
524
|
+
# =============================================================================
|
|
525
|
+
|
|
526
|
+
# Check Claude CLI version for compatibility with modern flags
|
|
527
|
+
check_claude_version() {
|
|
528
|
+
local version=$($CLAUDE_CODE_CMD --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
|
|
529
|
+
|
|
530
|
+
if [[ -z "$version" ]]; then
|
|
531
|
+
log_status "WARN" "Cannot detect Claude CLI version, assuming compatible"
|
|
532
|
+
return 0
|
|
533
|
+
fi
|
|
534
|
+
|
|
535
|
+
# Compare versions (simplified semver comparison)
|
|
536
|
+
local required="$CLAUDE_MIN_VERSION"
|
|
537
|
+
|
|
538
|
+
# Convert to comparable integers (major * 10000 + minor * 100 + patch)
|
|
539
|
+
local ver_parts=(${version//./ })
|
|
540
|
+
local req_parts=(${required//./ })
|
|
541
|
+
|
|
542
|
+
local ver_num=$((${ver_parts[0]:-0} * 10000 + ${ver_parts[1]:-0} * 100 + ${ver_parts[2]:-0}))
|
|
543
|
+
local req_num=$((${req_parts[0]:-0} * 10000 + ${req_parts[1]:-0} * 100 + ${req_parts[2]:-0}))
|
|
544
|
+
|
|
545
|
+
if [[ $ver_num -lt $req_num ]]; then
|
|
546
|
+
log_status "WARN" "Claude CLI version $version < $required. Some modern features may not work."
|
|
547
|
+
log_status "WARN" "Consider upgrading: npm update -g @anthropic-ai/claude-code"
|
|
548
|
+
return 1
|
|
549
|
+
fi
|
|
550
|
+
|
|
551
|
+
log_status "INFO" "Claude CLI version $version (>= $required) - modern features enabled"
|
|
552
|
+
return 0
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
# Validate allowed tools against whitelist
|
|
556
|
+
# Returns 0 if valid, 1 if invalid with error message
|
|
557
|
+
validate_allowed_tools() {
|
|
558
|
+
local tools_input=$1
|
|
559
|
+
|
|
560
|
+
if [[ -z "$tools_input" ]]; then
|
|
561
|
+
return 0 # Empty is valid (uses defaults)
|
|
562
|
+
fi
|
|
563
|
+
|
|
564
|
+
# Split by comma
|
|
565
|
+
local IFS=','
|
|
566
|
+
read -ra tools <<< "$tools_input"
|
|
567
|
+
|
|
568
|
+
for tool in "${tools[@]}"; do
|
|
569
|
+
# Trim whitespace
|
|
570
|
+
tool=$(echo "$tool" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
571
|
+
|
|
572
|
+
if [[ -z "$tool" ]]; then
|
|
573
|
+
continue
|
|
574
|
+
fi
|
|
575
|
+
|
|
576
|
+
local valid=false
|
|
577
|
+
|
|
578
|
+
# Check against valid patterns
|
|
579
|
+
for pattern in "${VALID_TOOL_PATTERNS[@]}"; do
|
|
580
|
+
if [[ "$tool" == "$pattern" ]]; then
|
|
581
|
+
valid=true
|
|
582
|
+
break
|
|
583
|
+
fi
|
|
584
|
+
|
|
585
|
+
# Check for Bash(*) pattern - any Bash with parentheses is allowed
|
|
586
|
+
if [[ "$tool" =~ ^Bash\(.+\)$ ]]; then
|
|
587
|
+
valid=true
|
|
588
|
+
break
|
|
589
|
+
fi
|
|
590
|
+
done
|
|
591
|
+
|
|
592
|
+
if [[ "$valid" == "false" ]]; then
|
|
593
|
+
echo "Error: Invalid tool in --allowed-tools: '$tool'"
|
|
594
|
+
echo "Valid tools: ${VALID_TOOL_PATTERNS[*]}"
|
|
595
|
+
echo "Note: Bash(...) patterns with any content are allowed (e.g., 'Bash(git *)')"
|
|
596
|
+
return 1
|
|
597
|
+
fi
|
|
598
|
+
done
|
|
599
|
+
|
|
600
|
+
return 0
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
# Build loop context for Claude Code session
|
|
604
|
+
# Provides loop-specific context via --append-system-prompt
|
|
605
|
+
build_loop_context() {
|
|
606
|
+
local loop_count=$1
|
|
607
|
+
local context=""
|
|
608
|
+
|
|
609
|
+
# Add loop number
|
|
610
|
+
context="Loop #${loop_count}. "
|
|
611
|
+
|
|
612
|
+
# Extract incomplete tasks from @fix_plan.md
|
|
613
|
+
# Bug #3 Fix: Support indented markdown checkboxes with [[:space:]]* pattern
|
|
614
|
+
if [[ -f "$RALPH_DIR/@fix_plan.md" ]]; then
|
|
615
|
+
local incomplete_tasks=$(grep -cE "^[[:space:]]*- \[ \]" "$RALPH_DIR/@fix_plan.md" 2>/dev/null || true)
|
|
616
|
+
[[ -z "$incomplete_tasks" ]] && incomplete_tasks=0
|
|
617
|
+
context+="Remaining tasks: ${incomplete_tasks}. "
|
|
618
|
+
fi
|
|
619
|
+
|
|
620
|
+
# Add circuit breaker state
|
|
621
|
+
if [[ -f "$RALPH_DIR/.circuit_breaker_state" ]]; then
|
|
622
|
+
local cb_state=$(jq -r '.state // "UNKNOWN"' "$RALPH_DIR/.circuit_breaker_state" 2>/dev/null)
|
|
623
|
+
if [[ "$cb_state" != "CLOSED" && "$cb_state" != "null" && -n "$cb_state" ]]; then
|
|
624
|
+
context+="Circuit breaker: ${cb_state}. "
|
|
625
|
+
fi
|
|
626
|
+
fi
|
|
627
|
+
|
|
628
|
+
# Add previous loop summary (truncated)
|
|
629
|
+
if [[ -f "$RESPONSE_ANALYSIS_FILE" ]]; then
|
|
630
|
+
local prev_summary=$(jq -r '.analysis.work_summary // ""' "$RESPONSE_ANALYSIS_FILE" 2>/dev/null | head -c 200)
|
|
631
|
+
if [[ -n "$prev_summary" && "$prev_summary" != "null" ]]; then
|
|
632
|
+
context+="Previous: ${prev_summary}"
|
|
633
|
+
fi
|
|
634
|
+
fi
|
|
635
|
+
|
|
636
|
+
# Limit total length to ~500 chars
|
|
637
|
+
echo "${context:0:500}"
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
# Get session file age in hours (cross-platform)
|
|
641
|
+
# Returns: age in hours on stdout, or -1 if stat fails
|
|
642
|
+
# Note: Returns 0 for files less than 1 hour old
|
|
643
|
+
get_session_file_age_hours() {
|
|
644
|
+
local file=$1
|
|
645
|
+
|
|
646
|
+
if [[ ! -f "$file" ]]; then
|
|
647
|
+
echo "0"
|
|
648
|
+
return
|
|
649
|
+
fi
|
|
650
|
+
|
|
651
|
+
# Get file modification time using capability detection
|
|
652
|
+
# Handles macOS with Homebrew coreutils where stat flags differ
|
|
653
|
+
local file_mtime
|
|
654
|
+
|
|
655
|
+
# Try GNU stat first (Linux, macOS with Homebrew coreutils)
|
|
656
|
+
if file_mtime=$(stat -c %Y "$file" 2>/dev/null) && [[ -n "$file_mtime" && "$file_mtime" =~ ^[0-9]+$ ]]; then
|
|
657
|
+
: # success
|
|
658
|
+
# Try BSD stat (native macOS)
|
|
659
|
+
elif file_mtime=$(stat -f %m "$file" 2>/dev/null) && [[ -n "$file_mtime" && "$file_mtime" =~ ^[0-9]+$ ]]; then
|
|
660
|
+
: # success
|
|
661
|
+
# Fallback to date -r (most portable)
|
|
662
|
+
elif file_mtime=$(date -r "$file" +%s 2>/dev/null) && [[ -n "$file_mtime" && "$file_mtime" =~ ^[0-9]+$ ]]; then
|
|
663
|
+
: # success
|
|
664
|
+
else
|
|
665
|
+
file_mtime=""
|
|
666
|
+
fi
|
|
667
|
+
|
|
668
|
+
# Handle stat failure - return -1 to indicate error
|
|
669
|
+
# This prevents false expiration when stat fails
|
|
670
|
+
if [[ -z "$file_mtime" || "$file_mtime" == "0" ]]; then
|
|
671
|
+
echo "-1"
|
|
672
|
+
return
|
|
673
|
+
fi
|
|
674
|
+
|
|
675
|
+
local current_time
|
|
676
|
+
current_time=$(date +%s)
|
|
677
|
+
|
|
678
|
+
local age_seconds=$((current_time - file_mtime))
|
|
679
|
+
local age_hours=$((age_seconds / 3600))
|
|
680
|
+
|
|
681
|
+
echo "$age_hours"
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
# Initialize or resume Claude session (with expiration check)
|
|
685
|
+
#
|
|
686
|
+
# Session Expiration Strategy:
|
|
687
|
+
# - Default expiration: 24 hours (configurable via CLAUDE_SESSION_EXPIRY_HOURS)
|
|
688
|
+
# - 24 hours chosen because: long enough for multi-day projects, short enough
|
|
689
|
+
# to prevent stale context from causing unpredictable behavior
|
|
690
|
+
# - Sessions auto-expire to ensure Claude starts fresh periodically
|
|
691
|
+
#
|
|
692
|
+
# Returns (stdout):
|
|
693
|
+
# - Session ID string: when resuming a valid, non-expired session
|
|
694
|
+
# - Empty string: when starting new session (no file, expired, or stat error)
|
|
695
|
+
#
|
|
696
|
+
# Return codes:
|
|
697
|
+
# - 0: Always returns success (caller should check stdout for session ID)
|
|
698
|
+
#
|
|
699
|
+
init_claude_session() {
|
|
700
|
+
if [[ -f "$CLAUDE_SESSION_FILE" ]]; then
|
|
701
|
+
# Check session age
|
|
702
|
+
local age_hours
|
|
703
|
+
age_hours=$(get_session_file_age_hours "$CLAUDE_SESSION_FILE")
|
|
704
|
+
|
|
705
|
+
# Handle stat failure (-1) - treat as needing new session
|
|
706
|
+
# Don't expire sessions when we can't determine age
|
|
707
|
+
if [[ $age_hours -eq -1 ]]; then
|
|
708
|
+
log_status "WARN" "Could not determine session age, starting new session"
|
|
709
|
+
rm -f "$CLAUDE_SESSION_FILE"
|
|
710
|
+
echo ""
|
|
711
|
+
return 0
|
|
712
|
+
fi
|
|
713
|
+
|
|
714
|
+
# Check if session has expired
|
|
715
|
+
if [[ $age_hours -ge $CLAUDE_SESSION_EXPIRY_HOURS ]]; then
|
|
716
|
+
log_status "INFO" "Session expired (${age_hours}h old, max ${CLAUDE_SESSION_EXPIRY_HOURS}h), starting new session"
|
|
717
|
+
rm -f "$CLAUDE_SESSION_FILE"
|
|
718
|
+
echo ""
|
|
719
|
+
return 0
|
|
720
|
+
fi
|
|
721
|
+
|
|
722
|
+
# Session is valid, try to read it
|
|
723
|
+
local session_id=$(cat "$CLAUDE_SESSION_FILE" 2>/dev/null)
|
|
724
|
+
if [[ -n "$session_id" ]]; then
|
|
725
|
+
log_status "INFO" "Resuming Claude session: ${session_id:0:20}... (${age_hours}h old)"
|
|
726
|
+
echo "$session_id"
|
|
727
|
+
return 0
|
|
728
|
+
fi
|
|
729
|
+
fi
|
|
730
|
+
|
|
731
|
+
log_status "INFO" "Starting new Claude session"
|
|
732
|
+
echo ""
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
# Save session ID after successful execution
|
|
736
|
+
save_claude_session() {
|
|
737
|
+
local output_file=$1
|
|
738
|
+
|
|
739
|
+
# Try to extract session ID from JSON output
|
|
740
|
+
if [[ -f "$output_file" ]]; then
|
|
741
|
+
local session_id=$(jq -r '.metadata.session_id // .session_id // empty' "$output_file" 2>/dev/null)
|
|
742
|
+
if [[ -n "$session_id" && "$session_id" != "null" ]]; then
|
|
743
|
+
echo "$session_id" > "$CLAUDE_SESSION_FILE"
|
|
744
|
+
log_status "INFO" "Saved Claude session: ${session_id:0:20}..."
|
|
745
|
+
fi
|
|
746
|
+
fi
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
# =============================================================================
|
|
750
|
+
# SESSION LIFECYCLE MANAGEMENT FUNCTIONS (Phase 1.2)
|
|
751
|
+
# =============================================================================
|
|
752
|
+
|
|
753
|
+
# Get current session ID from Ralph session file
|
|
754
|
+
# Returns: session ID string or empty if not found
|
|
755
|
+
get_session_id() {
|
|
756
|
+
if [[ ! -f "$RALPH_SESSION_FILE" ]]; then
|
|
757
|
+
echo ""
|
|
758
|
+
return 0
|
|
759
|
+
fi
|
|
760
|
+
|
|
761
|
+
# Extract session_id from JSON file (SC2155: separate declare from assign)
|
|
762
|
+
local session_id
|
|
763
|
+
session_id=$(jq -r '.session_id // ""' "$RALPH_SESSION_FILE" 2>/dev/null)
|
|
764
|
+
local jq_status=$?
|
|
765
|
+
|
|
766
|
+
# Handle jq failure or null/empty results
|
|
767
|
+
if [[ $jq_status -ne 0 || -z "$session_id" || "$session_id" == "null" ]]; then
|
|
768
|
+
session_id=""
|
|
769
|
+
fi
|
|
770
|
+
echo "$session_id"
|
|
771
|
+
return 0
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
# Reset session with reason logging
|
|
775
|
+
# Usage: reset_session "reason_for_reset"
|
|
776
|
+
reset_session() {
|
|
777
|
+
local reason=${1:-"manual_reset"}
|
|
778
|
+
|
|
779
|
+
# Get current timestamp
|
|
780
|
+
local reset_timestamp
|
|
781
|
+
reset_timestamp=$(get_iso_timestamp)
|
|
782
|
+
|
|
783
|
+
# Always create/overwrite the session file using jq for safe JSON escaping
|
|
784
|
+
jq -n \
|
|
785
|
+
--arg session_id "" \
|
|
786
|
+
--arg created_at "" \
|
|
787
|
+
--arg last_used "" \
|
|
788
|
+
--arg reset_at "$reset_timestamp" \
|
|
789
|
+
--arg reset_reason "$reason" \
|
|
790
|
+
'{
|
|
791
|
+
session_id: $session_id,
|
|
792
|
+
created_at: $created_at,
|
|
793
|
+
last_used: $last_used,
|
|
794
|
+
reset_at: $reset_at,
|
|
795
|
+
reset_reason: $reset_reason
|
|
796
|
+
}' > "$RALPH_SESSION_FILE"
|
|
797
|
+
|
|
798
|
+
# Also clear the Claude session file for consistency
|
|
799
|
+
rm -f "$CLAUDE_SESSION_FILE" 2>/dev/null
|
|
800
|
+
|
|
801
|
+
# Clear exit signals to prevent stale completion indicators from causing premature exit (issue #91)
|
|
802
|
+
# This ensures a fresh start without leftover state from previous sessions
|
|
803
|
+
if [[ -f "$EXIT_SIGNALS_FILE" ]]; then
|
|
804
|
+
echo '{"test_only_loops": [], "done_signals": [], "completion_indicators": []}' > "$EXIT_SIGNALS_FILE"
|
|
805
|
+
[[ "${VERBOSE_PROGRESS:-}" == "true" ]] && log_status "INFO" "Cleared exit signals file"
|
|
806
|
+
fi
|
|
807
|
+
|
|
808
|
+
# Clear response analysis to prevent stale EXIT_SIGNAL from previous session
|
|
809
|
+
rm -f "$RESPONSE_ANALYSIS_FILE" 2>/dev/null
|
|
810
|
+
|
|
811
|
+
# Log the session transition (non-fatal to prevent script exit under set -e)
|
|
812
|
+
log_session_transition "active" "reset" "$reason" "${loop_count:-0}" || true
|
|
813
|
+
|
|
814
|
+
log_status "INFO" "Session reset: $reason"
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
# Log session state transitions to history file
|
|
818
|
+
# Usage: log_session_transition from_state to_state reason loop_number
|
|
819
|
+
log_session_transition() {
|
|
820
|
+
local from_state=$1
|
|
821
|
+
local to_state=$2
|
|
822
|
+
local reason=$3
|
|
823
|
+
local loop_number=${4:-0}
|
|
824
|
+
|
|
825
|
+
# Get timestamp once (SC2155: separate declare from assign)
|
|
826
|
+
local ts
|
|
827
|
+
ts=$(get_iso_timestamp)
|
|
828
|
+
|
|
829
|
+
# Create transition entry using jq for safe JSON (SC2155: separate declare from assign)
|
|
830
|
+
local transition
|
|
831
|
+
transition=$(jq -n -c \
|
|
832
|
+
--arg timestamp "$ts" \
|
|
833
|
+
--arg from_state "$from_state" \
|
|
834
|
+
--arg to_state "$to_state" \
|
|
835
|
+
--arg reason "$reason" \
|
|
836
|
+
--argjson loop_number "$loop_number" \
|
|
837
|
+
'{
|
|
838
|
+
timestamp: $timestamp,
|
|
839
|
+
from_state: $from_state,
|
|
840
|
+
to_state: $to_state,
|
|
841
|
+
reason: $reason,
|
|
842
|
+
loop_number: $loop_number
|
|
843
|
+
}')
|
|
844
|
+
|
|
845
|
+
# Read history file defensively - fallback to empty array on any failure
|
|
846
|
+
local history
|
|
847
|
+
if [[ -f "$RALPH_SESSION_HISTORY_FILE" ]]; then
|
|
848
|
+
history=$(cat "$RALPH_SESSION_HISTORY_FILE" 2>/dev/null)
|
|
849
|
+
# Validate JSON, fallback to empty array if corrupted
|
|
850
|
+
if ! echo "$history" | jq empty 2>/dev/null; then
|
|
851
|
+
history='[]'
|
|
852
|
+
fi
|
|
853
|
+
else
|
|
854
|
+
history='[]'
|
|
855
|
+
fi
|
|
856
|
+
|
|
857
|
+
# Append transition and keep only last 50 entries
|
|
858
|
+
local updated_history
|
|
859
|
+
updated_history=$(echo "$history" | jq ". += [$transition] | .[-50:]" 2>/dev/null)
|
|
860
|
+
local jq_status=$?
|
|
861
|
+
|
|
862
|
+
# Only write if jq succeeded
|
|
863
|
+
if [[ $jq_status -eq 0 && -n "$updated_history" ]]; then
|
|
864
|
+
echo "$updated_history" > "$RALPH_SESSION_HISTORY_FILE"
|
|
865
|
+
else
|
|
866
|
+
# Fallback: start fresh with just this transition
|
|
867
|
+
echo "[$transition]" > "$RALPH_SESSION_HISTORY_FILE"
|
|
868
|
+
fi
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
# Generate a unique session ID using timestamp and random component
|
|
872
|
+
generate_session_id() {
|
|
873
|
+
local ts
|
|
874
|
+
ts=$(date +%s)
|
|
875
|
+
local rand
|
|
876
|
+
rand=$RANDOM
|
|
877
|
+
echo "ralph-${ts}-${rand}"
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
# Initialize session tracking (called at loop start)
|
|
881
|
+
init_session_tracking() {
|
|
882
|
+
local ts
|
|
883
|
+
ts=$(get_iso_timestamp)
|
|
884
|
+
|
|
885
|
+
# Create session file if it doesn't exist
|
|
886
|
+
if [[ ! -f "$RALPH_SESSION_FILE" ]]; then
|
|
887
|
+
local new_session_id
|
|
888
|
+
new_session_id=$(generate_session_id)
|
|
889
|
+
|
|
890
|
+
jq -n \
|
|
891
|
+
--arg session_id "$new_session_id" \
|
|
892
|
+
--arg created_at "$ts" \
|
|
893
|
+
--arg last_used "$ts" \
|
|
894
|
+
--arg reset_at "" \
|
|
895
|
+
--arg reset_reason "" \
|
|
896
|
+
'{
|
|
897
|
+
session_id: $session_id,
|
|
898
|
+
created_at: $created_at,
|
|
899
|
+
last_used: $last_used,
|
|
900
|
+
reset_at: $reset_at,
|
|
901
|
+
reset_reason: $reset_reason
|
|
902
|
+
}' > "$RALPH_SESSION_FILE"
|
|
903
|
+
|
|
904
|
+
log_status "INFO" "Initialized session tracking (session: $new_session_id)"
|
|
905
|
+
return 0
|
|
906
|
+
fi
|
|
907
|
+
|
|
908
|
+
# Validate existing session file
|
|
909
|
+
if ! jq empty "$RALPH_SESSION_FILE" 2>/dev/null; then
|
|
910
|
+
log_status "WARN" "Corrupted session file detected, recreating..."
|
|
911
|
+
local new_session_id
|
|
912
|
+
new_session_id=$(generate_session_id)
|
|
913
|
+
|
|
914
|
+
jq -n \
|
|
915
|
+
--arg session_id "$new_session_id" \
|
|
916
|
+
--arg created_at "$ts" \
|
|
917
|
+
--arg last_used "$ts" \
|
|
918
|
+
--arg reset_at "$ts" \
|
|
919
|
+
--arg reset_reason "corrupted_file_recovery" \
|
|
920
|
+
'{
|
|
921
|
+
session_id: $session_id,
|
|
922
|
+
created_at: $created_at,
|
|
923
|
+
last_used: $last_used,
|
|
924
|
+
reset_at: $reset_at,
|
|
925
|
+
reset_reason: $reset_reason
|
|
926
|
+
}' > "$RALPH_SESSION_FILE"
|
|
927
|
+
fi
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
# Update last_used timestamp in session file (called on each loop iteration)
|
|
931
|
+
update_session_last_used() {
|
|
932
|
+
if [[ ! -f "$RALPH_SESSION_FILE" ]]; then
|
|
933
|
+
return 0
|
|
934
|
+
fi
|
|
935
|
+
|
|
936
|
+
local ts
|
|
937
|
+
ts=$(get_iso_timestamp)
|
|
938
|
+
|
|
939
|
+
# Update last_used in existing session file
|
|
940
|
+
local updated
|
|
941
|
+
updated=$(jq --arg last_used "$ts" '.last_used = $last_used' "$RALPH_SESSION_FILE" 2>/dev/null)
|
|
942
|
+
local jq_status=$?
|
|
943
|
+
|
|
944
|
+
if [[ $jq_status -eq 0 && -n "$updated" ]]; then
|
|
945
|
+
echo "$updated" > "$RALPH_SESSION_FILE"
|
|
946
|
+
fi
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
# Global array for Claude command arguments (avoids shell injection)
|
|
950
|
+
declare -a CLAUDE_CMD_ARGS=()
|
|
951
|
+
|
|
952
|
+
# Build Claude CLI command with modern flags using array (shell-injection safe)
|
|
953
|
+
# Populates global CLAUDE_CMD_ARGS array for direct execution
|
|
954
|
+
# Uses -p flag with prompt content (Claude CLI does not have --prompt-file)
|
|
955
|
+
build_claude_command() {
|
|
956
|
+
local prompt_file=$1
|
|
957
|
+
local loop_context=$2
|
|
958
|
+
local session_id=$3
|
|
959
|
+
|
|
960
|
+
# Reset global array
|
|
961
|
+
# Note: We do NOT use --dangerously-skip-permissions here. Tool permissions
|
|
962
|
+
# are controlled via --allowedTools from CLAUDE_ALLOWED_TOOLS in .ralphrc.
|
|
963
|
+
# This preserves the permission denial circuit breaker (Issue #101).
|
|
964
|
+
CLAUDE_CMD_ARGS=("$CLAUDE_CODE_CMD")
|
|
965
|
+
|
|
966
|
+
# Check if prompt file exists
|
|
967
|
+
if [[ ! -f "$prompt_file" ]]; then
|
|
968
|
+
log_status "ERROR" "Prompt file not found: $prompt_file"
|
|
969
|
+
return 1
|
|
970
|
+
fi
|
|
971
|
+
|
|
972
|
+
# Add output format flag
|
|
973
|
+
if [[ "$CLAUDE_OUTPUT_FORMAT" == "json" ]]; then
|
|
974
|
+
CLAUDE_CMD_ARGS+=("--output-format" "json")
|
|
975
|
+
fi
|
|
976
|
+
|
|
977
|
+
# Add allowed tools (each tool as separate array element)
|
|
978
|
+
if [[ -n "$CLAUDE_ALLOWED_TOOLS" ]]; then
|
|
979
|
+
CLAUDE_CMD_ARGS+=("--allowedTools")
|
|
980
|
+
# Split by comma and add each tool
|
|
981
|
+
local IFS=','
|
|
982
|
+
read -ra tools_array <<< "$CLAUDE_ALLOWED_TOOLS"
|
|
983
|
+
for tool in "${tools_array[@]}"; do
|
|
984
|
+
# Trim whitespace
|
|
985
|
+
tool=$(echo "$tool" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
986
|
+
if [[ -n "$tool" ]]; then
|
|
987
|
+
CLAUDE_CMD_ARGS+=("$tool")
|
|
988
|
+
fi
|
|
989
|
+
done
|
|
990
|
+
fi
|
|
991
|
+
|
|
992
|
+
# Add session continuity flag
|
|
993
|
+
# IMPORTANT: Use --resume with explicit session ID instead of --continue
|
|
994
|
+
# --continue resumes the "most recent session in current directory" which
|
|
995
|
+
# can hijack active Claude Code sessions. --resume with a specific session ID
|
|
996
|
+
# ensures we only resume Ralph's own sessions. (Issue #151)
|
|
997
|
+
if [[ "$CLAUDE_USE_CONTINUE" == "true" && -n "$session_id" ]]; then
|
|
998
|
+
CLAUDE_CMD_ARGS+=("--resume" "$session_id")
|
|
999
|
+
fi
|
|
1000
|
+
# If no session_id, start fresh - Claude will generate a new session ID
|
|
1001
|
+
# which we'll capture via save_claude_session() for future loops
|
|
1002
|
+
|
|
1003
|
+
# Add loop context as system prompt (no escaping needed - array handles it)
|
|
1004
|
+
if [[ -n "$loop_context" ]]; then
|
|
1005
|
+
CLAUDE_CMD_ARGS+=("--append-system-prompt" "$loop_context")
|
|
1006
|
+
fi
|
|
1007
|
+
|
|
1008
|
+
# Read prompt file content and use -p flag
|
|
1009
|
+
# Note: Claude CLI uses -p for prompts, not --prompt-file (which doesn't exist)
|
|
1010
|
+
# Array-based approach maintains shell injection safety
|
|
1011
|
+
local prompt_content
|
|
1012
|
+
prompt_content=$(cat "$prompt_file")
|
|
1013
|
+
CLAUDE_CMD_ARGS+=("-p" "$prompt_content")
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
# Main execution function
|
|
1017
|
+
execute_claude_code() {
|
|
1018
|
+
local timestamp=$(date '+%Y-%m-%d_%H-%M-%S')
|
|
1019
|
+
local output_file="$LOG_DIR/claude_output_${timestamp}.log"
|
|
1020
|
+
local loop_count=$1
|
|
1021
|
+
local calls_made=$(cat "$CALL_COUNT_FILE" 2>/dev/null || echo "0")
|
|
1022
|
+
calls_made=$((calls_made + 1))
|
|
1023
|
+
|
|
1024
|
+
# Fix #141: Capture git HEAD SHA at loop start to detect commits as progress
|
|
1025
|
+
# Store in file for access by progress detection after Claude execution
|
|
1026
|
+
local loop_start_sha=""
|
|
1027
|
+
if command -v git &>/dev/null && git rev-parse --git-dir &>/dev/null 2>&1; then
|
|
1028
|
+
loop_start_sha=$(git rev-parse HEAD 2>/dev/null || echo "")
|
|
1029
|
+
fi
|
|
1030
|
+
echo "$loop_start_sha" > "$RALPH_DIR/.loop_start_sha"
|
|
1031
|
+
|
|
1032
|
+
log_status "LOOP" "Executing Claude Code (Call $calls_made/$MAX_CALLS_PER_HOUR)"
|
|
1033
|
+
local timeout_seconds=$((CLAUDE_TIMEOUT_MINUTES * 60))
|
|
1034
|
+
log_status "INFO" "⏳ Starting Claude Code execution... (timeout: ${CLAUDE_TIMEOUT_MINUTES}m)"
|
|
1035
|
+
|
|
1036
|
+
# Build loop context for session continuity
|
|
1037
|
+
local loop_context=""
|
|
1038
|
+
if [[ "$CLAUDE_USE_CONTINUE" == "true" ]]; then
|
|
1039
|
+
loop_context=$(build_loop_context "$loop_count")
|
|
1040
|
+
if [[ -n "$loop_context" && "$VERBOSE_PROGRESS" == "true" ]]; then
|
|
1041
|
+
log_status "INFO" "Loop context: $loop_context"
|
|
1042
|
+
fi
|
|
1043
|
+
fi
|
|
1044
|
+
|
|
1045
|
+
# Initialize or resume session
|
|
1046
|
+
local session_id=""
|
|
1047
|
+
if [[ "$CLAUDE_USE_CONTINUE" == "true" ]]; then
|
|
1048
|
+
session_id=$(init_claude_session)
|
|
1049
|
+
fi
|
|
1050
|
+
|
|
1051
|
+
# Live mode requires JSON output (stream-json) — override text format
|
|
1052
|
+
if [[ "$LIVE_OUTPUT" == "true" && "$CLAUDE_OUTPUT_FORMAT" == "text" ]]; then
|
|
1053
|
+
log_status "WARN" "Live mode requires JSON output format. Overriding text → json for this session."
|
|
1054
|
+
CLAUDE_OUTPUT_FORMAT="json"
|
|
1055
|
+
fi
|
|
1056
|
+
|
|
1057
|
+
# Build the Claude CLI command with modern flags
|
|
1058
|
+
local use_modern_cli=false
|
|
1059
|
+
|
|
1060
|
+
if build_claude_command "$PROMPT_FILE" "$loop_context" "$session_id"; then
|
|
1061
|
+
use_modern_cli=true
|
|
1062
|
+
log_status "INFO" "Using modern CLI mode (${CLAUDE_OUTPUT_FORMAT} output)"
|
|
1063
|
+
else
|
|
1064
|
+
log_status "WARN" "Failed to build modern CLI command, falling back to legacy mode"
|
|
1065
|
+
if [[ "$LIVE_OUTPUT" == "true" ]]; then
|
|
1066
|
+
log_status "ERROR" "Live mode requires a built Claude command. Falling back to background mode."
|
|
1067
|
+
LIVE_OUTPUT=false
|
|
1068
|
+
fi
|
|
1069
|
+
fi
|
|
1070
|
+
|
|
1071
|
+
# Execute Claude Code
|
|
1072
|
+
local exit_code=0
|
|
1073
|
+
|
|
1074
|
+
# Initialize live.log for this execution
|
|
1075
|
+
echo -e "\n\n=== Loop #$loop_count - $(date '+%Y-%m-%d %H:%M:%S') ===" > "$LIVE_LOG_FILE"
|
|
1076
|
+
|
|
1077
|
+
if [[ "$LIVE_OUTPUT" == "true" ]]; then
|
|
1078
|
+
# LIVE MODE: Show streaming output in real-time using stream-json + jq
|
|
1079
|
+
# Based on: https://www.ytyng.com/en/blog/claude-stream-json-jq/
|
|
1080
|
+
#
|
|
1081
|
+
# Uses CLAUDE_CMD_ARGS from build_claude_command() to preserve:
|
|
1082
|
+
# - --allowedTools (tool permissions)
|
|
1083
|
+
# - --append-system-prompt (loop context)
|
|
1084
|
+
# - --continue (session continuity)
|
|
1085
|
+
# - -p (prompt content)
|
|
1086
|
+
|
|
1087
|
+
# Check dependencies for live mode
|
|
1088
|
+
if ! command -v jq &> /dev/null; then
|
|
1089
|
+
log_status "ERROR" "Live mode requires 'jq' but it's not installed. Falling back to background mode."
|
|
1090
|
+
LIVE_OUTPUT=false
|
|
1091
|
+
elif ! command -v stdbuf &> /dev/null; then
|
|
1092
|
+
log_status "ERROR" "Live mode requires 'stdbuf' (from coreutils) but it's not installed. Falling back to background mode."
|
|
1093
|
+
LIVE_OUTPUT=false
|
|
1094
|
+
fi
|
|
1095
|
+
fi
|
|
1096
|
+
|
|
1097
|
+
if [[ "$LIVE_OUTPUT" == "true" ]]; then
|
|
1098
|
+
# Safety check: live mode requires a successfully built modern command
|
|
1099
|
+
if [[ "$use_modern_cli" != "true" || ${#CLAUDE_CMD_ARGS[@]} -eq 0 ]]; then
|
|
1100
|
+
log_status "ERROR" "Live mode requires a built Claude command. Falling back to background mode."
|
|
1101
|
+
LIVE_OUTPUT=false
|
|
1102
|
+
fi
|
|
1103
|
+
fi
|
|
1104
|
+
|
|
1105
|
+
if [[ "$LIVE_OUTPUT" == "true" ]]; then
|
|
1106
|
+
log_status "INFO" "📺 Live output mode enabled - showing Claude Code streaming..."
|
|
1107
|
+
echo -e "${PURPLE}━━━━━━━━━━━━━━━━ Claude Code Output ━━━━━━━━━━━━━━━━${NC}"
|
|
1108
|
+
|
|
1109
|
+
# Modify CLAUDE_CMD_ARGS: replace --output-format value with stream-json
|
|
1110
|
+
# and add streaming-specific flags
|
|
1111
|
+
local -a LIVE_CMD_ARGS=()
|
|
1112
|
+
local skip_next=false
|
|
1113
|
+
for arg in "${CLAUDE_CMD_ARGS[@]}"; do
|
|
1114
|
+
if [[ "$skip_next" == "true" ]]; then
|
|
1115
|
+
# Replace "json" with "stream-json" for output format
|
|
1116
|
+
LIVE_CMD_ARGS+=("stream-json")
|
|
1117
|
+
skip_next=false
|
|
1118
|
+
elif [[ "$arg" == "--output-format" ]]; then
|
|
1119
|
+
LIVE_CMD_ARGS+=("$arg")
|
|
1120
|
+
skip_next=true
|
|
1121
|
+
else
|
|
1122
|
+
LIVE_CMD_ARGS+=("$arg")
|
|
1123
|
+
fi
|
|
1124
|
+
done
|
|
1125
|
+
|
|
1126
|
+
# Add streaming-specific flags (--verbose and --include-partial-messages)
|
|
1127
|
+
# These are required for stream-json to work properly
|
|
1128
|
+
LIVE_CMD_ARGS+=("--verbose" "--include-partial-messages")
|
|
1129
|
+
|
|
1130
|
+
# jq filter: show text + tool names + newlines for readability
|
|
1131
|
+
local jq_filter='
|
|
1132
|
+
if .type == "stream_event" then
|
|
1133
|
+
if .event.type == "content_block_delta" and .event.delta.type == "text_delta" then
|
|
1134
|
+
.event.delta.text
|
|
1135
|
+
elif .event.type == "content_block_start" and .event.content_block.type == "tool_use" then
|
|
1136
|
+
"\n\n⚡ [" + .event.content_block.name + "]\n"
|
|
1137
|
+
elif .event.type == "content_block_stop" then
|
|
1138
|
+
"\n"
|
|
1139
|
+
else
|
|
1140
|
+
empty
|
|
1141
|
+
end
|
|
1142
|
+
else
|
|
1143
|
+
empty
|
|
1144
|
+
end'
|
|
1145
|
+
|
|
1146
|
+
# Execute with streaming, preserving all flags from build_claude_command()
|
|
1147
|
+
# Use stdbuf to disable buffering for real-time output
|
|
1148
|
+
# Use portable_timeout for consistent timeout protection (Issue: missing timeout)
|
|
1149
|
+
# Capture all pipeline exit codes for proper error handling
|
|
1150
|
+
# stdin must be redirected from /dev/null because newer Claude CLI versions
|
|
1151
|
+
# read from stdin even in -p (print) mode, causing the process to hang
|
|
1152
|
+
set -o pipefail
|
|
1153
|
+
portable_timeout ${timeout_seconds}s stdbuf -oL "${LIVE_CMD_ARGS[@]}" \
|
|
1154
|
+
< /dev/null 2>&1 | stdbuf -oL tee "$output_file" | stdbuf -oL jq --unbuffered -j "$jq_filter" 2>/dev/null | tee "$LIVE_LOG_FILE"
|
|
1155
|
+
|
|
1156
|
+
# Capture exit codes from pipeline
|
|
1157
|
+
local -a pipe_status=("${PIPESTATUS[@]}")
|
|
1158
|
+
set +o pipefail
|
|
1159
|
+
|
|
1160
|
+
# Primary exit code is from Claude/timeout (first command in pipeline)
|
|
1161
|
+
exit_code=${pipe_status[0]}
|
|
1162
|
+
|
|
1163
|
+
# Check for tee failures (second command) - could break logging/session
|
|
1164
|
+
if [[ ${pipe_status[1]} -ne 0 ]]; then
|
|
1165
|
+
log_status "WARN" "Failed to write stream output to log file (exit code ${pipe_status[1]})"
|
|
1166
|
+
fi
|
|
1167
|
+
|
|
1168
|
+
# Check for jq failures (third command) - warn but don't fail
|
|
1169
|
+
if [[ ${pipe_status[2]} -ne 0 ]]; then
|
|
1170
|
+
log_status "WARN" "jq filter had issues parsing some stream events (exit code ${pipe_status[2]})"
|
|
1171
|
+
fi
|
|
1172
|
+
|
|
1173
|
+
echo ""
|
|
1174
|
+
echo -e "${PURPLE}━━━━━━━━━━━━━━━━ End of Output ━━━━━━━━━━━━━━━━━━━${NC}"
|
|
1175
|
+
|
|
1176
|
+
# Extract session ID from stream-json output for session continuity
|
|
1177
|
+
# Stream-json format has session_id in the final "result" type message
|
|
1178
|
+
# Keep full stream output in _stream.log, extract session data separately
|
|
1179
|
+
if [[ "$CLAUDE_USE_CONTINUE" == "true" && -f "$output_file" ]]; then
|
|
1180
|
+
# Preserve full stream output for analysis (don't overwrite output_file)
|
|
1181
|
+
local stream_output_file="${output_file%.log}_stream.log"
|
|
1182
|
+
cp "$output_file" "$stream_output_file"
|
|
1183
|
+
|
|
1184
|
+
# Extract the result message and convert to standard JSON format
|
|
1185
|
+
# Use flexible regex to match various JSON formatting styles
|
|
1186
|
+
# Matches: "type":"result", "type": "result", "type" : "result"
|
|
1187
|
+
local result_line=$(grep -E '"type"[[:space:]]*:[[:space:]]*"result"' "$output_file" 2>/dev/null | tail -1)
|
|
1188
|
+
|
|
1189
|
+
if [[ -n "$result_line" ]]; then
|
|
1190
|
+
# Validate that extracted line is valid JSON before using it
|
|
1191
|
+
if echo "$result_line" | jq -e . >/dev/null 2>&1; then
|
|
1192
|
+
# Write validated result as the output_file for downstream processing
|
|
1193
|
+
# (save_claude_session and analyze_response expect JSON format)
|
|
1194
|
+
echo "$result_line" > "$output_file"
|
|
1195
|
+
log_status "INFO" "Extracted and validated session data from stream output"
|
|
1196
|
+
else
|
|
1197
|
+
log_status "WARN" "Extracted result line is not valid JSON, keeping stream output"
|
|
1198
|
+
# Restore original stream output
|
|
1199
|
+
cp "$stream_output_file" "$output_file"
|
|
1200
|
+
fi
|
|
1201
|
+
else
|
|
1202
|
+
log_status "WARN" "Could not find result message in stream output"
|
|
1203
|
+
# Keep stream output as-is for debugging
|
|
1204
|
+
fi
|
|
1205
|
+
fi
|
|
1206
|
+
else
|
|
1207
|
+
# BACKGROUND MODE: Original behavior with progress monitoring
|
|
1208
|
+
if [[ "$use_modern_cli" == "true" ]]; then
|
|
1209
|
+
# Modern execution with command array (shell-injection safe)
|
|
1210
|
+
# Execute array directly without bash -c to prevent shell metacharacter interpretation
|
|
1211
|
+
# stdin must be redirected from /dev/null because newer Claude CLI versions
|
|
1212
|
+
# read from stdin even in -p (print) mode, causing SIGTTIN suspension
|
|
1213
|
+
# when the process is backgrounded
|
|
1214
|
+
if portable_timeout ${timeout_seconds}s "${CLAUDE_CMD_ARGS[@]}" < /dev/null > "$output_file" 2>&1 &
|
|
1215
|
+
then
|
|
1216
|
+
: # Continue to wait loop
|
|
1217
|
+
else
|
|
1218
|
+
log_status "ERROR" "❌ Failed to start Claude Code process (modern mode)"
|
|
1219
|
+
# Fall back to legacy mode
|
|
1220
|
+
log_status "INFO" "Falling back to legacy mode..."
|
|
1221
|
+
use_modern_cli=false
|
|
1222
|
+
fi
|
|
1223
|
+
fi
|
|
1224
|
+
|
|
1225
|
+
# Fall back to legacy stdin piping if modern mode failed or not enabled
|
|
1226
|
+
# Note: Legacy mode doesn't use --allowedTools, so tool permissions
|
|
1227
|
+
# will be handled by Claude Code's default permission system
|
|
1228
|
+
if [[ "$use_modern_cli" == "false" ]]; then
|
|
1229
|
+
if portable_timeout ${timeout_seconds}s $CLAUDE_CODE_CMD < "$PROMPT_FILE" > "$output_file" 2>&1 &
|
|
1230
|
+
then
|
|
1231
|
+
: # Continue to wait loop
|
|
1232
|
+
else
|
|
1233
|
+
log_status "ERROR" "❌ Failed to start Claude Code process"
|
|
1234
|
+
return 1
|
|
1235
|
+
fi
|
|
1236
|
+
fi
|
|
1237
|
+
|
|
1238
|
+
# Get PID and monitor progress
|
|
1239
|
+
local claude_pid=$!
|
|
1240
|
+
local progress_counter=0
|
|
1241
|
+
|
|
1242
|
+
# Show progress while Claude Code is running
|
|
1243
|
+
while kill -0 $claude_pid 2>/dev/null; do
|
|
1244
|
+
progress_counter=$((progress_counter + 1))
|
|
1245
|
+
case $((progress_counter % 4)) in
|
|
1246
|
+
1) progress_indicator="⠋" ;;
|
|
1247
|
+
2) progress_indicator="⠙" ;;
|
|
1248
|
+
3) progress_indicator="⠹" ;;
|
|
1249
|
+
0) progress_indicator="⠸" ;;
|
|
1250
|
+
esac
|
|
1251
|
+
|
|
1252
|
+
# Get last line from output if available
|
|
1253
|
+
local last_line=""
|
|
1254
|
+
if [[ -f "$output_file" && -s "$output_file" ]]; then
|
|
1255
|
+
last_line=$(tail -1 "$output_file" 2>/dev/null | head -c 80)
|
|
1256
|
+
# Copy to live.log for tmux monitoring
|
|
1257
|
+
cp "$output_file" "$LIVE_LOG_FILE" 2>/dev/null
|
|
1258
|
+
fi
|
|
1259
|
+
|
|
1260
|
+
# Update progress file for monitor
|
|
1261
|
+
cat > "$PROGRESS_FILE" << EOF
|
|
1262
|
+
{
|
|
1263
|
+
"status": "executing",
|
|
1264
|
+
"indicator": "$progress_indicator",
|
|
1265
|
+
"elapsed_seconds": $((progress_counter * 10)),
|
|
1266
|
+
"last_output": "$last_line",
|
|
1267
|
+
"timestamp": "$(date '+%Y-%m-%d %H:%M:%S')"
|
|
1268
|
+
}
|
|
1269
|
+
EOF
|
|
1270
|
+
|
|
1271
|
+
# Only log if verbose mode is enabled
|
|
1272
|
+
if [[ "$VERBOSE_PROGRESS" == "true" ]]; then
|
|
1273
|
+
if [[ -n "$last_line" ]]; then
|
|
1274
|
+
log_status "INFO" "$progress_indicator Claude Code: $last_line... (${progress_counter}0s)"
|
|
1275
|
+
else
|
|
1276
|
+
log_status "INFO" "$progress_indicator Claude Code working... (${progress_counter}0s elapsed)"
|
|
1277
|
+
fi
|
|
1278
|
+
fi
|
|
1279
|
+
|
|
1280
|
+
sleep 10
|
|
1281
|
+
done
|
|
1282
|
+
|
|
1283
|
+
# Wait for the process to finish and get exit code
|
|
1284
|
+
wait $claude_pid
|
|
1285
|
+
exit_code=$?
|
|
1286
|
+
fi
|
|
1287
|
+
|
|
1288
|
+
if [ $exit_code -eq 0 ]; then
|
|
1289
|
+
# Only increment counter on successful execution
|
|
1290
|
+
echo "$calls_made" > "$CALL_COUNT_FILE"
|
|
1291
|
+
|
|
1292
|
+
# Clear progress file
|
|
1293
|
+
echo '{"status": "completed", "timestamp": "'$(date '+%Y-%m-%d %H:%M:%S')'"}' > "$PROGRESS_FILE"
|
|
1294
|
+
|
|
1295
|
+
log_status "SUCCESS" "✅ Claude Code execution completed successfully"
|
|
1296
|
+
|
|
1297
|
+
# Save session ID from JSON output (Phase 1.1)
|
|
1298
|
+
if [[ "$CLAUDE_USE_CONTINUE" == "true" ]]; then
|
|
1299
|
+
save_claude_session "$output_file"
|
|
1300
|
+
fi
|
|
1301
|
+
|
|
1302
|
+
# Analyze the response
|
|
1303
|
+
log_status "INFO" "🔍 Analyzing Claude Code response..."
|
|
1304
|
+
analyze_response "$output_file" "$loop_count"
|
|
1305
|
+
local analysis_exit_code=$?
|
|
1306
|
+
|
|
1307
|
+
# Update exit signals based on analysis
|
|
1308
|
+
update_exit_signals
|
|
1309
|
+
|
|
1310
|
+
# Log analysis summary
|
|
1311
|
+
log_analysis_summary
|
|
1312
|
+
|
|
1313
|
+
# Get file change count for circuit breaker
|
|
1314
|
+
# Fix #141: Detect both uncommitted changes AND committed changes
|
|
1315
|
+
local files_changed=0
|
|
1316
|
+
local loop_start_sha=""
|
|
1317
|
+
local current_sha=""
|
|
1318
|
+
|
|
1319
|
+
if [[ -f "$RALPH_DIR/.loop_start_sha" ]]; then
|
|
1320
|
+
loop_start_sha=$(cat "$RALPH_DIR/.loop_start_sha" 2>/dev/null || echo "")
|
|
1321
|
+
fi
|
|
1322
|
+
|
|
1323
|
+
if command -v git &>/dev/null && git rev-parse --git-dir &>/dev/null 2>&1; then
|
|
1324
|
+
current_sha=$(git rev-parse HEAD 2>/dev/null || echo "")
|
|
1325
|
+
|
|
1326
|
+
# Check if commits were made (HEAD changed)
|
|
1327
|
+
if [[ -n "$loop_start_sha" && -n "$current_sha" && "$loop_start_sha" != "$current_sha" ]]; then
|
|
1328
|
+
# Commits were made - count union of committed files AND working tree changes
|
|
1329
|
+
# This catches cases where Claude commits some files but still has other modified files
|
|
1330
|
+
files_changed=$(
|
|
1331
|
+
{
|
|
1332
|
+
git diff --name-only "$loop_start_sha" "$current_sha" 2>/dev/null
|
|
1333
|
+
git diff --name-only HEAD 2>/dev/null # unstaged changes
|
|
1334
|
+
git diff --name-only --cached 2>/dev/null # staged changes
|
|
1335
|
+
} | sort -u | wc -l
|
|
1336
|
+
)
|
|
1337
|
+
[[ "$VERBOSE_PROGRESS" == "true" ]] && log_status "DEBUG" "Detected $files_changed unique files changed (commits + working tree) since loop start"
|
|
1338
|
+
else
|
|
1339
|
+
# No commits - check for uncommitted changes (staged + unstaged)
|
|
1340
|
+
files_changed=$(
|
|
1341
|
+
{
|
|
1342
|
+
git diff --name-only 2>/dev/null # unstaged changes
|
|
1343
|
+
git diff --name-only --cached 2>/dev/null # staged changes
|
|
1344
|
+
} | sort -u | wc -l
|
|
1345
|
+
)
|
|
1346
|
+
fi
|
|
1347
|
+
fi
|
|
1348
|
+
|
|
1349
|
+
local has_errors="false"
|
|
1350
|
+
|
|
1351
|
+
# Two-stage error detection to avoid JSON field false positives
|
|
1352
|
+
# Stage 1: Filter out JSON field patterns like "is_error": false
|
|
1353
|
+
# Stage 2: Look for actual error messages in specific contexts
|
|
1354
|
+
# Avoid type annotations like "error: Error" by requiring lowercase after ": error"
|
|
1355
|
+
if grep -v '"[^"]*error[^"]*":' "$output_file" 2>/dev/null | \
|
|
1356
|
+
grep -qE '(^Error:|^ERROR:|^error:|\]: error|Link: error|Error occurred|failed with error|[Ee]xception|Fatal|FATAL)'; then
|
|
1357
|
+
has_errors="true"
|
|
1358
|
+
|
|
1359
|
+
# Debug logging: show what triggered error detection
|
|
1360
|
+
if [[ "$VERBOSE_PROGRESS" == "true" ]]; then
|
|
1361
|
+
log_status "DEBUG" "Error patterns found:"
|
|
1362
|
+
grep -v '"[^"]*error[^"]*":' "$output_file" 2>/dev/null | \
|
|
1363
|
+
grep -nE '(^Error:|^ERROR:|^error:|\]: error|Link: error|Error occurred|failed with error|[Ee]xception|Fatal|FATAL)' | \
|
|
1364
|
+
head -3 | while IFS= read -r line; do
|
|
1365
|
+
log_status "DEBUG" " $line"
|
|
1366
|
+
done
|
|
1367
|
+
fi
|
|
1368
|
+
|
|
1369
|
+
log_status "WARN" "Errors detected in output, check: $output_file"
|
|
1370
|
+
fi
|
|
1371
|
+
local output_length=$(wc -c < "$output_file" 2>/dev/null || echo 0)
|
|
1372
|
+
|
|
1373
|
+
# Record result in circuit breaker
|
|
1374
|
+
record_loop_result "$loop_count" "$files_changed" "$has_errors" "$output_length"
|
|
1375
|
+
local circuit_result=$?
|
|
1376
|
+
|
|
1377
|
+
if [[ $circuit_result -ne 0 ]]; then
|
|
1378
|
+
log_status "WARN" "Circuit breaker opened - halting execution"
|
|
1379
|
+
return 3 # Special code for circuit breaker trip
|
|
1380
|
+
fi
|
|
1381
|
+
|
|
1382
|
+
return 0
|
|
1383
|
+
else
|
|
1384
|
+
# Clear progress file on failure
|
|
1385
|
+
echo '{"status": "failed", "timestamp": "'$(date '+%Y-%m-%d %H:%M:%S')'"}' > "$PROGRESS_FILE"
|
|
1386
|
+
|
|
1387
|
+
# Check if the failure is due to API 5-hour limit
|
|
1388
|
+
if grep -qi "5.*hour.*limit\|limit.*reached.*try.*back\|usage.*limit.*reached" "$output_file"; then
|
|
1389
|
+
log_status "ERROR" "🚫 Claude API 5-hour usage limit reached"
|
|
1390
|
+
return 2 # Special return code for API limit
|
|
1391
|
+
else
|
|
1392
|
+
log_status "ERROR" "❌ Claude Code execution failed, check: $output_file"
|
|
1393
|
+
return 1
|
|
1394
|
+
fi
|
|
1395
|
+
fi
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
# Cleanup function
|
|
1399
|
+
cleanup() {
|
|
1400
|
+
log_status "INFO" "Ralph loop interrupted. Cleaning up..."
|
|
1401
|
+
reset_session "manual_interrupt"
|
|
1402
|
+
update_status "$loop_count" "$(cat "$CALL_COUNT_FILE" 2>/dev/null || echo "0")" "interrupted" "stopped"
|
|
1403
|
+
exit 0
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
# Set up signal handlers
|
|
1407
|
+
trap cleanup SIGINT SIGTERM
|
|
1408
|
+
|
|
1409
|
+
# Global variable for loop count (needed by cleanup function)
|
|
1410
|
+
loop_count=0
|
|
1411
|
+
|
|
1412
|
+
# Main loop
|
|
1413
|
+
main() {
|
|
1414
|
+
# Load project-specific configuration from .ralphrc
|
|
1415
|
+
if load_ralphrc; then
|
|
1416
|
+
if [[ "$RALPHRC_LOADED" == "true" ]]; then
|
|
1417
|
+
log_status "INFO" "Loaded configuration from .ralphrc"
|
|
1418
|
+
fi
|
|
1419
|
+
fi
|
|
1420
|
+
|
|
1421
|
+
log_status "SUCCESS" "🚀 Ralph loop starting with Claude Code"
|
|
1422
|
+
log_status "INFO" "Max calls per hour: $MAX_CALLS_PER_HOUR"
|
|
1423
|
+
log_status "INFO" "Logs: $LOG_DIR/ | Docs: $DOCS_DIR/ | Status: $STATUS_FILE"
|
|
1424
|
+
|
|
1425
|
+
# Check if project uses old flat structure and needs migration
|
|
1426
|
+
if [[ -f "PROMPT.md" ]] && [[ ! -d ".ralph" ]]; then
|
|
1427
|
+
log_status "ERROR" "This project uses the old flat structure."
|
|
1428
|
+
echo ""
|
|
1429
|
+
echo "Ralph v0.10.0+ uses a .ralph/ subfolder to keep your project root clean."
|
|
1430
|
+
echo ""
|
|
1431
|
+
echo "To upgrade your project, run:"
|
|
1432
|
+
echo " ralph-migrate"
|
|
1433
|
+
echo ""
|
|
1434
|
+
echo "This will move Ralph-specific files to .ralph/ while preserving src/ at root."
|
|
1435
|
+
echo "A backup will be created before migration."
|
|
1436
|
+
exit 1
|
|
1437
|
+
fi
|
|
1438
|
+
|
|
1439
|
+
# Check if this is a Ralph project directory
|
|
1440
|
+
if [[ ! -f "$PROMPT_FILE" ]]; then
|
|
1441
|
+
log_status "ERROR" "Prompt file '$PROMPT_FILE' not found!"
|
|
1442
|
+
echo ""
|
|
1443
|
+
|
|
1444
|
+
# Check if this looks like a partial Ralph project
|
|
1445
|
+
if [[ -f "$RALPH_DIR/@fix_plan.md" ]] || [[ -d "$RALPH_DIR/specs" ]] || [[ -f "$RALPH_DIR/AGENT.md" ]]; then
|
|
1446
|
+
echo "This appears to be a Ralph project but is missing .ralph/PROMPT.md."
|
|
1447
|
+
echo "You may need to create or restore the PROMPT.md file."
|
|
1448
|
+
else
|
|
1449
|
+
echo "This directory is not a Ralph project."
|
|
1450
|
+
fi
|
|
1451
|
+
|
|
1452
|
+
echo ""
|
|
1453
|
+
echo "To fix this:"
|
|
1454
|
+
echo " 1. Enable Ralph in existing project: ralph-enable"
|
|
1455
|
+
echo " 2. Create a new project: ralph-setup my-project"
|
|
1456
|
+
echo " 3. Import existing requirements: ralph-import requirements.md"
|
|
1457
|
+
echo " 4. Navigate to an existing Ralph project directory"
|
|
1458
|
+
echo " 5. Or create .ralph/PROMPT.md manually in this directory"
|
|
1459
|
+
echo ""
|
|
1460
|
+
echo "Ralph projects should contain: .ralph/PROMPT.md, .ralph/@fix_plan.md, .ralph/specs/, src/, etc."
|
|
1461
|
+
exit 1
|
|
1462
|
+
fi
|
|
1463
|
+
|
|
1464
|
+
# Initialize session tracking before entering the loop
|
|
1465
|
+
init_session_tracking
|
|
1466
|
+
|
|
1467
|
+
log_status "INFO" "Starting main loop..."
|
|
1468
|
+
|
|
1469
|
+
while true; do
|
|
1470
|
+
loop_count=$((loop_count + 1))
|
|
1471
|
+
|
|
1472
|
+
# Update session last_used timestamp
|
|
1473
|
+
update_session_last_used
|
|
1474
|
+
|
|
1475
|
+
log_status "INFO" "Loop #$loop_count - calling init_call_tracking..."
|
|
1476
|
+
init_call_tracking
|
|
1477
|
+
|
|
1478
|
+
log_status "LOOP" "=== Starting Loop #$loop_count ==="
|
|
1479
|
+
|
|
1480
|
+
# Check circuit breaker before attempting execution
|
|
1481
|
+
if should_halt_execution; then
|
|
1482
|
+
reset_session "circuit_breaker_open"
|
|
1483
|
+
update_status "$loop_count" "$(cat "$CALL_COUNT_FILE")" "circuit_breaker_open" "halted" "stagnation_detected"
|
|
1484
|
+
log_status "ERROR" "🛑 Circuit breaker has opened - execution halted"
|
|
1485
|
+
break
|
|
1486
|
+
fi
|
|
1487
|
+
|
|
1488
|
+
# Check rate limits
|
|
1489
|
+
if ! can_make_call; then
|
|
1490
|
+
wait_for_reset
|
|
1491
|
+
continue
|
|
1492
|
+
fi
|
|
1493
|
+
|
|
1494
|
+
# Check for graceful exit conditions
|
|
1495
|
+
local exit_reason=$(should_exit_gracefully)
|
|
1496
|
+
if [[ "$exit_reason" != "" ]]; then
|
|
1497
|
+
# Handle permission_denied specially (Issue #101)
|
|
1498
|
+
if [[ "$exit_reason" == "permission_denied" ]]; then
|
|
1499
|
+
log_status "ERROR" "🚫 Permission denied - halting loop"
|
|
1500
|
+
reset_session "permission_denied"
|
|
1501
|
+
update_status "$loop_count" "$(cat "$CALL_COUNT_FILE")" "permission_denied" "halted" "permission_denied"
|
|
1502
|
+
|
|
1503
|
+
# Display helpful guidance for resolving permission issues
|
|
1504
|
+
echo ""
|
|
1505
|
+
echo -e "${RED}╔════════════════════════════════════════════════════════════╗${NC}"
|
|
1506
|
+
echo -e "${RED}║ PERMISSION DENIED - Loop Halted ║${NC}"
|
|
1507
|
+
echo -e "${RED}╚════════════════════════════════════════════════════════════╝${NC}"
|
|
1508
|
+
echo ""
|
|
1509
|
+
echo -e "${YELLOW}Claude Code was denied permission to execute commands.${NC}"
|
|
1510
|
+
echo ""
|
|
1511
|
+
echo -e "${YELLOW}To fix this:${NC}"
|
|
1512
|
+
echo " 1. Edit .ralphrc and update ALLOWED_TOOLS to include the required tools"
|
|
1513
|
+
echo " 2. Common patterns:"
|
|
1514
|
+
echo " - Bash(npm *) - All npm commands"
|
|
1515
|
+
echo " - Bash(npm install) - Only npm install"
|
|
1516
|
+
echo " - Bash(pnpm *) - All pnpm commands"
|
|
1517
|
+
echo " - Bash(yarn *) - All yarn commands"
|
|
1518
|
+
echo ""
|
|
1519
|
+
echo -e "${YELLOW}After updating .ralphrc:${NC}"
|
|
1520
|
+
echo " ralph --reset-session # Clear stale session state"
|
|
1521
|
+
echo " ralph --monitor # Restart the loop"
|
|
1522
|
+
echo ""
|
|
1523
|
+
|
|
1524
|
+
# Show current ALLOWED_TOOLS if .ralphrc exists
|
|
1525
|
+
if [[ -f ".ralphrc" ]]; then
|
|
1526
|
+
local current_tools=$(grep "^ALLOWED_TOOLS=" ".ralphrc" 2>/dev/null | cut -d= -f2- | tr -d '"')
|
|
1527
|
+
if [[ -n "$current_tools" ]]; then
|
|
1528
|
+
echo -e "${BLUE}Current ALLOWED_TOOLS:${NC} $current_tools"
|
|
1529
|
+
echo ""
|
|
1530
|
+
fi
|
|
1531
|
+
fi
|
|
1532
|
+
|
|
1533
|
+
break
|
|
1534
|
+
fi
|
|
1535
|
+
|
|
1536
|
+
log_status "SUCCESS" "🏁 Graceful exit triggered: $exit_reason"
|
|
1537
|
+
reset_session "project_complete"
|
|
1538
|
+
update_status "$loop_count" "$(cat "$CALL_COUNT_FILE")" "graceful_exit" "completed" "$exit_reason"
|
|
1539
|
+
|
|
1540
|
+
log_status "SUCCESS" "🎉 Ralph has completed the project! Final stats:"
|
|
1541
|
+
log_status "INFO" " - Total loops: $loop_count"
|
|
1542
|
+
log_status "INFO" " - API calls used: $(cat "$CALL_COUNT_FILE")"
|
|
1543
|
+
log_status "INFO" " - Exit reason: $exit_reason"
|
|
1544
|
+
|
|
1545
|
+
break
|
|
1546
|
+
fi
|
|
1547
|
+
|
|
1548
|
+
# Update status
|
|
1549
|
+
local calls_made=$(cat "$CALL_COUNT_FILE" 2>/dev/null || echo "0")
|
|
1550
|
+
update_status "$loop_count" "$calls_made" "executing" "running"
|
|
1551
|
+
|
|
1552
|
+
# Execute Claude Code
|
|
1553
|
+
execute_claude_code "$loop_count"
|
|
1554
|
+
local exec_result=$?
|
|
1555
|
+
|
|
1556
|
+
if [ $exec_result -eq 0 ]; then
|
|
1557
|
+
update_status "$loop_count" "$(cat "$CALL_COUNT_FILE")" "completed" "success"
|
|
1558
|
+
|
|
1559
|
+
# Brief pause between successful executions
|
|
1560
|
+
sleep 5
|
|
1561
|
+
elif [ $exec_result -eq 3 ]; then
|
|
1562
|
+
# Circuit breaker opened
|
|
1563
|
+
reset_session "circuit_breaker_trip"
|
|
1564
|
+
update_status "$loop_count" "$(cat "$CALL_COUNT_FILE")" "circuit_breaker_open" "halted" "stagnation_detected"
|
|
1565
|
+
log_status "ERROR" "🛑 Circuit breaker has opened - halting loop"
|
|
1566
|
+
log_status "INFO" "Run 'ralph --reset-circuit' to reset the circuit breaker after addressing issues"
|
|
1567
|
+
break
|
|
1568
|
+
elif [ $exec_result -eq 2 ]; then
|
|
1569
|
+
# API 5-hour limit reached - handle specially
|
|
1570
|
+
update_status "$loop_count" "$(cat "$CALL_COUNT_FILE")" "api_limit" "paused"
|
|
1571
|
+
log_status "WARN" "🛑 Claude API 5-hour limit reached!"
|
|
1572
|
+
|
|
1573
|
+
# Ask user whether to wait or exit
|
|
1574
|
+
echo -e "\n${YELLOW}The Claude API 5-hour usage limit has been reached.${NC}"
|
|
1575
|
+
echo -e "${YELLOW}You can either:${NC}"
|
|
1576
|
+
echo -e " ${GREEN}1)${NC} Wait for the limit to reset (usually within an hour)"
|
|
1577
|
+
echo -e " ${GREEN}2)${NC} Exit the loop and try again later"
|
|
1578
|
+
echo -e "\n${BLUE}Choose an option (1 or 2):${NC} "
|
|
1579
|
+
|
|
1580
|
+
# Read user input with timeout
|
|
1581
|
+
read -t 30 -n 1 user_choice
|
|
1582
|
+
echo # New line after input
|
|
1583
|
+
|
|
1584
|
+
if [[ "$user_choice" == "2" ]] || [[ -z "$user_choice" ]]; then
|
|
1585
|
+
log_status "INFO" "User chose to exit (or timed out). Exiting loop..."
|
|
1586
|
+
update_status "$loop_count" "$(cat "$CALL_COUNT_FILE")" "api_limit_exit" "stopped" "api_5hour_limit"
|
|
1587
|
+
break
|
|
1588
|
+
else
|
|
1589
|
+
log_status "INFO" "User chose to wait. Waiting for API limit reset..."
|
|
1590
|
+
# Wait for longer period when API limit is hit
|
|
1591
|
+
local wait_minutes=60
|
|
1592
|
+
log_status "INFO" "Waiting $wait_minutes minutes before retrying..."
|
|
1593
|
+
|
|
1594
|
+
# Countdown display
|
|
1595
|
+
local wait_seconds=$((wait_minutes * 60))
|
|
1596
|
+
while [[ $wait_seconds -gt 0 ]]; do
|
|
1597
|
+
local minutes=$((wait_seconds / 60))
|
|
1598
|
+
local seconds=$((wait_seconds % 60))
|
|
1599
|
+
printf "\r${YELLOW}Time until retry: %02d:%02d${NC}" $minutes $seconds
|
|
1600
|
+
sleep 1
|
|
1601
|
+
((wait_seconds--))
|
|
1602
|
+
done
|
|
1603
|
+
printf "\n"
|
|
1604
|
+
fi
|
|
1605
|
+
else
|
|
1606
|
+
update_status "$loop_count" "$(cat "$CALL_COUNT_FILE")" "failed" "error"
|
|
1607
|
+
log_status "WARN" "Execution failed, waiting 30 seconds before retry..."
|
|
1608
|
+
sleep 30
|
|
1609
|
+
fi
|
|
1610
|
+
|
|
1611
|
+
log_status "LOOP" "=== Completed Loop #$loop_count ==="
|
|
1612
|
+
done
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
# Help function
|
|
1616
|
+
show_help() {
|
|
1617
|
+
cat << HELPEOF
|
|
1618
|
+
Ralph Loop for Claude Code
|
|
1619
|
+
|
|
1620
|
+
Usage: $0 [OPTIONS]
|
|
1621
|
+
|
|
1622
|
+
IMPORTANT: This command must be run from a Ralph project directory.
|
|
1623
|
+
Use 'ralph-setup project-name' to create a new project first.
|
|
1624
|
+
|
|
1625
|
+
Options:
|
|
1626
|
+
-h, --help Show this help message
|
|
1627
|
+
-c, --calls NUM Set max calls per hour (default: $MAX_CALLS_PER_HOUR)
|
|
1628
|
+
-p, --prompt FILE Set prompt file (default: $PROMPT_FILE)
|
|
1629
|
+
-s, --status Show current status and exit
|
|
1630
|
+
-m, --monitor Start with tmux session and live monitor (requires tmux)
|
|
1631
|
+
-v, --verbose Show detailed progress updates during execution
|
|
1632
|
+
-l, --live Show Claude Code output in real-time (auto-switches to JSON output)
|
|
1633
|
+
-t, --timeout MIN Set Claude Code execution timeout in minutes (default: $CLAUDE_TIMEOUT_MINUTES)
|
|
1634
|
+
--reset-circuit Reset circuit breaker to CLOSED state
|
|
1635
|
+
--circuit-status Show circuit breaker status and exit
|
|
1636
|
+
--auto-reset-circuit Auto-reset circuit breaker on startup (bypasses cooldown)
|
|
1637
|
+
--reset-session Reset session state and exit (clears session continuity)
|
|
1638
|
+
|
|
1639
|
+
Modern CLI Options (Phase 1.1):
|
|
1640
|
+
--output-format FORMAT Set Claude output format: json or text (default: $CLAUDE_OUTPUT_FORMAT)
|
|
1641
|
+
Note: --live mode requires JSON and will auto-switch
|
|
1642
|
+
--allowed-tools TOOLS Comma-separated list of allowed tools (default: $CLAUDE_ALLOWED_TOOLS)
|
|
1643
|
+
--no-continue Disable session continuity across loops
|
|
1644
|
+
--session-expiry HOURS Set session expiration time in hours (default: $CLAUDE_SESSION_EXPIRY_HOURS)
|
|
1645
|
+
|
|
1646
|
+
Files created:
|
|
1647
|
+
- $LOG_DIR/: All execution logs
|
|
1648
|
+
- $DOCS_DIR/: Generated documentation
|
|
1649
|
+
- $STATUS_FILE: Current status (JSON)
|
|
1650
|
+
- .ralph/.ralph_session: Session lifecycle tracking
|
|
1651
|
+
- .ralph/.ralph_session_history: Session transition history (last 50)
|
|
1652
|
+
- .ralph/.call_count: API call counter for rate limiting
|
|
1653
|
+
- .ralph/.last_reset: Timestamp of last rate limit reset
|
|
1654
|
+
|
|
1655
|
+
Example workflow:
|
|
1656
|
+
ralph-setup my-project # Create project
|
|
1657
|
+
cd my-project # Enter project directory
|
|
1658
|
+
$0 --monitor # Start Ralph with monitoring
|
|
1659
|
+
|
|
1660
|
+
Examples:
|
|
1661
|
+
$0 --calls 50 --prompt my_prompt.md
|
|
1662
|
+
$0 --monitor # Start with integrated tmux monitoring
|
|
1663
|
+
$0 --live # Show Claude Code output in real-time (streaming)
|
|
1664
|
+
$0 --live --verbose # Live streaming + verbose logging
|
|
1665
|
+
$0 --monitor --timeout 30 # 30-minute timeout for complex tasks
|
|
1666
|
+
$0 --verbose --timeout 5 # 5-minute timeout with detailed progress
|
|
1667
|
+
$0 --output-format text # Use legacy text output format
|
|
1668
|
+
$0 --no-continue # Disable session continuity
|
|
1669
|
+
$0 --session-expiry 48 # 48-hour session expiration
|
|
1670
|
+
|
|
1671
|
+
HELPEOF
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
# Parse command line arguments
|
|
1675
|
+
while [[ $# -gt 0 ]]; do
|
|
1676
|
+
case $1 in
|
|
1677
|
+
-h|--help)
|
|
1678
|
+
show_help
|
|
1679
|
+
exit 0
|
|
1680
|
+
;;
|
|
1681
|
+
-c|--calls)
|
|
1682
|
+
MAX_CALLS_PER_HOUR="$2"
|
|
1683
|
+
shift 2
|
|
1684
|
+
;;
|
|
1685
|
+
-p|--prompt)
|
|
1686
|
+
PROMPT_FILE="$2"
|
|
1687
|
+
shift 2
|
|
1688
|
+
;;
|
|
1689
|
+
-s|--status)
|
|
1690
|
+
if [[ -f "$STATUS_FILE" ]]; then
|
|
1691
|
+
echo "Current Status:"
|
|
1692
|
+
cat "$STATUS_FILE" | jq . 2>/dev/null || cat "$STATUS_FILE"
|
|
1693
|
+
else
|
|
1694
|
+
echo "No status file found. Ralph may not be running."
|
|
1695
|
+
fi
|
|
1696
|
+
exit 0
|
|
1697
|
+
;;
|
|
1698
|
+
-m|--monitor)
|
|
1699
|
+
USE_TMUX=true
|
|
1700
|
+
shift
|
|
1701
|
+
;;
|
|
1702
|
+
-v|--verbose)
|
|
1703
|
+
VERBOSE_PROGRESS=true
|
|
1704
|
+
shift
|
|
1705
|
+
;;
|
|
1706
|
+
-l|--live)
|
|
1707
|
+
LIVE_OUTPUT=true
|
|
1708
|
+
shift
|
|
1709
|
+
;;
|
|
1710
|
+
-t|--timeout)
|
|
1711
|
+
if [[ "$2" =~ ^[1-9][0-9]*$ ]] && [[ "$2" -le 120 ]]; then
|
|
1712
|
+
CLAUDE_TIMEOUT_MINUTES="$2"
|
|
1713
|
+
else
|
|
1714
|
+
echo "Error: Timeout must be a positive integer between 1 and 120 minutes"
|
|
1715
|
+
exit 1
|
|
1716
|
+
fi
|
|
1717
|
+
shift 2
|
|
1718
|
+
;;
|
|
1719
|
+
--reset-circuit)
|
|
1720
|
+
# Source the circuit breaker library
|
|
1721
|
+
SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
|
|
1722
|
+
source "$SCRIPT_DIR/lib/circuit_breaker.sh"
|
|
1723
|
+
source "$SCRIPT_DIR/lib/date_utils.sh"
|
|
1724
|
+
reset_circuit_breaker "Manual reset via command line"
|
|
1725
|
+
reset_session "manual_circuit_reset"
|
|
1726
|
+
exit 0
|
|
1727
|
+
;;
|
|
1728
|
+
--reset-session)
|
|
1729
|
+
# Reset session state only
|
|
1730
|
+
SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
|
|
1731
|
+
source "$SCRIPT_DIR/lib/date_utils.sh"
|
|
1732
|
+
reset_session "manual_reset_flag"
|
|
1733
|
+
echo -e "\033[0;32m✅ Session state reset successfully\033[0m"
|
|
1734
|
+
exit 0
|
|
1735
|
+
;;
|
|
1736
|
+
--circuit-status)
|
|
1737
|
+
# Source the circuit breaker library
|
|
1738
|
+
SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
|
|
1739
|
+
source "$SCRIPT_DIR/lib/circuit_breaker.sh"
|
|
1740
|
+
show_circuit_status
|
|
1741
|
+
exit 0
|
|
1742
|
+
;;
|
|
1743
|
+
--output-format)
|
|
1744
|
+
if [[ "$2" == "json" || "$2" == "text" ]]; then
|
|
1745
|
+
CLAUDE_OUTPUT_FORMAT="$2"
|
|
1746
|
+
else
|
|
1747
|
+
echo "Error: --output-format must be 'json' or 'text'"
|
|
1748
|
+
exit 1
|
|
1749
|
+
fi
|
|
1750
|
+
shift 2
|
|
1751
|
+
;;
|
|
1752
|
+
--allowed-tools)
|
|
1753
|
+
if ! validate_allowed_tools "$2"; then
|
|
1754
|
+
exit 1
|
|
1755
|
+
fi
|
|
1756
|
+
CLAUDE_ALLOWED_TOOLS="$2"
|
|
1757
|
+
shift 2
|
|
1758
|
+
;;
|
|
1759
|
+
--no-continue)
|
|
1760
|
+
CLAUDE_USE_CONTINUE=false
|
|
1761
|
+
shift
|
|
1762
|
+
;;
|
|
1763
|
+
--session-expiry)
|
|
1764
|
+
if [[ -z "$2" || ! "$2" =~ ^[1-9][0-9]*$ ]]; then
|
|
1765
|
+
echo "Error: --session-expiry requires a positive integer (hours)"
|
|
1766
|
+
exit 1
|
|
1767
|
+
fi
|
|
1768
|
+
CLAUDE_SESSION_EXPIRY_HOURS="$2"
|
|
1769
|
+
shift 2
|
|
1770
|
+
;;
|
|
1771
|
+
--auto-reset-circuit)
|
|
1772
|
+
CB_AUTO_RESET=true
|
|
1773
|
+
shift
|
|
1774
|
+
;;
|
|
1775
|
+
*)
|
|
1776
|
+
echo "Unknown option: $1"
|
|
1777
|
+
show_help
|
|
1778
|
+
exit 1
|
|
1779
|
+
;;
|
|
1780
|
+
esac
|
|
1781
|
+
done
|
|
1782
|
+
|
|
1783
|
+
# Only execute when run directly, not when sourced
|
|
1784
|
+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
1785
|
+
# If tmux mode requested, set it up
|
|
1786
|
+
if [[ "$USE_TMUX" == "true" ]]; then
|
|
1787
|
+
check_tmux_available
|
|
1788
|
+
setup_tmux_session
|
|
1789
|
+
fi
|
|
1790
|
+
|
|
1791
|
+
# Start the main loop
|
|
1792
|
+
main
|
|
1793
|
+
fi
|