rafcode 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +32 -0
- package/CLAUDE.md +187 -0
- package/LICENSE +21 -0
- package/RAF/001-raf-task-improvements/input.md +9 -0
- package/RAF/001-raf-task-improvements/outcomes/001-add-decisions-folder.md +21 -0
- package/RAF/001-raf-task-improvements/outcomes/002-fix-write-error-on-shutdown.md +22 -0
- package/RAF/001-raf-task-improvements/outcomes/003-stash-changes-on-failure.md +34 -0
- package/RAF/001-raf-task-improvements/outcomes/004-add-project-name-to-commits.md +28 -0
- package/RAF/001-raf-task-improvements/outcomes/005-add-running-time-display.md +36 -0
- package/RAF/001-raf-task-improvements/outcomes/006-add-task-name-to-logs.md +22 -0
- package/RAF/001-raf-task-improvements/outcomes/007-show-model-at-task-start.md +52 -0
- package/RAF/001-raf-task-improvements/outcomes/009-remove-editor-placeholder-text.md +20 -0
- package/RAF/001-raf-task-improvements/outcomes/SUMMARY.md +83 -0
- package/RAF/001-raf-task-improvements/plans/001-add-decisions-folder.md +38 -0
- package/RAF/001-raf-task-improvements/plans/002-fix-write-error-on-shutdown.md +33 -0
- package/RAF/001-raf-task-improvements/plans/003-stash-changes-on-failure.md +37 -0
- package/RAF/001-raf-task-improvements/plans/004-add-project-name-to-commits.md +34 -0
- package/RAF/001-raf-task-improvements/plans/005-add-running-time-display.md +39 -0
- package/RAF/001-raf-task-improvements/plans/006-add-task-name-to-logs.md +37 -0
- package/RAF/001-raf-task-improvements/plans/009-remove-editor-placeholder-text.md +34 -0
- package/RAF/002-raf-task-improvements-execution/decisions/DECISIONS.md +13 -0
- package/RAF/002-raf-task-improvements-execution/input.md +3 -0
- package/RAF/002-raf-task-improvements-execution/outcomes/001-commit-show-model-at-task-start.md +17 -0
- package/RAF/002-raf-task-improvements-execution/outcomes/002-delete-skipped-plan.md +23 -0
- package/RAF/002-raf-task-improvements-execution/outcomes/SUMMARY.md +32 -0
- package/RAF/002-raf-task-improvements-execution/plans/001-commit-show-model-at-task-start.md +37 -0
- package/RAF/002-raf-task-improvements-execution/plans/002-delete-skipped-plan.md +23 -0
- package/RAF/003-multi-project-execution/decisions/DECISIONS.md +68 -0
- package/RAF/003-multi-project-execution/input.md +6 -0
- package/RAF/003-multi-project-execution/outcomes/001-remove-state-json.md +52 -0
- package/RAF/003-multi-project-execution/outcomes/002-update-raf-status.md +50 -0
- package/RAF/003-multi-project-execution/outcomes/003-simplify-git-logic.md +35 -0
- package/RAF/003-multi-project-execution/outcomes/004-auto-commit-planning.md +43 -0
- package/RAF/003-multi-project-execution/outcomes/005-rerun-failed-tasks.md +43 -0
- package/RAF/003-multi-project-execution/outcomes/006-multi-project-execution.md +42 -0
- package/RAF/003-multi-project-execution/outcomes/007-verify-timeout.md +54 -0
- package/RAF/003-multi-project-execution/outcomes/008-move-decisions-file.md +38 -0
- package/RAF/003-multi-project-execution/outcomes/SUMMARY.md +79 -0
- package/RAF/003-multi-project-execution/plans/001-remove-state-json.md +71 -0
- package/RAF/003-multi-project-execution/plans/002-update-raf-status.md +65 -0
- package/RAF/003-multi-project-execution/plans/003-simplify-git-logic.md +74 -0
- package/RAF/003-multi-project-execution/plans/004-auto-commit-planning.md +57 -0
- package/RAF/003-multi-project-execution/plans/005-rerun-failed-tasks.md +69 -0
- package/RAF/003-multi-project-execution/plans/006-multi-project-execution.md +81 -0
- package/RAF/003-multi-project-execution/plans/007-verify-timeout.md +63 -0
- package/RAF/003-multi-project-execution/plans/008-move-decisions-file.md +78 -0
- package/RAF/004-task-naming-optimization/decisions.md +22 -0
- package/RAF/004-task-naming-optimization/input.md +6 -0
- package/RAF/004-task-naming-optimization/outcomes/001-remove-summary-file.md +17 -0
- package/RAF/004-task-naming-optimization/outcomes/002-base36-project-numbering.md +32 -0
- package/RAF/004-task-naming-optimization/outcomes/003-improve-haiku-prompt.md +20 -0
- package/RAF/004-task-naming-optimization/outcomes/SUMMARY.md +28 -0
- package/RAF/004-task-naming-optimization/plans/001-remove-summary-file.md +34 -0
- package/RAF/004-task-naming-optimization/plans/002-base36-project-numbering.md +56 -0
- package/RAF/004-task-naming-optimization/plans/003-improve-haiku-prompt.md +50 -0
- package/RAF/005-task-naming-improvements/decisions.md +60 -0
- package/RAF/005-task-naming-improvements/input.md +2 -0
- package/RAF/005-task-naming-improvements/outcomes/001-enhance-identifier-resolution.md +42 -0
- package/RAF/005-task-naming-improvements/outcomes/002-add-identifier-support-to-status.md +38 -0
- package/RAF/005-task-naming-improvements/outcomes/003-update-do-for-full-folder-names.md +44 -0
- package/RAF/005-task-naming-improvements/outcomes/004-implement-amend-flag-for-plan.md +55 -0
- package/RAF/005-task-naming-improvements/outcomes/005-commit-outcomes-on-complete.md +47 -0
- package/RAF/005-task-naming-improvements/outcomes/006-update-execution-prompt-commit-schema.md +40 -0
- package/RAF/005-task-naming-improvements/outcomes/007-allow-pending-task-amendments.md +38 -0
- package/RAF/005-task-naming-improvements/outcomes/008-fix-timeout-label.md +24 -0
- package/RAF/005-task-naming-improvements/plans/001-enhance-identifier-resolution.md +46 -0
- package/RAF/005-task-naming-improvements/plans/002-add-identifier-support-to-status.md +36 -0
- package/RAF/005-task-naming-improvements/plans/003-update-do-for-full-folder-names.md +38 -0
- package/RAF/005-task-naming-improvements/plans/004-implement-amend-flag-for-plan.md +67 -0
- package/RAF/005-task-naming-improvements/plans/005-commit-outcomes-on-complete.md +86 -0
- package/RAF/005-task-naming-improvements/plans/006-update-execution-prompt-commit-schema.md +60 -0
- package/RAF/005-task-naming-improvements/plans/007-allow-pending-task-amendments.md +60 -0
- package/RAF/005-task-naming-improvements/plans/008-fix-timeout-label.md +31 -0
- package/RAF/006-fix-double-summary-headers/decisions.md +28 -0
- package/RAF/006-fix-double-summary-headers/input.md +3 -0
- package/RAF/006-fix-double-summary-headers/outcomes/001-fix-double-summary-headers.md +29 -0
- package/RAF/006-fix-double-summary-headers/outcomes/002-update-readme-for-npm.md +31 -0
- package/RAF/006-fix-double-summary-headers/outcomes/003-npm-publish-instructions.md +30 -0
- package/RAF/006-fix-double-summary-headers/outcomes/004-flexible-project-lookup.md +47 -0
- package/RAF/006-fix-double-summary-headers/plans/001-fix-double-summary-headers.md +42 -0
- package/RAF/006-fix-double-summary-headers/plans/002-update-readme-for-npm.md +44 -0
- package/RAF/006-fix-double-summary-headers/plans/003-npm-publish-instructions.md +45 -0
- package/RAF/006-fix-double-summary-headers/plans/004-flexible-project-lookup.md +40 -0
- package/RAF/007-improve-outcome-format/decisions.md +28 -0
- package/RAF/007-improve-outcome-format/input.md +2 -0
- package/RAF/007-improve-outcome-format/outcomes/001-update-execution-prompt.md +10 -0
- package/RAF/007-improve-outcome-format/outcomes/002-update-state-derivation.md +17 -0
- package/RAF/007-improve-outcome-format/outcomes/003-update-do-command-outcome-handling.md +16 -0
- package/RAF/007-improve-outcome-format/outcomes/004-implement-failure-analysis.md +16 -0
- package/RAF/007-improve-outcome-format/outcomes/005-update-documentation.md +15 -0
- package/RAF/007-improve-outcome-format/plans/001-update-execution-prompt.md +36 -0
- package/RAF/007-improve-outcome-format/plans/002-update-state-derivation.md +35 -0
- package/RAF/007-improve-outcome-format/plans/003-update-do-command-outcome-handling.md +37 -0
- package/RAF/007-improve-outcome-format/plans/004-implement-failure-analysis.md +44 -0
- package/RAF/007-improve-outcome-format/plans/005-update-documentation.md +33 -0
- package/RAF/008-beautiful-do/decisions.md +31 -0
- package/RAF/008-beautiful-do/input.md +1 -0
- package/RAF/008-beautiful-do/outcomes/001-terminal-symbols.md +55 -0
- package/RAF/008-beautiful-do/outcomes/002-refactor-do-output.md +95 -0
- package/RAF/008-beautiful-do/outcomes/003-refactor-status-output.md +71 -0
- package/RAF/008-beautiful-do/outcomes/004-simplify-logger.md +53 -0
- package/RAF/008-beautiful-do/outcomes/005-add-tests.md +41 -0
- package/RAF/008-beautiful-do/plans/001-terminal-symbols.md +41 -0
- package/RAF/008-beautiful-do/plans/002-refactor-do-output.md +44 -0
- package/RAF/008-beautiful-do/plans/003-refactor-status-output.md +37 -0
- package/RAF/008-beautiful-do/plans/004-simplify-logger.md +32 -0
- package/RAF/008-beautiful-do/plans/005-add-tests.md +40 -0
- package/RAF/009-system-promt-ammend/decisions.md +13 -0
- package/RAF/009-system-promt-ammend/input.md +9 -0
- package/RAF/009-system-promt-ammend/outcomes/001-model-override.md +79 -0
- package/RAF/009-system-promt-ammend/outcomes/002-system-prompt-append.md +51 -0
- package/RAF/009-system-promt-ammend/outcomes/003-retry-context.md +60 -0
- package/RAF/009-system-promt-ammend/plans/001-model-override.md +61 -0
- package/RAF/009-system-promt-ammend/plans/002-system-prompt-append.md +56 -0
- package/RAF/009-system-promt-ammend/plans/003-retry-context.md +76 -0
- package/RAF/010-outcome-marker-fallback/decisions.md +19 -0
- package/RAF/010-outcome-marker-fallback/input.md +1 -0
- package/RAF/010-outcome-marker-fallback/outcomes/001-outcome-file-marker-fallback.md +35 -0
- package/RAF/010-outcome-marker-fallback/outcomes/002-creative-project-naming.md +47 -0
- package/RAF/010-outcome-marker-fallback/plans/001-outcome-file-marker-fallback.md +58 -0
- package/RAF/010-outcome-marker-fallback/plans/002-creative-project-naming.md +68 -0
- package/RAF/011-do-task-in-commit/decisions.md +22 -0
- package/RAF/011-do-task-in-commit/input.md +1 -0
- package/RAF/011-do-task-in-commit/outcomes/001-update-execution-prompt.md +54 -0
- package/RAF/011-do-task-in-commit/outcomes/002-update-tests.md +61 -0
- package/RAF/011-do-task-in-commit/outcomes/003-update-documentation.md +51 -0
- package/RAF/011-do-task-in-commit/plans/001-update-execution-prompt.md +46 -0
- package/RAF/011-do-task-in-commit/plans/002-update-tests.md +51 -0
- package/RAF/011-do-task-in-commit/plans/003-update-documentation.md +45 -0
- package/RAF/012-name-picker-buffet/decisions.md +40 -0
- package/RAF/012-name-picker-buffet/input.md +6 -0
- package/RAF/012-name-picker-buffet/outcomes/001-name-picker-for-raf-plan.md +49 -0
- package/RAF/012-name-picker-buffet/outcomes/002-interactive-project-picker-for-raf-do.md +49 -0
- package/RAF/012-name-picker-buffet/outcomes/003-raf-status-truncation.md +55 -0
- package/RAF/012-name-picker-buffet/outcomes/004-failure-reason-details.md +65 -0
- package/RAF/012-name-picker-buffet/outcomes/005-remove-raf-commits.md +57 -0
- package/RAF/012-name-picker-buffet/outcomes/006-update-execution-prompt-for-commits.md +47 -0
- package/RAF/012-name-picker-buffet/outcomes/007-fix-plan-mode-user-prompt.md +83 -0
- package/RAF/012-name-picker-buffet/outcomes/008-add-auto-flag-for-plan-mode.md +77 -0
- package/RAF/012-name-picker-buffet/plans/001-name-picker-for-raf-plan.md +47 -0
- package/RAF/012-name-picker-buffet/plans/002-interactive-project-picker-for-raf-do.md +43 -0
- package/RAF/012-name-picker-buffet/plans/003-raf-status-truncation.md +36 -0
- package/RAF/012-name-picker-buffet/plans/004-failure-reason-details.md +46 -0
- package/RAF/012-name-picker-buffet/plans/005-remove-raf-commits.md +42 -0
- package/RAF/012-name-picker-buffet/plans/006-update-execution-prompt-for-commits.md +47 -0
- package/RAF/012-name-picker-buffet/plans/007-fix-plan-mode-user-prompt.md +55 -0
- package/RAF/012-name-picker-buffet/plans/008-add-auto-flag-for-plan-mode.md +49 -0
- package/RAF/013-dependencies-watchdog/decisions.md +37 -0
- package/RAF/013-dependencies-watchdog/input.md +1 -0
- package/RAF/013-dependencies-watchdog/outcomes/001-define-dependency-syntax.md +56 -0
- package/RAF/013-dependencies-watchdog/outcomes/002-update-planning-prompts.md +60 -0
- package/RAF/013-dependencies-watchdog/outcomes/003-parse-dependencies-update-state.md +81 -0
- package/RAF/013-dependencies-watchdog/outcomes/004-implement-dependency-checking-in-do.md +116 -0
- package/RAF/013-dependencies-watchdog/outcomes/005-update-execution-prompts.md +75 -0
- package/RAF/013-dependencies-watchdog/outcomes/006-add-tests.md +100 -0
- package/RAF/013-dependencies-watchdog/outcomes/007-add-act-alias.md +46 -0
- package/RAF/013-dependencies-watchdog/outcomes/008-add-exit-message.md +52 -0
- package/RAF/013-dependencies-watchdog/plans/001-define-dependency-syntax.md +32 -0
- package/RAF/013-dependencies-watchdog/plans/002-update-planning-prompts.md +38 -0
- package/RAF/013-dependencies-watchdog/plans/003-parse-dependencies-update-state.md +46 -0
- package/RAF/013-dependencies-watchdog/plans/004-implement-dependency-checking-in-do.md +48 -0
- package/RAF/013-dependencies-watchdog/plans/005-update-execution-prompts.md +44 -0
- package/RAF/013-dependencies-watchdog/plans/006-add-tests.md +54 -0
- package/RAF/013-dependencies-watchdog/plans/007-add-act-alias.md +26 -0
- package/RAF/013-dependencies-watchdog/plans/008-add-exit-message.md +31 -0
- package/RAF/014-watchdog/decisions.md +16 -0
- package/RAF/014-watchdog/input.md +2 -0
- package/RAF/014-watchdog/outcomes/001-amend-flag-position.md +50 -0
- package/RAF/014-watchdog/outcomes/002-details-only-on-failure.md +58 -0
- package/RAF/014-watchdog/plans/001-amend-flag-position.md +34 -0
- package/RAF/014-watchdog/plans/002-details-only-on-failure.md +46 -0
- package/RAF/015-name-lottery/decisions.md +14 -0
- package/RAF/015-name-lottery/input.md +3 -0
- package/RAF/015-name-lottery/outcomes/001-auto-pick-project-name.md +31 -0
- package/RAF/015-name-lottery/outcomes/002-mention-plan-files-in-commit.md +23 -0
- package/RAF/015-name-lottery/outcomes/003-fix-input-md-in-amend-flow.md +44 -0
- package/RAF/015-name-lottery/plans/001-auto-pick-project-name.md +38 -0
- package/RAF/015-name-lottery/plans/002-mention-plan-files-in-commit.md +32 -0
- package/RAF/015-name-lottery/plans/003-fix-input-md-in-amend-flow.md +44 -0
- package/README.md +116 -0
- package/dist/commands/do.d.ts +12 -0
- package/dist/commands/do.d.ts.map +1 -0
- package/dist/commands/do.js +684 -0
- package/dist/commands/do.js.map +1 -0
- package/dist/commands/plan.d.ts +3 -0
- package/dist/commands/plan.d.ts.map +1 -0
- package/dist/commands/plan.js +345 -0
- package/dist/commands/plan.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +117 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/core/claude-runner.d.ts +78 -0
- package/dist/core/claude-runner.d.ts.map +1 -0
- package/dist/core/claude-runner.js +297 -0
- package/dist/core/claude-runner.js.map +1 -0
- package/dist/core/editor.d.ts +10 -0
- package/dist/core/editor.d.ts.map +1 -0
- package/dist/core/editor.js +77 -0
- package/dist/core/editor.js.map +1 -0
- package/dist/core/failure-analyzer.d.ts +28 -0
- package/dist/core/failure-analyzer.d.ts.map +1 -0
- package/dist/core/failure-analyzer.js +305 -0
- package/dist/core/failure-analyzer.js.map +1 -0
- package/dist/core/git.d.ts +42 -0
- package/dist/core/git.d.ts.map +1 -0
- package/dist/core/git.js +148 -0
- package/dist/core/git.js.map +1 -0
- package/dist/core/project-manager.d.ts +72 -0
- package/dist/core/project-manager.d.ts.map +1 -0
- package/dist/core/project-manager.js +193 -0
- package/dist/core/project-manager.js.map +1 -0
- package/dist/core/retry-handler.d.ts +19 -0
- package/dist/core/retry-handler.d.ts.map +1 -0
- package/dist/core/retry-handler.js +51 -0
- package/dist/core/retry-handler.js.map +1 -0
- package/dist/core/shutdown-handler.d.ts +30 -0
- package/dist/core/shutdown-handler.d.ts.map +1 -0
- package/dist/core/shutdown-handler.js +79 -0
- package/dist/core/shutdown-handler.js.map +1 -0
- package/dist/core/state-derivation.d.ts +82 -0
- package/dist/core/state-derivation.d.ts.map +1 -0
- package/dist/core/state-derivation.js +271 -0
- package/dist/core/state-derivation.js.map +1 -0
- package/dist/core/state-manager.d.ts +54 -0
- package/dist/core/state-manager.d.ts.map +1 -0
- package/dist/core/state-manager.js +198 -0
- package/dist/core/state-manager.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/output-parser.d.ts +19 -0
- package/dist/parsers/output-parser.d.ts.map +1 -0
- package/dist/parsers/output-parser.js +137 -0
- package/dist/parsers/output-parser.js.map +1 -0
- package/dist/prompts/amend.d.ts +20 -0
- package/dist/prompts/amend.d.ts.map +1 -0
- package/dist/prompts/amend.js +166 -0
- package/dist/prompts/amend.js.map +1 -0
- package/dist/prompts/execution.d.ts +30 -0
- package/dist/prompts/execution.d.ts.map +1 -0
- package/dist/prompts/execution.js +179 -0
- package/dist/prompts/execution.js.map +1 -0
- package/dist/prompts/planning.d.ts +15 -0
- package/dist/prompts/planning.d.ts.map +1 -0
- package/dist/prompts/planning.js +163 -0
- package/dist/prompts/planning.js.map +1 -0
- package/dist/types/config.d.ts +26 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +7 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/state.d.ts +33 -0
- package/dist/types/state.d.ts.map +1 -0
- package/dist/types/state.js +28 -0
- package/dist/types/state.js.map +1 -0
- package/dist/ui/name-picker-subprocess.d.ts +11 -0
- package/dist/ui/name-picker-subprocess.d.ts.map +1 -0
- package/dist/ui/name-picker-subprocess.js +83 -0
- package/dist/ui/name-picker-subprocess.js.map +1 -0
- package/dist/ui/name-picker.d.ts +19 -0
- package/dist/ui/name-picker.d.ts.map +1 -0
- package/dist/ui/name-picker.js +173 -0
- package/dist/ui/name-picker.js.map +1 -0
- package/dist/ui/project-picker.d.ts +27 -0
- package/dist/ui/project-picker.d.ts.map +1 -0
- package/dist/ui/project-picker.js +58 -0
- package/dist/ui/project-picker.js.map +1 -0
- package/dist/utils/config.d.ts +24 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +63 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/logger.d.ts +32 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +60 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/name-generator.d.ts +20 -0
- package/dist/utils/name-generator.d.ts.map +1 -0
- package/dist/utils/name-generator.js +183 -0
- package/dist/utils/name-generator.js.map +1 -0
- package/dist/utils/paths.d.ts +132 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +412 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/status-line.d.ts +14 -0
- package/dist/utils/status-line.d.ts.map +1 -0
- package/dist/utils/status-line.js +36 -0
- package/dist/utils/status-line.js.map +1 -0
- package/dist/utils/terminal-symbols.d.ts +50 -0
- package/dist/utils/terminal-symbols.d.ts.map +1 -0
- package/dist/utils/terminal-symbols.js +97 -0
- package/dist/utils/terminal-symbols.js.map +1 -0
- package/dist/utils/timer.d.ts +17 -0
- package/dist/utils/timer.d.ts.map +1 -0
- package/dist/utils/timer.js +56 -0
- package/dist/utils/timer.js.map +1 -0
- package/dist/utils/validation.d.ts +17 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +106 -0
- package/dist/utils/validation.js.map +1 -0
- package/dist/utils/version.d.ts +2 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +12 -0
- package/dist/utils/version.js.map +1 -0
- package/jest.config.ts +30 -0
- package/package.json +55 -0
- package/src/commands/do.ts +829 -0
- package/src/commands/plan.ts +422 -0
- package/src/commands/status.ts +146 -0
- package/src/core/claude-runner.ts +374 -0
- package/src/core/editor.ts +85 -0
- package/src/core/failure-analyzer.ts +372 -0
- package/src/core/git.ts +166 -0
- package/src/core/project-manager.ts +243 -0
- package/src/core/retry-handler.ts +72 -0
- package/src/core/shutdown-handler.ts +93 -0
- package/src/core/state-derivation.ts +343 -0
- package/src/index.ts +20 -0
- package/src/parsers/output-parser.ts +164 -0
- package/src/prompts/amend.ts +194 -0
- package/src/prompts/execution.ts +223 -0
- package/src/prompts/planning.ts +175 -0
- package/src/types/config.ts +35 -0
- package/src/ui/name-picker-subprocess.ts +96 -0
- package/src/ui/name-picker.ts +198 -0
- package/src/ui/project-picker.ts +80 -0
- package/src/utils/config.ts +69 -0
- package/src/utils/logger.ts +81 -0
- package/src/utils/name-generator.ts +211 -0
- package/src/utils/paths.ts +497 -0
- package/src/utils/status-line.ts +45 -0
- package/src/utils/terminal-symbols.ts +124 -0
- package/src/utils/timer.ts +64 -0
- package/src/utils/validation.ts +132 -0
- package/src/utils/version.ts +12 -0
- package/tests/unit/claude-runner-interactive.test.ts +343 -0
- package/tests/unit/claude-runner.test.ts +629 -0
- package/tests/unit/command-output.test.ts +295 -0
- package/tests/unit/config.test.ts +72 -0
- package/tests/unit/dependency-integration.test.ts +559 -0
- package/tests/unit/do-blocked-tasks.test.ts +323 -0
- package/tests/unit/do-command.test.ts +198 -0
- package/tests/unit/do-multiproject.test.ts +270 -0
- package/tests/unit/do-rerun.test.ts +270 -0
- package/tests/unit/execution-prompt.test.ts +406 -0
- package/tests/unit/failure-analyzer.test.ts +276 -0
- package/tests/unit/failure-history.test.ts +143 -0
- package/tests/unit/git-stash.test.ts +138 -0
- package/tests/unit/git.test.ts +80 -0
- package/tests/unit/logger.test.ts +132 -0
- package/tests/unit/name-generator.test.ts +283 -0
- package/tests/unit/name-picker.test.ts +179 -0
- package/tests/unit/outcome-content.test.ts +166 -0
- package/tests/unit/output-parser.test.ts +178 -0
- package/tests/unit/paths.test.ts +741 -0
- package/tests/unit/plan-command-amend-flag.test.ts +115 -0
- package/tests/unit/plan-command-amend-input.test.ts +156 -0
- package/tests/unit/plan-command-auto-flag.test.ts +112 -0
- package/tests/unit/plan-command.test.ts +580 -0
- package/tests/unit/planning-prompt.test.ts +137 -0
- package/tests/unit/project-manager.test.ts +265 -0
- package/tests/unit/project-picker.test.ts +338 -0
- package/tests/unit/retry-handler.test.ts +89 -0
- package/tests/unit/state-derivation.test.ts +714 -0
- package/tests/unit/status-command.test.ts +271 -0
- package/tests/unit/status-line.test.ts +92 -0
- package/tests/unit/terminal-symbols.test.ts +214 -0
- package/tests/unit/timer.test.ts +102 -0
- package/tests/unit/validation.test.ts +118 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { getPlansDir, getOutcomesDir, getInputPath } from '../utils/paths.js';
|
|
4
|
+
|
|
5
|
+
export type DerivedTaskStatus = 'pending' | 'completed' | 'failed' | 'blocked';
|
|
6
|
+
|
|
7
|
+
export type DerivedProjectStatus =
|
|
8
|
+
| 'planning'
|
|
9
|
+
| 'ready'
|
|
10
|
+
| 'executing'
|
|
11
|
+
| 'completed'
|
|
12
|
+
| 'failed';
|
|
13
|
+
|
|
14
|
+
export interface DerivedTask {
|
|
15
|
+
id: string;
|
|
16
|
+
planFile: string;
|
|
17
|
+
status: DerivedTaskStatus;
|
|
18
|
+
dependencies: string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface DerivedProjectState {
|
|
22
|
+
tasks: DerivedTask[];
|
|
23
|
+
status: DerivedProjectStatus;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface DiscoveredProject {
|
|
27
|
+
number: number;
|
|
28
|
+
name: string;
|
|
29
|
+
path: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface DerivedStats {
|
|
33
|
+
pending: number;
|
|
34
|
+
completed: number;
|
|
35
|
+
failed: number;
|
|
36
|
+
blocked: number;
|
|
37
|
+
total: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Discover all projects in the RAF directory.
|
|
42
|
+
* Projects are directories matching the pattern NNN-project-name.
|
|
43
|
+
*/
|
|
44
|
+
export function discoverProjects(rafDir: string): DiscoveredProject[] {
|
|
45
|
+
if (!fs.existsSync(rafDir)) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const entries = fs.readdirSync(rafDir, { withFileTypes: true });
|
|
50
|
+
const projects: DiscoveredProject[] = [];
|
|
51
|
+
|
|
52
|
+
for (const entry of entries) {
|
|
53
|
+
if (entry.isDirectory()) {
|
|
54
|
+
const match = entry.name.match(/^(\d{2,3})-(.+)$/);
|
|
55
|
+
if (match && match[1] && match[2]) {
|
|
56
|
+
projects.push({
|
|
57
|
+
number: parseInt(match[1], 10),
|
|
58
|
+
name: match[2],
|
|
59
|
+
path: path.join(rafDir, entry.name),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return projects.sort((a, b) => a.number - b.number);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Parse the Dependencies section from plan file content.
|
|
70
|
+
* Format: `## Dependencies\n001, 002` → ["001", "002"]
|
|
71
|
+
* Returns empty array if no Dependencies section exists.
|
|
72
|
+
*/
|
|
73
|
+
export function parseDependencies(content: string): string[] {
|
|
74
|
+
const dependenciesMatch = content.match(/^## Dependencies\s*\n([^\n#]+)/m);
|
|
75
|
+
if (!dependenciesMatch || !dependenciesMatch[1]) {
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const dependenciesLine = dependenciesMatch[1].trim();
|
|
80
|
+
if (!dependenciesLine) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Parse comma-separated task IDs
|
|
85
|
+
return dependenciesLine
|
|
86
|
+
.split(',')
|
|
87
|
+
.map((id) => id.trim())
|
|
88
|
+
.filter((id) => /^\d{2,3}$/.test(id));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Parse an outcome file to extract its status.
|
|
93
|
+
* Status is determined by the presence of promise markers:
|
|
94
|
+
* - `<promise>COMPLETE</promise>` → completed
|
|
95
|
+
* - `<promise>FAILED</promise>` → failed
|
|
96
|
+
* - `<promise>BLOCKED</promise>` → blocked
|
|
97
|
+
* Uses the last occurrence if multiple markers exist.
|
|
98
|
+
*/
|
|
99
|
+
export function parseOutcomeStatus(content: string): DerivedTaskStatus | null {
|
|
100
|
+
const markerRegex = /<promise>(COMPLETE|FAILED|BLOCKED)<\/promise>/g;
|
|
101
|
+
let lastMatch: RegExpExecArray | null = null;
|
|
102
|
+
let match: RegExpExecArray | null;
|
|
103
|
+
|
|
104
|
+
while ((match = markerRegex.exec(content)) !== null) {
|
|
105
|
+
lastMatch = match;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (lastMatch && lastMatch[1]) {
|
|
109
|
+
switch (lastMatch[1]) {
|
|
110
|
+
case 'COMPLETE':
|
|
111
|
+
return 'completed';
|
|
112
|
+
case 'FAILED':
|
|
113
|
+
return 'failed';
|
|
114
|
+
case 'BLOCKED':
|
|
115
|
+
return 'blocked';
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Derive project status from tasks.
|
|
123
|
+
* - planning: has input.md but no plans folder or empty plans
|
|
124
|
+
* - ready: has plan files but no outcome files
|
|
125
|
+
* - executing: has some outcome files but not all (or has failed outcomes without SUCCESS)
|
|
126
|
+
* - completed: all plan files have corresponding SUCCESS outcomes
|
|
127
|
+
* - failed: has FAILED outcome files
|
|
128
|
+
*/
|
|
129
|
+
export function deriveProjectStatus(
|
|
130
|
+
projectPath: string,
|
|
131
|
+
tasks: DerivedTask[]
|
|
132
|
+
): DerivedProjectStatus {
|
|
133
|
+
const plansDir = getPlansDir(projectPath);
|
|
134
|
+
const inputPath = getInputPath(projectPath);
|
|
135
|
+
|
|
136
|
+
const hasInput = fs.existsSync(inputPath);
|
|
137
|
+
const hasPlansDir = fs.existsSync(plansDir);
|
|
138
|
+
|
|
139
|
+
// If no plans directory or empty plans
|
|
140
|
+
if (!hasPlansDir || tasks.length === 0) {
|
|
141
|
+
return hasInput ? 'planning' : 'planning';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const stats = {
|
|
145
|
+
pending: 0,
|
|
146
|
+
completed: 0,
|
|
147
|
+
failed: 0,
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
for (const task of tasks) {
|
|
151
|
+
if (task.status === 'pending') stats.pending++;
|
|
152
|
+
else if (task.status === 'completed') stats.completed++;
|
|
153
|
+
else if (task.status === 'failed') stats.failed++;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// If any task failed, project is failed
|
|
157
|
+
if (stats.failed > 0) {
|
|
158
|
+
return 'failed';
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// If all tasks completed, project is completed
|
|
162
|
+
if (stats.completed === tasks.length && tasks.length > 0) {
|
|
163
|
+
return 'completed';
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// If no tasks have been started (all pending), project is ready
|
|
167
|
+
if (stats.pending === tasks.length) {
|
|
168
|
+
return 'ready';
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Otherwise, project is executing
|
|
172
|
+
return 'executing';
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Derive project state from the folder structure.
|
|
177
|
+
* Scans plans/ for plan files and outcomes/ for outcome files.
|
|
178
|
+
* Matches them by task ID (NNN prefix) and determines status.
|
|
179
|
+
* Also parses dependencies and derives blocked status.
|
|
180
|
+
*/
|
|
181
|
+
export function deriveProjectState(projectPath: string): DerivedProjectState {
|
|
182
|
+
const plansDir = getPlansDir(projectPath);
|
|
183
|
+
const outcomesDir = getOutcomesDir(projectPath);
|
|
184
|
+
|
|
185
|
+
const tasks: DerivedTask[] = [];
|
|
186
|
+
|
|
187
|
+
// Scan plans directory for plan files
|
|
188
|
+
if (!fs.existsSync(plansDir)) {
|
|
189
|
+
const status = deriveProjectStatus(projectPath, tasks);
|
|
190
|
+
return { tasks, status };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const planFiles = fs.readdirSync(plansDir)
|
|
194
|
+
.filter((f) => f.endsWith('.md'))
|
|
195
|
+
.sort();
|
|
196
|
+
|
|
197
|
+
// Build a map of outcome statuses
|
|
198
|
+
const outcomeStatuses = new Map<string, DerivedTaskStatus>();
|
|
199
|
+
if (fs.existsSync(outcomesDir)) {
|
|
200
|
+
const outcomeFiles = fs.readdirSync(outcomesDir)
|
|
201
|
+
.filter((f) => f.endsWith('.md'))
|
|
202
|
+
.sort();
|
|
203
|
+
|
|
204
|
+
for (const outcomeFile of outcomeFiles) {
|
|
205
|
+
const match = outcomeFile.match(/^(\d{2,3})-/);
|
|
206
|
+
if (match && match[1]) {
|
|
207
|
+
const taskId = match[1];
|
|
208
|
+
const content = fs.readFileSync(path.join(outcomesDir, outcomeFile), 'utf-8');
|
|
209
|
+
const status = parseOutcomeStatus(content);
|
|
210
|
+
if (status) {
|
|
211
|
+
outcomeStatuses.set(taskId, status);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// First pass: Match plan files to outcomes and parse dependencies
|
|
218
|
+
for (const planFile of planFiles) {
|
|
219
|
+
const match = planFile.match(/^(\d{2,3})-(.+)\.md$/);
|
|
220
|
+
if (match && match[1]) {
|
|
221
|
+
const taskId = match[1];
|
|
222
|
+
const status = outcomeStatuses.get(taskId) ?? 'pending';
|
|
223
|
+
|
|
224
|
+
// Read plan file to extract dependencies
|
|
225
|
+
const planContent = fs.readFileSync(path.join(plansDir, planFile), 'utf-8');
|
|
226
|
+
const dependencies = parseDependencies(planContent);
|
|
227
|
+
|
|
228
|
+
tasks.push({
|
|
229
|
+
id: taskId,
|
|
230
|
+
planFile: path.join('plans', planFile),
|
|
231
|
+
status,
|
|
232
|
+
dependencies,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Second pass: Derive blocked status for pending tasks
|
|
238
|
+
// A task is blocked if any of its dependencies are failed or blocked
|
|
239
|
+
const taskStatusMap = new Map<string, DerivedTaskStatus>();
|
|
240
|
+
for (const task of tasks) {
|
|
241
|
+
taskStatusMap.set(task.id, task.status);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Process blocked status (may need multiple passes for transitive blocking)
|
|
245
|
+
let changed = true;
|
|
246
|
+
while (changed) {
|
|
247
|
+
changed = false;
|
|
248
|
+
for (const task of tasks) {
|
|
249
|
+
if (task.status === 'pending' && task.dependencies.length > 0) {
|
|
250
|
+
const isBlocked = task.dependencies.some((depId) => {
|
|
251
|
+
const depStatus = taskStatusMap.get(depId);
|
|
252
|
+
return depStatus === 'failed' || depStatus === 'blocked';
|
|
253
|
+
});
|
|
254
|
+
if (isBlocked) {
|
|
255
|
+
task.status = 'blocked';
|
|
256
|
+
taskStatusMap.set(task.id, 'blocked');
|
|
257
|
+
changed = true;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const projectStatus = deriveProjectStatus(projectPath, tasks);
|
|
264
|
+
return { tasks, status: projectStatus };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Get the next pending task from derived state.
|
|
269
|
+
*/
|
|
270
|
+
export function getNextPendingTask(state: DerivedProjectState): DerivedTask | null {
|
|
271
|
+
for (const task of state.tasks) {
|
|
272
|
+
if (task.status === 'pending') {
|
|
273
|
+
return task;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Get the next task that should be executed (pending or failed).
|
|
281
|
+
* Skips blocked tasks - they cannot be executed until their dependencies pass.
|
|
282
|
+
*/
|
|
283
|
+
export function getNextExecutableTask(state: DerivedProjectState): DerivedTask | null {
|
|
284
|
+
// First try pending tasks (blocked tasks have status 'blocked', not 'pending')
|
|
285
|
+
for (const task of state.tasks) {
|
|
286
|
+
if (task.status === 'pending') {
|
|
287
|
+
return task;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// Then try failed tasks (for retry)
|
|
291
|
+
for (const task of state.tasks) {
|
|
292
|
+
if (task.status === 'failed') {
|
|
293
|
+
return task;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Calculate statistics from derived state.
|
|
301
|
+
*/
|
|
302
|
+
export function getDerivedStats(state: DerivedProjectState): DerivedStats {
|
|
303
|
+
const stats: DerivedStats = {
|
|
304
|
+
pending: 0,
|
|
305
|
+
completed: 0,
|
|
306
|
+
failed: 0,
|
|
307
|
+
blocked: 0,
|
|
308
|
+
total: state.tasks.length,
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
for (const task of state.tasks) {
|
|
312
|
+
switch (task.status) {
|
|
313
|
+
case 'pending':
|
|
314
|
+
stats.pending++;
|
|
315
|
+
break;
|
|
316
|
+
case 'completed':
|
|
317
|
+
stats.completed++;
|
|
318
|
+
break;
|
|
319
|
+
case 'failed':
|
|
320
|
+
stats.failed++;
|
|
321
|
+
break;
|
|
322
|
+
case 'blocked':
|
|
323
|
+
stats.blocked++;
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return stats;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Check if all tasks are completed.
|
|
333
|
+
*/
|
|
334
|
+
export function isProjectComplete(state: DerivedProjectState): boolean {
|
|
335
|
+
return state.tasks.every((t) => t.status === 'completed');
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Check if any task has failed.
|
|
340
|
+
*/
|
|
341
|
+
export function hasProjectFailed(state: DerivedProjectState): boolean {
|
|
342
|
+
return state.tasks.some((t) => t.status === 'failed');
|
|
343
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { createPlanCommand } from './commands/plan.js';
|
|
5
|
+
import { createDoCommand } from './commands/do.js';
|
|
6
|
+
import { createStatusCommand } from './commands/status.js';
|
|
7
|
+
import { getVersion } from './utils/version.js';
|
|
8
|
+
|
|
9
|
+
const program = new Command();
|
|
10
|
+
|
|
11
|
+
program
|
|
12
|
+
.name('raf')
|
|
13
|
+
.description('RAF - Automated Task Planning & Execution with Claude Code')
|
|
14
|
+
.version(getVersion());
|
|
15
|
+
|
|
16
|
+
program.addCommand(createPlanCommand());
|
|
17
|
+
program.addCommand(createDoCommand());
|
|
18
|
+
program.addCommand(createStatusCommand());
|
|
19
|
+
|
|
20
|
+
program.parse();
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
export type TaskResult = 'complete' | 'failed' | 'unknown';
|
|
2
|
+
|
|
3
|
+
export interface ParsedOutput {
|
|
4
|
+
result: TaskResult;
|
|
5
|
+
failureReason?: string;
|
|
6
|
+
contextOverflow: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const COMPLETE_PATTERN = /<promise>COMPLETE<\/promise>/i;
|
|
10
|
+
const FAILED_PATTERN = /<promise>FAILED<\/promise>/i;
|
|
11
|
+
const FAILURE_REASON_PATTERN = /Reason:\s*(.+?)(?:\n|$)/i;
|
|
12
|
+
|
|
13
|
+
const CONTEXT_OVERFLOW_PATTERNS = [
|
|
14
|
+
/context length exceeded/i,
|
|
15
|
+
/token limit/i,
|
|
16
|
+
/maximum context/i,
|
|
17
|
+
/context window/i,
|
|
18
|
+
/too many tokens/i,
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Parse Claude's output to determine task result.
|
|
23
|
+
*/
|
|
24
|
+
export function parseOutput(output: string): ParsedOutput {
|
|
25
|
+
const result: ParsedOutput = {
|
|
26
|
+
result: 'unknown',
|
|
27
|
+
contextOverflow: false,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Check for context overflow
|
|
31
|
+
for (const pattern of CONTEXT_OVERFLOW_PATTERNS) {
|
|
32
|
+
if (pattern.test(output)) {
|
|
33
|
+
result.contextOverflow = true;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Check for completion markers (search from the end for the last occurrence)
|
|
39
|
+
const completeMatch = output.match(COMPLETE_PATTERN);
|
|
40
|
+
const failedMatch = output.match(FAILED_PATTERN);
|
|
41
|
+
|
|
42
|
+
if (completeMatch && failedMatch) {
|
|
43
|
+
// Both present - use whichever appears last
|
|
44
|
+
const completeIndex = output.lastIndexOf(completeMatch[0]);
|
|
45
|
+
const failedIndex = output.lastIndexOf(failedMatch[0]);
|
|
46
|
+
|
|
47
|
+
if (failedIndex > completeIndex) {
|
|
48
|
+
result.result = 'failed';
|
|
49
|
+
extractFailureReason(output, result);
|
|
50
|
+
} else {
|
|
51
|
+
result.result = 'complete';
|
|
52
|
+
}
|
|
53
|
+
} else if (completeMatch) {
|
|
54
|
+
result.result = 'complete';
|
|
55
|
+
} else if (failedMatch) {
|
|
56
|
+
result.result = 'failed';
|
|
57
|
+
extractFailureReason(output, result);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function extractFailureReason(output: string, result: ParsedOutput): void {
|
|
64
|
+
const reasonMatch = output.match(FAILURE_REASON_PATTERN);
|
|
65
|
+
if (reasonMatch) {
|
|
66
|
+
result.failureReason = reasonMatch[1]?.trim();
|
|
67
|
+
} else {
|
|
68
|
+
// Try to find any text after the FAILED marker
|
|
69
|
+
const failedIndex = output.lastIndexOf('<promise>FAILED</promise>');
|
|
70
|
+
if (failedIndex !== -1) {
|
|
71
|
+
const afterFailed = output.substring(failedIndex + '<promise>FAILED</promise>'.length).trim();
|
|
72
|
+
if (afterFailed) {
|
|
73
|
+
// Take first few lines as reason
|
|
74
|
+
const lines = afterFailed.split('\n').slice(0, 3);
|
|
75
|
+
result.failureReason = lines.join(' ').trim().substring(0, 500);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!result.failureReason) {
|
|
81
|
+
result.failureReason = 'Unknown failure (no reason provided)';
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Check if output indicates a retryable failure.
|
|
87
|
+
*/
|
|
88
|
+
export function isRetryableFailure(parsed: ParsedOutput): boolean {
|
|
89
|
+
// Context overflow is not retryable
|
|
90
|
+
if (parsed.contextOverflow) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Unknown result might be retryable
|
|
95
|
+
if (parsed.result === 'unknown') {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Check failure reason for non-retryable patterns
|
|
100
|
+
if (parsed.result === 'failed' && parsed.failureReason) {
|
|
101
|
+
const nonRetryable = [
|
|
102
|
+
/cannot be done/i,
|
|
103
|
+
/impossible/i,
|
|
104
|
+
/not supported/i,
|
|
105
|
+
/permission denied/i,
|
|
106
|
+
/access denied/i,
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
for (const pattern of nonRetryable) {
|
|
110
|
+
if (pattern.test(parsed.failureReason)) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Most failures are retryable
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Extract a summary from Claude's output for the outcome file.
|
|
124
|
+
*/
|
|
125
|
+
export function extractSummary(output: string): string {
|
|
126
|
+
// Remove ANSI codes
|
|
127
|
+
const cleanOutput = output.replace(/\x1b\[[0-9;]*m/g, '');
|
|
128
|
+
|
|
129
|
+
// Try to find meaningful summary sections
|
|
130
|
+
const lines = cleanOutput.split('\n');
|
|
131
|
+
const summaryLines: string[] = [];
|
|
132
|
+
let inCodeBlock = false;
|
|
133
|
+
|
|
134
|
+
for (const line of lines) {
|
|
135
|
+
// Skip code blocks
|
|
136
|
+
if (line.trim().startsWith('```')) {
|
|
137
|
+
inCodeBlock = !inCodeBlock;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (inCodeBlock) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Skip empty lines and very short lines
|
|
146
|
+
if (line.trim().length < 5) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Skip lines that look like file paths or commands
|
|
151
|
+
if (line.trim().startsWith('/') || line.trim().startsWith('$')) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
summaryLines.push(line);
|
|
156
|
+
|
|
157
|
+
// Limit summary length
|
|
158
|
+
if (summaryLines.length >= 50) {
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return summaryLines.join('\n').trim() || 'No summary available.';
|
|
164
|
+
}
|