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.
Files changed (370) hide show
  1. package/.claude/settings.local.json +32 -0
  2. package/CLAUDE.md +187 -0
  3. package/LICENSE +21 -0
  4. package/RAF/001-raf-task-improvements/input.md +9 -0
  5. package/RAF/001-raf-task-improvements/outcomes/001-add-decisions-folder.md +21 -0
  6. package/RAF/001-raf-task-improvements/outcomes/002-fix-write-error-on-shutdown.md +22 -0
  7. package/RAF/001-raf-task-improvements/outcomes/003-stash-changes-on-failure.md +34 -0
  8. package/RAF/001-raf-task-improvements/outcomes/004-add-project-name-to-commits.md +28 -0
  9. package/RAF/001-raf-task-improvements/outcomes/005-add-running-time-display.md +36 -0
  10. package/RAF/001-raf-task-improvements/outcomes/006-add-task-name-to-logs.md +22 -0
  11. package/RAF/001-raf-task-improvements/outcomes/007-show-model-at-task-start.md +52 -0
  12. package/RAF/001-raf-task-improvements/outcomes/009-remove-editor-placeholder-text.md +20 -0
  13. package/RAF/001-raf-task-improvements/outcomes/SUMMARY.md +83 -0
  14. package/RAF/001-raf-task-improvements/plans/001-add-decisions-folder.md +38 -0
  15. package/RAF/001-raf-task-improvements/plans/002-fix-write-error-on-shutdown.md +33 -0
  16. package/RAF/001-raf-task-improvements/plans/003-stash-changes-on-failure.md +37 -0
  17. package/RAF/001-raf-task-improvements/plans/004-add-project-name-to-commits.md +34 -0
  18. package/RAF/001-raf-task-improvements/plans/005-add-running-time-display.md +39 -0
  19. package/RAF/001-raf-task-improvements/plans/006-add-task-name-to-logs.md +37 -0
  20. package/RAF/001-raf-task-improvements/plans/009-remove-editor-placeholder-text.md +34 -0
  21. package/RAF/002-raf-task-improvements-execution/decisions/DECISIONS.md +13 -0
  22. package/RAF/002-raf-task-improvements-execution/input.md +3 -0
  23. package/RAF/002-raf-task-improvements-execution/outcomes/001-commit-show-model-at-task-start.md +17 -0
  24. package/RAF/002-raf-task-improvements-execution/outcomes/002-delete-skipped-plan.md +23 -0
  25. package/RAF/002-raf-task-improvements-execution/outcomes/SUMMARY.md +32 -0
  26. package/RAF/002-raf-task-improvements-execution/plans/001-commit-show-model-at-task-start.md +37 -0
  27. package/RAF/002-raf-task-improvements-execution/plans/002-delete-skipped-plan.md +23 -0
  28. package/RAF/003-multi-project-execution/decisions/DECISIONS.md +68 -0
  29. package/RAF/003-multi-project-execution/input.md +6 -0
  30. package/RAF/003-multi-project-execution/outcomes/001-remove-state-json.md +52 -0
  31. package/RAF/003-multi-project-execution/outcomes/002-update-raf-status.md +50 -0
  32. package/RAF/003-multi-project-execution/outcomes/003-simplify-git-logic.md +35 -0
  33. package/RAF/003-multi-project-execution/outcomes/004-auto-commit-planning.md +43 -0
  34. package/RAF/003-multi-project-execution/outcomes/005-rerun-failed-tasks.md +43 -0
  35. package/RAF/003-multi-project-execution/outcomes/006-multi-project-execution.md +42 -0
  36. package/RAF/003-multi-project-execution/outcomes/007-verify-timeout.md +54 -0
  37. package/RAF/003-multi-project-execution/outcomes/008-move-decisions-file.md +38 -0
  38. package/RAF/003-multi-project-execution/outcomes/SUMMARY.md +79 -0
  39. package/RAF/003-multi-project-execution/plans/001-remove-state-json.md +71 -0
  40. package/RAF/003-multi-project-execution/plans/002-update-raf-status.md +65 -0
  41. package/RAF/003-multi-project-execution/plans/003-simplify-git-logic.md +74 -0
  42. package/RAF/003-multi-project-execution/plans/004-auto-commit-planning.md +57 -0
  43. package/RAF/003-multi-project-execution/plans/005-rerun-failed-tasks.md +69 -0
  44. package/RAF/003-multi-project-execution/plans/006-multi-project-execution.md +81 -0
  45. package/RAF/003-multi-project-execution/plans/007-verify-timeout.md +63 -0
  46. package/RAF/003-multi-project-execution/plans/008-move-decisions-file.md +78 -0
  47. package/RAF/004-task-naming-optimization/decisions.md +22 -0
  48. package/RAF/004-task-naming-optimization/input.md +6 -0
  49. package/RAF/004-task-naming-optimization/outcomes/001-remove-summary-file.md +17 -0
  50. package/RAF/004-task-naming-optimization/outcomes/002-base36-project-numbering.md +32 -0
  51. package/RAF/004-task-naming-optimization/outcomes/003-improve-haiku-prompt.md +20 -0
  52. package/RAF/004-task-naming-optimization/outcomes/SUMMARY.md +28 -0
  53. package/RAF/004-task-naming-optimization/plans/001-remove-summary-file.md +34 -0
  54. package/RAF/004-task-naming-optimization/plans/002-base36-project-numbering.md +56 -0
  55. package/RAF/004-task-naming-optimization/plans/003-improve-haiku-prompt.md +50 -0
  56. package/RAF/005-task-naming-improvements/decisions.md +60 -0
  57. package/RAF/005-task-naming-improvements/input.md +2 -0
  58. package/RAF/005-task-naming-improvements/outcomes/001-enhance-identifier-resolution.md +42 -0
  59. package/RAF/005-task-naming-improvements/outcomes/002-add-identifier-support-to-status.md +38 -0
  60. package/RAF/005-task-naming-improvements/outcomes/003-update-do-for-full-folder-names.md +44 -0
  61. package/RAF/005-task-naming-improvements/outcomes/004-implement-amend-flag-for-plan.md +55 -0
  62. package/RAF/005-task-naming-improvements/outcomes/005-commit-outcomes-on-complete.md +47 -0
  63. package/RAF/005-task-naming-improvements/outcomes/006-update-execution-prompt-commit-schema.md +40 -0
  64. package/RAF/005-task-naming-improvements/outcomes/007-allow-pending-task-amendments.md +38 -0
  65. package/RAF/005-task-naming-improvements/outcomes/008-fix-timeout-label.md +24 -0
  66. package/RAF/005-task-naming-improvements/plans/001-enhance-identifier-resolution.md +46 -0
  67. package/RAF/005-task-naming-improvements/plans/002-add-identifier-support-to-status.md +36 -0
  68. package/RAF/005-task-naming-improvements/plans/003-update-do-for-full-folder-names.md +38 -0
  69. package/RAF/005-task-naming-improvements/plans/004-implement-amend-flag-for-plan.md +67 -0
  70. package/RAF/005-task-naming-improvements/plans/005-commit-outcomes-on-complete.md +86 -0
  71. package/RAF/005-task-naming-improvements/plans/006-update-execution-prompt-commit-schema.md +60 -0
  72. package/RAF/005-task-naming-improvements/plans/007-allow-pending-task-amendments.md +60 -0
  73. package/RAF/005-task-naming-improvements/plans/008-fix-timeout-label.md +31 -0
  74. package/RAF/006-fix-double-summary-headers/decisions.md +28 -0
  75. package/RAF/006-fix-double-summary-headers/input.md +3 -0
  76. package/RAF/006-fix-double-summary-headers/outcomes/001-fix-double-summary-headers.md +29 -0
  77. package/RAF/006-fix-double-summary-headers/outcomes/002-update-readme-for-npm.md +31 -0
  78. package/RAF/006-fix-double-summary-headers/outcomes/003-npm-publish-instructions.md +30 -0
  79. package/RAF/006-fix-double-summary-headers/outcomes/004-flexible-project-lookup.md +47 -0
  80. package/RAF/006-fix-double-summary-headers/plans/001-fix-double-summary-headers.md +42 -0
  81. package/RAF/006-fix-double-summary-headers/plans/002-update-readme-for-npm.md +44 -0
  82. package/RAF/006-fix-double-summary-headers/plans/003-npm-publish-instructions.md +45 -0
  83. package/RAF/006-fix-double-summary-headers/plans/004-flexible-project-lookup.md +40 -0
  84. package/RAF/007-improve-outcome-format/decisions.md +28 -0
  85. package/RAF/007-improve-outcome-format/input.md +2 -0
  86. package/RAF/007-improve-outcome-format/outcomes/001-update-execution-prompt.md +10 -0
  87. package/RAF/007-improve-outcome-format/outcomes/002-update-state-derivation.md +17 -0
  88. package/RAF/007-improve-outcome-format/outcomes/003-update-do-command-outcome-handling.md +16 -0
  89. package/RAF/007-improve-outcome-format/outcomes/004-implement-failure-analysis.md +16 -0
  90. package/RAF/007-improve-outcome-format/outcomes/005-update-documentation.md +15 -0
  91. package/RAF/007-improve-outcome-format/plans/001-update-execution-prompt.md +36 -0
  92. package/RAF/007-improve-outcome-format/plans/002-update-state-derivation.md +35 -0
  93. package/RAF/007-improve-outcome-format/plans/003-update-do-command-outcome-handling.md +37 -0
  94. package/RAF/007-improve-outcome-format/plans/004-implement-failure-analysis.md +44 -0
  95. package/RAF/007-improve-outcome-format/plans/005-update-documentation.md +33 -0
  96. package/RAF/008-beautiful-do/decisions.md +31 -0
  97. package/RAF/008-beautiful-do/input.md +1 -0
  98. package/RAF/008-beautiful-do/outcomes/001-terminal-symbols.md +55 -0
  99. package/RAF/008-beautiful-do/outcomes/002-refactor-do-output.md +95 -0
  100. package/RAF/008-beautiful-do/outcomes/003-refactor-status-output.md +71 -0
  101. package/RAF/008-beautiful-do/outcomes/004-simplify-logger.md +53 -0
  102. package/RAF/008-beautiful-do/outcomes/005-add-tests.md +41 -0
  103. package/RAF/008-beautiful-do/plans/001-terminal-symbols.md +41 -0
  104. package/RAF/008-beautiful-do/plans/002-refactor-do-output.md +44 -0
  105. package/RAF/008-beautiful-do/plans/003-refactor-status-output.md +37 -0
  106. package/RAF/008-beautiful-do/plans/004-simplify-logger.md +32 -0
  107. package/RAF/008-beautiful-do/plans/005-add-tests.md +40 -0
  108. package/RAF/009-system-promt-ammend/decisions.md +13 -0
  109. package/RAF/009-system-promt-ammend/input.md +9 -0
  110. package/RAF/009-system-promt-ammend/outcomes/001-model-override.md +79 -0
  111. package/RAF/009-system-promt-ammend/outcomes/002-system-prompt-append.md +51 -0
  112. package/RAF/009-system-promt-ammend/outcomes/003-retry-context.md +60 -0
  113. package/RAF/009-system-promt-ammend/plans/001-model-override.md +61 -0
  114. package/RAF/009-system-promt-ammend/plans/002-system-prompt-append.md +56 -0
  115. package/RAF/009-system-promt-ammend/plans/003-retry-context.md +76 -0
  116. package/RAF/010-outcome-marker-fallback/decisions.md +19 -0
  117. package/RAF/010-outcome-marker-fallback/input.md +1 -0
  118. package/RAF/010-outcome-marker-fallback/outcomes/001-outcome-file-marker-fallback.md +35 -0
  119. package/RAF/010-outcome-marker-fallback/outcomes/002-creative-project-naming.md +47 -0
  120. package/RAF/010-outcome-marker-fallback/plans/001-outcome-file-marker-fallback.md +58 -0
  121. package/RAF/010-outcome-marker-fallback/plans/002-creative-project-naming.md +68 -0
  122. package/RAF/011-do-task-in-commit/decisions.md +22 -0
  123. package/RAF/011-do-task-in-commit/input.md +1 -0
  124. package/RAF/011-do-task-in-commit/outcomes/001-update-execution-prompt.md +54 -0
  125. package/RAF/011-do-task-in-commit/outcomes/002-update-tests.md +61 -0
  126. package/RAF/011-do-task-in-commit/outcomes/003-update-documentation.md +51 -0
  127. package/RAF/011-do-task-in-commit/plans/001-update-execution-prompt.md +46 -0
  128. package/RAF/011-do-task-in-commit/plans/002-update-tests.md +51 -0
  129. package/RAF/011-do-task-in-commit/plans/003-update-documentation.md +45 -0
  130. package/RAF/012-name-picker-buffet/decisions.md +40 -0
  131. package/RAF/012-name-picker-buffet/input.md +6 -0
  132. package/RAF/012-name-picker-buffet/outcomes/001-name-picker-for-raf-plan.md +49 -0
  133. package/RAF/012-name-picker-buffet/outcomes/002-interactive-project-picker-for-raf-do.md +49 -0
  134. package/RAF/012-name-picker-buffet/outcomes/003-raf-status-truncation.md +55 -0
  135. package/RAF/012-name-picker-buffet/outcomes/004-failure-reason-details.md +65 -0
  136. package/RAF/012-name-picker-buffet/outcomes/005-remove-raf-commits.md +57 -0
  137. package/RAF/012-name-picker-buffet/outcomes/006-update-execution-prompt-for-commits.md +47 -0
  138. package/RAF/012-name-picker-buffet/outcomes/007-fix-plan-mode-user-prompt.md +83 -0
  139. package/RAF/012-name-picker-buffet/outcomes/008-add-auto-flag-for-plan-mode.md +77 -0
  140. package/RAF/012-name-picker-buffet/plans/001-name-picker-for-raf-plan.md +47 -0
  141. package/RAF/012-name-picker-buffet/plans/002-interactive-project-picker-for-raf-do.md +43 -0
  142. package/RAF/012-name-picker-buffet/plans/003-raf-status-truncation.md +36 -0
  143. package/RAF/012-name-picker-buffet/plans/004-failure-reason-details.md +46 -0
  144. package/RAF/012-name-picker-buffet/plans/005-remove-raf-commits.md +42 -0
  145. package/RAF/012-name-picker-buffet/plans/006-update-execution-prompt-for-commits.md +47 -0
  146. package/RAF/012-name-picker-buffet/plans/007-fix-plan-mode-user-prompt.md +55 -0
  147. package/RAF/012-name-picker-buffet/plans/008-add-auto-flag-for-plan-mode.md +49 -0
  148. package/RAF/013-dependencies-watchdog/decisions.md +37 -0
  149. package/RAF/013-dependencies-watchdog/input.md +1 -0
  150. package/RAF/013-dependencies-watchdog/outcomes/001-define-dependency-syntax.md +56 -0
  151. package/RAF/013-dependencies-watchdog/outcomes/002-update-planning-prompts.md +60 -0
  152. package/RAF/013-dependencies-watchdog/outcomes/003-parse-dependencies-update-state.md +81 -0
  153. package/RAF/013-dependencies-watchdog/outcomes/004-implement-dependency-checking-in-do.md +116 -0
  154. package/RAF/013-dependencies-watchdog/outcomes/005-update-execution-prompts.md +75 -0
  155. package/RAF/013-dependencies-watchdog/outcomes/006-add-tests.md +100 -0
  156. package/RAF/013-dependencies-watchdog/outcomes/007-add-act-alias.md +46 -0
  157. package/RAF/013-dependencies-watchdog/outcomes/008-add-exit-message.md +52 -0
  158. package/RAF/013-dependencies-watchdog/plans/001-define-dependency-syntax.md +32 -0
  159. package/RAF/013-dependencies-watchdog/plans/002-update-planning-prompts.md +38 -0
  160. package/RAF/013-dependencies-watchdog/plans/003-parse-dependencies-update-state.md +46 -0
  161. package/RAF/013-dependencies-watchdog/plans/004-implement-dependency-checking-in-do.md +48 -0
  162. package/RAF/013-dependencies-watchdog/plans/005-update-execution-prompts.md +44 -0
  163. package/RAF/013-dependencies-watchdog/plans/006-add-tests.md +54 -0
  164. package/RAF/013-dependencies-watchdog/plans/007-add-act-alias.md +26 -0
  165. package/RAF/013-dependencies-watchdog/plans/008-add-exit-message.md +31 -0
  166. package/RAF/014-watchdog/decisions.md +16 -0
  167. package/RAF/014-watchdog/input.md +2 -0
  168. package/RAF/014-watchdog/outcomes/001-amend-flag-position.md +50 -0
  169. package/RAF/014-watchdog/outcomes/002-details-only-on-failure.md +58 -0
  170. package/RAF/014-watchdog/plans/001-amend-flag-position.md +34 -0
  171. package/RAF/014-watchdog/plans/002-details-only-on-failure.md +46 -0
  172. package/RAF/015-name-lottery/decisions.md +14 -0
  173. package/RAF/015-name-lottery/input.md +3 -0
  174. package/RAF/015-name-lottery/outcomes/001-auto-pick-project-name.md +31 -0
  175. package/RAF/015-name-lottery/outcomes/002-mention-plan-files-in-commit.md +23 -0
  176. package/RAF/015-name-lottery/outcomes/003-fix-input-md-in-amend-flow.md +44 -0
  177. package/RAF/015-name-lottery/plans/001-auto-pick-project-name.md +38 -0
  178. package/RAF/015-name-lottery/plans/002-mention-plan-files-in-commit.md +32 -0
  179. package/RAF/015-name-lottery/plans/003-fix-input-md-in-amend-flow.md +44 -0
  180. package/README.md +116 -0
  181. package/dist/commands/do.d.ts +12 -0
  182. package/dist/commands/do.d.ts.map +1 -0
  183. package/dist/commands/do.js +684 -0
  184. package/dist/commands/do.js.map +1 -0
  185. package/dist/commands/plan.d.ts +3 -0
  186. package/dist/commands/plan.d.ts.map +1 -0
  187. package/dist/commands/plan.js +345 -0
  188. package/dist/commands/plan.js.map +1 -0
  189. package/dist/commands/status.d.ts +3 -0
  190. package/dist/commands/status.d.ts.map +1 -0
  191. package/dist/commands/status.js +117 -0
  192. package/dist/commands/status.js.map +1 -0
  193. package/dist/core/claude-runner.d.ts +78 -0
  194. package/dist/core/claude-runner.d.ts.map +1 -0
  195. package/dist/core/claude-runner.js +297 -0
  196. package/dist/core/claude-runner.js.map +1 -0
  197. package/dist/core/editor.d.ts +10 -0
  198. package/dist/core/editor.d.ts.map +1 -0
  199. package/dist/core/editor.js +77 -0
  200. package/dist/core/editor.js.map +1 -0
  201. package/dist/core/failure-analyzer.d.ts +28 -0
  202. package/dist/core/failure-analyzer.d.ts.map +1 -0
  203. package/dist/core/failure-analyzer.js +305 -0
  204. package/dist/core/failure-analyzer.js.map +1 -0
  205. package/dist/core/git.d.ts +42 -0
  206. package/dist/core/git.d.ts.map +1 -0
  207. package/dist/core/git.js +148 -0
  208. package/dist/core/git.js.map +1 -0
  209. package/dist/core/project-manager.d.ts +72 -0
  210. package/dist/core/project-manager.d.ts.map +1 -0
  211. package/dist/core/project-manager.js +193 -0
  212. package/dist/core/project-manager.js.map +1 -0
  213. package/dist/core/retry-handler.d.ts +19 -0
  214. package/dist/core/retry-handler.d.ts.map +1 -0
  215. package/dist/core/retry-handler.js +51 -0
  216. package/dist/core/retry-handler.js.map +1 -0
  217. package/dist/core/shutdown-handler.d.ts +30 -0
  218. package/dist/core/shutdown-handler.d.ts.map +1 -0
  219. package/dist/core/shutdown-handler.js +79 -0
  220. package/dist/core/shutdown-handler.js.map +1 -0
  221. package/dist/core/state-derivation.d.ts +82 -0
  222. package/dist/core/state-derivation.d.ts.map +1 -0
  223. package/dist/core/state-derivation.js +271 -0
  224. package/dist/core/state-derivation.js.map +1 -0
  225. package/dist/core/state-manager.d.ts +54 -0
  226. package/dist/core/state-manager.d.ts.map +1 -0
  227. package/dist/core/state-manager.js +198 -0
  228. package/dist/core/state-manager.js.map +1 -0
  229. package/dist/index.d.ts +3 -0
  230. package/dist/index.d.ts.map +1 -0
  231. package/dist/index.js +16 -0
  232. package/dist/index.js.map +1 -0
  233. package/dist/parsers/output-parser.d.ts +19 -0
  234. package/dist/parsers/output-parser.d.ts.map +1 -0
  235. package/dist/parsers/output-parser.js +137 -0
  236. package/dist/parsers/output-parser.js.map +1 -0
  237. package/dist/prompts/amend.d.ts +20 -0
  238. package/dist/prompts/amend.d.ts.map +1 -0
  239. package/dist/prompts/amend.js +166 -0
  240. package/dist/prompts/amend.js.map +1 -0
  241. package/dist/prompts/execution.d.ts +30 -0
  242. package/dist/prompts/execution.d.ts.map +1 -0
  243. package/dist/prompts/execution.js +179 -0
  244. package/dist/prompts/execution.js.map +1 -0
  245. package/dist/prompts/planning.d.ts +15 -0
  246. package/dist/prompts/planning.d.ts.map +1 -0
  247. package/dist/prompts/planning.js +163 -0
  248. package/dist/prompts/planning.js.map +1 -0
  249. package/dist/types/config.d.ts +26 -0
  250. package/dist/types/config.d.ts.map +1 -0
  251. package/dist/types/config.js +7 -0
  252. package/dist/types/config.js.map +1 -0
  253. package/dist/types/state.d.ts +33 -0
  254. package/dist/types/state.d.ts.map +1 -0
  255. package/dist/types/state.js +28 -0
  256. package/dist/types/state.js.map +1 -0
  257. package/dist/ui/name-picker-subprocess.d.ts +11 -0
  258. package/dist/ui/name-picker-subprocess.d.ts.map +1 -0
  259. package/dist/ui/name-picker-subprocess.js +83 -0
  260. package/dist/ui/name-picker-subprocess.js.map +1 -0
  261. package/dist/ui/name-picker.d.ts +19 -0
  262. package/dist/ui/name-picker.d.ts.map +1 -0
  263. package/dist/ui/name-picker.js +173 -0
  264. package/dist/ui/name-picker.js.map +1 -0
  265. package/dist/ui/project-picker.d.ts +27 -0
  266. package/dist/ui/project-picker.d.ts.map +1 -0
  267. package/dist/ui/project-picker.js +58 -0
  268. package/dist/ui/project-picker.js.map +1 -0
  269. package/dist/utils/config.d.ts +24 -0
  270. package/dist/utils/config.d.ts.map +1 -0
  271. package/dist/utils/config.js +63 -0
  272. package/dist/utils/config.js.map +1 -0
  273. package/dist/utils/logger.d.ts +32 -0
  274. package/dist/utils/logger.d.ts.map +1 -0
  275. package/dist/utils/logger.js +60 -0
  276. package/dist/utils/logger.js.map +1 -0
  277. package/dist/utils/name-generator.d.ts +20 -0
  278. package/dist/utils/name-generator.d.ts.map +1 -0
  279. package/dist/utils/name-generator.js +183 -0
  280. package/dist/utils/name-generator.js.map +1 -0
  281. package/dist/utils/paths.d.ts +132 -0
  282. package/dist/utils/paths.d.ts.map +1 -0
  283. package/dist/utils/paths.js +412 -0
  284. package/dist/utils/paths.js.map +1 -0
  285. package/dist/utils/status-line.d.ts +14 -0
  286. package/dist/utils/status-line.d.ts.map +1 -0
  287. package/dist/utils/status-line.js +36 -0
  288. package/dist/utils/status-line.js.map +1 -0
  289. package/dist/utils/terminal-symbols.d.ts +50 -0
  290. package/dist/utils/terminal-symbols.d.ts.map +1 -0
  291. package/dist/utils/terminal-symbols.js +97 -0
  292. package/dist/utils/terminal-symbols.js.map +1 -0
  293. package/dist/utils/timer.d.ts +17 -0
  294. package/dist/utils/timer.d.ts.map +1 -0
  295. package/dist/utils/timer.js +56 -0
  296. package/dist/utils/timer.js.map +1 -0
  297. package/dist/utils/validation.d.ts +17 -0
  298. package/dist/utils/validation.d.ts.map +1 -0
  299. package/dist/utils/validation.js +106 -0
  300. package/dist/utils/validation.js.map +1 -0
  301. package/dist/utils/version.d.ts +2 -0
  302. package/dist/utils/version.d.ts.map +1 -0
  303. package/dist/utils/version.js +12 -0
  304. package/dist/utils/version.js.map +1 -0
  305. package/jest.config.ts +30 -0
  306. package/package.json +55 -0
  307. package/src/commands/do.ts +829 -0
  308. package/src/commands/plan.ts +422 -0
  309. package/src/commands/status.ts +146 -0
  310. package/src/core/claude-runner.ts +374 -0
  311. package/src/core/editor.ts +85 -0
  312. package/src/core/failure-analyzer.ts +372 -0
  313. package/src/core/git.ts +166 -0
  314. package/src/core/project-manager.ts +243 -0
  315. package/src/core/retry-handler.ts +72 -0
  316. package/src/core/shutdown-handler.ts +93 -0
  317. package/src/core/state-derivation.ts +343 -0
  318. package/src/index.ts +20 -0
  319. package/src/parsers/output-parser.ts +164 -0
  320. package/src/prompts/amend.ts +194 -0
  321. package/src/prompts/execution.ts +223 -0
  322. package/src/prompts/planning.ts +175 -0
  323. package/src/types/config.ts +35 -0
  324. package/src/ui/name-picker-subprocess.ts +96 -0
  325. package/src/ui/name-picker.ts +198 -0
  326. package/src/ui/project-picker.ts +80 -0
  327. package/src/utils/config.ts +69 -0
  328. package/src/utils/logger.ts +81 -0
  329. package/src/utils/name-generator.ts +211 -0
  330. package/src/utils/paths.ts +497 -0
  331. package/src/utils/status-line.ts +45 -0
  332. package/src/utils/terminal-symbols.ts +124 -0
  333. package/src/utils/timer.ts +64 -0
  334. package/src/utils/validation.ts +132 -0
  335. package/src/utils/version.ts +12 -0
  336. package/tests/unit/claude-runner-interactive.test.ts +343 -0
  337. package/tests/unit/claude-runner.test.ts +629 -0
  338. package/tests/unit/command-output.test.ts +295 -0
  339. package/tests/unit/config.test.ts +72 -0
  340. package/tests/unit/dependency-integration.test.ts +559 -0
  341. package/tests/unit/do-blocked-tasks.test.ts +323 -0
  342. package/tests/unit/do-command.test.ts +198 -0
  343. package/tests/unit/do-multiproject.test.ts +270 -0
  344. package/tests/unit/do-rerun.test.ts +270 -0
  345. package/tests/unit/execution-prompt.test.ts +406 -0
  346. package/tests/unit/failure-analyzer.test.ts +276 -0
  347. package/tests/unit/failure-history.test.ts +143 -0
  348. package/tests/unit/git-stash.test.ts +138 -0
  349. package/tests/unit/git.test.ts +80 -0
  350. package/tests/unit/logger.test.ts +132 -0
  351. package/tests/unit/name-generator.test.ts +283 -0
  352. package/tests/unit/name-picker.test.ts +179 -0
  353. package/tests/unit/outcome-content.test.ts +166 -0
  354. package/tests/unit/output-parser.test.ts +178 -0
  355. package/tests/unit/paths.test.ts +741 -0
  356. package/tests/unit/plan-command-amend-flag.test.ts +115 -0
  357. package/tests/unit/plan-command-amend-input.test.ts +156 -0
  358. package/tests/unit/plan-command-auto-flag.test.ts +112 -0
  359. package/tests/unit/plan-command.test.ts +580 -0
  360. package/tests/unit/planning-prompt.test.ts +137 -0
  361. package/tests/unit/project-manager.test.ts +265 -0
  362. package/tests/unit/project-picker.test.ts +338 -0
  363. package/tests/unit/retry-handler.test.ts +89 -0
  364. package/tests/unit/state-derivation.test.ts +714 -0
  365. package/tests/unit/status-command.test.ts +271 -0
  366. package/tests/unit/status-line.test.ts +92 -0
  367. package/tests/unit/terminal-symbols.test.ts +214 -0
  368. package/tests/unit/timer.test.ts +102 -0
  369. package/tests/unit/validation.test.ts +118 -0
  370. package/tsconfig.json +26 -0
@@ -0,0 +1,422 @@
1
+ import * as fs from 'node:fs';
2
+ import { Command } from 'commander';
3
+ import { ProjectManager } from '../core/project-manager.js';
4
+ import { ClaudeRunner } from '../core/claude-runner.js';
5
+ import { openEditor, getInputTemplate } from '../core/editor.js';
6
+ import { shutdownHandler } from '../core/shutdown-handler.js';
7
+ import { getPlanningPrompt } from '../prompts/planning.js';
8
+ import { getAmendPrompt } from '../prompts/amend.js';
9
+ import {
10
+ validateEnvironment,
11
+ reportValidation,
12
+ validateProjectName,
13
+ resolveModelOption,
14
+ } from '../utils/validation.js';
15
+ import { logger } from '../utils/logger.js';
16
+ import { generateProjectNames } from '../utils/name-generator.js';
17
+ import { pickProjectName } from '../ui/name-picker.js';
18
+ import {
19
+ getPlansDir,
20
+ getRafDir,
21
+ resolveProjectIdentifier,
22
+ resolveProjectIdentifierWithDetails,
23
+ getInputPath,
24
+ extractTaskNameFromPlanFile,
25
+ } from '../utils/paths.js';
26
+ import {
27
+ deriveProjectState,
28
+ isProjectComplete,
29
+ DerivedTask,
30
+ } from '../core/state-derivation.js';
31
+
32
+ interface PlanCommandOptions {
33
+ amend?: boolean;
34
+ model?: string;
35
+ sonnet?: boolean;
36
+ auto?: boolean;
37
+ }
38
+
39
+ export function createPlanCommand(): Command {
40
+ const command = new Command('plan')
41
+ .description('Create a new project and interactively plan tasks with Claude')
42
+ .argument('[projectName]', 'Optional project name (will be prompted if not provided)')
43
+ .option(
44
+ '-a, --amend',
45
+ 'Add tasks to an existing project (requires project identifier as argument)'
46
+ )
47
+ .option('-m, --model <name>', 'Claude model to use (sonnet, haiku, opus)')
48
+ .option('--sonnet', 'Use Sonnet model (shorthand for --model sonnet)')
49
+ .option('-y, --auto', "Skip Claude's permission prompts for file operations")
50
+ .action(async (projectName: string | undefined, options: PlanCommandOptions) => {
51
+ // Validate and resolve model option
52
+ let model: string;
53
+ try {
54
+ model = resolveModelOption(options.model, options.sonnet);
55
+ } catch (error) {
56
+ logger.error((error as Error).message);
57
+ process.exit(1);
58
+ }
59
+
60
+ const autoMode = options.auto ?? false;
61
+
62
+ if (options.amend) {
63
+ if (!projectName) {
64
+ logger.error('--amend requires a project identifier');
65
+ logger.error('Usage: raf plan <project> --amend');
66
+ logger.error(' or: raf plan --amend <project>');
67
+ process.exit(1);
68
+ }
69
+ await runAmendCommand(projectName, model, autoMode);
70
+ } else {
71
+ await runPlanCommand(projectName, model, autoMode);
72
+ }
73
+ });
74
+
75
+ return command;
76
+ }
77
+
78
+ async function runPlanCommand(projectName?: string, model?: string, autoMode: boolean = false): Promise<void> {
79
+ // Validate environment
80
+ const validation = validateEnvironment();
81
+ reportValidation(validation);
82
+
83
+ if (!validation.valid) {
84
+ process.exit(1);
85
+ }
86
+
87
+ // Check if project name matches an existing project
88
+ if (projectName) {
89
+ const rafDir = getRafDir();
90
+ const existingProject = resolveProjectIdentifier(rafDir, projectName);
91
+ if (existingProject) {
92
+ logger.error(`Project already exists: ${existingProject}`);
93
+ logger.error(`To add tasks to an existing project, use: raf plan --amend ${projectName}`);
94
+ process.exit(1);
95
+ }
96
+ }
97
+
98
+ // Open editor for user input
99
+ logger.info('Opening editor for project description...');
100
+ logger.info('(Save and close the editor when done)');
101
+ logger.newline();
102
+
103
+ let userInput: string;
104
+ try {
105
+ userInput = await openEditor(getInputTemplate());
106
+ } catch (error) {
107
+ logger.error(`Failed to get input: ${error}`);
108
+ process.exit(1);
109
+ }
110
+
111
+ // Check if input is empty or just the template
112
+ const cleanInput = userInput
113
+ .replace(/<!--[\s\S]*?-->/g, '')
114
+ .replace(/^#.*$/gm, '')
115
+ .trim();
116
+
117
+ if (!cleanInput) {
118
+ logger.error('No project description provided. Aborting.');
119
+ process.exit(1);
120
+ }
121
+
122
+ // Get or generate project name
123
+ let finalProjectName = projectName;
124
+ if (!finalProjectName) {
125
+ logger.info('Generating project name suggestions...');
126
+ const suggestedNames = await generateProjectNames(cleanInput);
127
+ logger.newline();
128
+
129
+ if (autoMode) {
130
+ // Auto-select the first generated name
131
+ // generateProjectNames always returns at least one fallback name
132
+ finalProjectName = suggestedNames[0] ?? 'project';
133
+ logger.info(`Auto-selected project name: ${finalProjectName}`);
134
+ } else {
135
+ finalProjectName = await pickProjectName(suggestedNames);
136
+ }
137
+ }
138
+
139
+ if (!validateProjectName(finalProjectName)) {
140
+ logger.error('Invalid project name. Use only letters, numbers, hyphens, and underscores.');
141
+ process.exit(1);
142
+ }
143
+
144
+ // Create project
145
+ const projectManager = new ProjectManager();
146
+ const projectPath = projectManager.createProject(finalProjectName);
147
+
148
+ logger.success(`Created project: ${projectPath}`);
149
+ logger.newline();
150
+
151
+ // Save input
152
+ projectManager.saveInput(projectPath, userInput);
153
+
154
+ // Set up shutdown handler
155
+ const claudeRunner = new ClaudeRunner({ model });
156
+ shutdownHandler.init();
157
+ shutdownHandler.registerClaudeRunner(claudeRunner);
158
+
159
+ // Register cleanup callback for empty project folder
160
+ shutdownHandler.onShutdown(() => {
161
+ projectManager.cleanupEmptyProject(projectPath);
162
+ });
163
+
164
+ // Run planning session
165
+ logger.info('Starting planning session with Claude...');
166
+ logger.info('Claude will interview you about each task.');
167
+ if (model) {
168
+ logger.info(`Using model: ${model}`);
169
+ }
170
+ if (autoMode) {
171
+ logger.warn('Auto mode enabled: permission prompts will be skipped.');
172
+ }
173
+ logger.newline();
174
+
175
+ const { systemPrompt, userMessage } = getPlanningPrompt({
176
+ projectPath,
177
+ inputContent: userInput,
178
+ });
179
+
180
+ try {
181
+ const exitCode = await claudeRunner.runInteractive(systemPrompt, userMessage, {
182
+ dangerouslySkipPermissions: autoMode,
183
+ });
184
+
185
+ if (exitCode !== 0) {
186
+ logger.warn(`Claude exited with code ${exitCode}`);
187
+ }
188
+
189
+ // Check for created plan files
190
+ const plansDir = getPlansDir(projectPath);
191
+ const planFiles = fs.existsSync(plansDir)
192
+ ? fs.readdirSync(plansDir).filter((f) => f.endsWith('.md')).sort()
193
+ : [];
194
+
195
+ if (planFiles.length === 0) {
196
+ logger.warn('No plan files were created.');
197
+ } else {
198
+ logger.newline();
199
+ logger.success(`Planning complete! Created ${planFiles.length} task(s).`);
200
+ logger.newline();
201
+ logger.info('Plans created:');
202
+ for (const planFile of planFiles) {
203
+ logger.info(` - plans/${planFile}`);
204
+ }
205
+
206
+ logger.newline();
207
+ logger.info(`Run 'raf do ${finalProjectName}' to execute the plans.`);
208
+ }
209
+ } catch (error) {
210
+ logger.error(`Planning failed: ${error}`);
211
+ throw error;
212
+ } finally {
213
+ // Cleanup empty project folder if no plans were created
214
+ projectManager.cleanupEmptyProject(projectPath);
215
+ }
216
+ }
217
+
218
+ async function runAmendCommand(identifier: string, model?: string, autoMode: boolean = false): Promise<void> {
219
+ // Validate environment
220
+ const validation = validateEnvironment();
221
+ reportValidation(validation);
222
+
223
+ if (!validation.valid) {
224
+ process.exit(1);
225
+ }
226
+
227
+ // Resolve the project
228
+ const rafDir = getRafDir();
229
+ const result = resolveProjectIdentifierWithDetails(rafDir, identifier);
230
+
231
+ if (!result.path) {
232
+ if (result.error === 'ambiguous' && result.matches) {
233
+ logger.error(`Ambiguous project name: ${identifier}`);
234
+ logger.error('Multiple projects match:');
235
+ for (const match of result.matches) {
236
+ logger.error(` - ${match.folder}`);
237
+ }
238
+ logger.error('Please specify the project ID or full folder name.');
239
+ } else {
240
+ logger.error(`Project not found: ${identifier}`);
241
+ }
242
+ process.exit(1);
243
+ }
244
+
245
+ const projectPath = result.path;
246
+
247
+ // Load existing project state
248
+ const projectState = deriveProjectState(projectPath);
249
+
250
+ if (projectState.tasks.length === 0) {
251
+ logger.error(`Project has no tasks yet. Use 'raf plan' instead.`);
252
+ process.exit(1);
253
+ }
254
+
255
+ // Check if project is fully completed and show warning
256
+ if (isProjectComplete(projectState)) {
257
+ logger.warn('Project is fully completed. New tasks will extend the existing plan.');
258
+ logger.newline();
259
+ }
260
+
261
+ // Get existing tasks with their names
262
+ const plansDir = getPlansDir(projectPath);
263
+ const existingTasks: Array<DerivedTask & { taskName: string }> = projectState.tasks.map(
264
+ (task) => {
265
+ const planFile = task.planFile.replace('plans/', '');
266
+ const taskName = extractTaskNameFromPlanFile(planFile) ?? 'unknown';
267
+ return { ...task, taskName };
268
+ }
269
+ );
270
+
271
+ // Calculate next task number
272
+ const maxTaskNumber = Math.max(
273
+ ...projectState.tasks.map((t) => parseInt(t.id, 10))
274
+ );
275
+ const nextTaskNumber = maxTaskNumber + 1;
276
+
277
+ // Load original input
278
+ const inputPath = getInputPath(projectPath);
279
+ const originalInput = fs.existsSync(inputPath)
280
+ ? fs.readFileSync(inputPath, 'utf-8')
281
+ : '';
282
+
283
+ // Show existing tasks summary
284
+ logger.info('Amending existing project:');
285
+ logger.info(` Path: ${projectPath}`);
286
+ logger.info(` Existing tasks: ${existingTasks.length}`);
287
+ logger.newline();
288
+ logger.info('Current tasks:');
289
+ for (const task of existingTasks) {
290
+ const statusIcon =
291
+ task.status === 'completed' ? '[done]' : task.status === 'failed' ? '[fail]' : '[ ]';
292
+ logger.info(` ${statusIcon} ${task.id}: ${task.taskName}`);
293
+ }
294
+ logger.newline();
295
+
296
+ // Open editor for new task description
297
+ const editorTemplate = getAmendTemplate(existingTasks, nextTaskNumber);
298
+ logger.info('Opening editor for new task description...');
299
+ logger.info('(Save and close the editor when done)');
300
+ logger.newline();
301
+
302
+ let userInput: string;
303
+ try {
304
+ userInput = await openEditor(editorTemplate);
305
+ } catch (error) {
306
+ logger.error(`Failed to get input: ${error}`);
307
+ process.exit(1);
308
+ }
309
+
310
+ // Extract new task description (remove the template comments)
311
+ const cleanInput = userInput
312
+ .replace(/<!--[\s\S]*?-->/g, '')
313
+ .replace(/^#.*$/gm, '')
314
+ .trim();
315
+
316
+ if (!cleanInput) {
317
+ logger.error('No new task description provided. Aborting.');
318
+ process.exit(1);
319
+ }
320
+
321
+ // Append new task description to input.md with separator
322
+ const separator = '\n\n---\n\n';
323
+ const updatedInput = originalInput.trim()
324
+ ? `${originalInput.trimEnd()}${separator}${cleanInput}`
325
+ : cleanInput;
326
+ fs.writeFileSync(inputPath, updatedInput);
327
+
328
+ // Set up shutdown handler
329
+ const claudeRunner = new ClaudeRunner({ model });
330
+ shutdownHandler.init();
331
+ shutdownHandler.registerClaudeRunner(claudeRunner);
332
+
333
+ // Run amend planning session
334
+ logger.info('Starting amendment session with Claude...');
335
+ logger.info('Claude will interview you about each new task.');
336
+ if (model) {
337
+ logger.info(`Using model: ${model}`);
338
+ }
339
+ if (autoMode) {
340
+ logger.warn('Auto mode enabled: permission prompts will be skipped.');
341
+ }
342
+ logger.newline();
343
+
344
+ const { systemPrompt, userMessage } = getAmendPrompt({
345
+ projectPath,
346
+ existingTasks,
347
+ nextTaskNumber,
348
+ newTaskDescription: cleanInput,
349
+ });
350
+
351
+ try {
352
+ const exitCode = await claudeRunner.runInteractive(systemPrompt, userMessage, {
353
+ dangerouslySkipPermissions: autoMode,
354
+ });
355
+
356
+ if (exitCode !== 0) {
357
+ logger.warn(`Claude exited with code ${exitCode}`);
358
+ }
359
+
360
+ // Check for new plan files
361
+ const allPlanFiles = fs.existsSync(plansDir)
362
+ ? fs.readdirSync(plansDir).filter((f) => f.endsWith('.md')).sort()
363
+ : [];
364
+
365
+ const newPlanFiles = allPlanFiles.filter((f) => {
366
+ const match = f.match(/^(\d{2,3})-/);
367
+ if (match && match[1]) {
368
+ return parseInt(match[1], 10) >= nextTaskNumber;
369
+ }
370
+ return false;
371
+ });
372
+
373
+ if (newPlanFiles.length === 0) {
374
+ logger.warn('No new plan files were created.');
375
+ } else {
376
+ logger.newline();
377
+ logger.success(`Amendment complete! Added ${newPlanFiles.length} new task(s).`);
378
+ logger.newline();
379
+ logger.info('New plans created:');
380
+ for (const planFile of newPlanFiles) {
381
+ logger.info(` - plans/${planFile}`);
382
+ }
383
+
384
+ logger.newline();
385
+ logger.info(`Total tasks: ${allPlanFiles.length}`);
386
+ logger.info(`Run 'raf do ${identifier}' to execute the new tasks.`);
387
+ }
388
+ } catch (error) {
389
+ logger.error(`Amendment failed: ${error}`);
390
+ throw error;
391
+ }
392
+ }
393
+
394
+ /**
395
+ * Generate editor template for amend mode showing existing tasks.
396
+ */
397
+ function getAmendTemplate(
398
+ existingTasks: Array<DerivedTask & { taskName: string }>,
399
+ nextTaskNumber: number
400
+ ): string {
401
+ const taskList = existingTasks
402
+ .map((task) => {
403
+ const status =
404
+ task.status === 'completed'
405
+ ? '[COMPLETED]'
406
+ : task.status === 'failed'
407
+ ? '[FAILED]'
408
+ : '[PENDING]';
409
+ return `# ${task.id}: ${task.taskName} ${status}`;
410
+ })
411
+ .join('\n');
412
+
413
+ return `# Describe the new tasks you want to add
414
+ #
415
+ # Existing tasks (read-only reference):
416
+ ${taskList}
417
+ #
418
+ # New tasks will be numbered starting from ${nextTaskNumber.toString().padStart(3, '0')}
419
+ #
420
+ # Describe what you want to add below:
421
+ `;
422
+ }
@@ -0,0 +1,146 @@
1
+ import { Command } from 'commander';
2
+ import { getRafDir, resolveProjectIdentifier, extractProjectName } from '../utils/paths.js';
3
+ import { logger } from '../utils/logger.js';
4
+ import type { StatusCommandOptions } from '../types/config.js';
5
+ import {
6
+ deriveProjectState,
7
+ getDerivedStats,
8
+ discoverProjects,
9
+ type DerivedTaskStatus,
10
+ } from '../core/state-derivation.js';
11
+ import { SYMBOLS, formatProgressBar, type TaskStatus } from '../utils/terminal-symbols.js';
12
+
13
+ /** Maximum number of projects to display in status list */
14
+ const MAX_DISPLAYED_PROJECTS = 10;
15
+
16
+ export function createStatusCommand(): Command {
17
+ const command = new Command('status')
18
+ .description('Show status of a project or list all projects')
19
+ .argument('[identifier]', 'Project identifier: number (3), name (my-project), or folder (001-my-project)')
20
+ .option('--json', 'Output as JSON')
21
+ .action(async (identifier?: string, options?: StatusCommandOptions) => {
22
+ await runStatusCommand(identifier, options);
23
+ });
24
+
25
+ return command;
26
+ }
27
+
28
+ async function runStatusCommand(
29
+ identifier?: string,
30
+ options?: StatusCommandOptions
31
+ ): Promise<void> {
32
+ const rafDir = getRafDir();
33
+
34
+ if (!identifier) {
35
+ // List all projects
36
+ await listAllProjects(rafDir, options);
37
+ return;
38
+ }
39
+
40
+ // Show specific project - resolve identifier (number, name, or full folder name)
41
+ const projectPath = resolveProjectIdentifier(rafDir, identifier);
42
+
43
+ if (!projectPath) {
44
+ logger.error(`Project not found: ${identifier}`);
45
+ process.exit(1);
46
+ }
47
+
48
+ // Derive state from folder structure
49
+ const state = deriveProjectState(projectPath);
50
+ const stats = getDerivedStats(state);
51
+ const projectName = extractProjectName(projectPath) ?? identifier;
52
+ const projectStatus = state.status;
53
+
54
+ if (options?.json) {
55
+ console.log(JSON.stringify({ projectName, status: projectStatus, state, stats }, null, 2));
56
+ return;
57
+ }
58
+
59
+ // Convert derived task statuses to TaskStatus for progress bar
60
+ const taskStatuses: TaskStatus[] = state.tasks.map((t) => derivedStatusToTaskStatus(t.status));
61
+ const progressBar = formatProgressBar(taskStatuses);
62
+
63
+ // Display compact project status
64
+ logger.info(`${SYMBOLS.project} ${projectName}`);
65
+ logger.info(`${progressBar} (${stats.completed}/${stats.total})`);
66
+ }
67
+
68
+ async function listAllProjects(
69
+ rafDir: string,
70
+ options?: StatusCommandOptions
71
+ ): Promise<void> {
72
+ const allProjects = discoverProjects(rafDir);
73
+
74
+ if (allProjects.length === 0) {
75
+ logger.info('No projects found.');
76
+ return;
77
+ }
78
+
79
+ if (options?.json) {
80
+ const projectsWithState = allProjects.map((p) => {
81
+ try {
82
+ const state = deriveProjectState(p.path);
83
+ const stats = getDerivedStats(state);
84
+ return {
85
+ ...p,
86
+ status: state.status,
87
+ state,
88
+ stats,
89
+ };
90
+ } catch {
91
+ return { ...p, status: null, state: null, stats: null };
92
+ }
93
+ });
94
+ console.log(JSON.stringify(projectsWithState, null, 2));
95
+ return;
96
+ }
97
+
98
+ // Truncate to last N projects if needed (projects are sorted by number ascending)
99
+ const totalProjects = allProjects.length;
100
+ const hiddenCount = Math.max(0, totalProjects - MAX_DISPLAYED_PROJECTS);
101
+ const displayedProjects = hiddenCount > 0
102
+ ? allProjects.slice(-MAX_DISPLAYED_PROJECTS)
103
+ : allProjects;
104
+
105
+ // Show truncation indicator at top if there are hidden projects
106
+ if (hiddenCount > 0) {
107
+ logger.dim(`... and ${hiddenCount} more project${hiddenCount === 1 ? '' : 's'}`);
108
+ }
109
+
110
+ for (const project of displayedProjects) {
111
+ try {
112
+ const state = deriveProjectState(project.path);
113
+ const stats = getDerivedStats(state);
114
+
115
+ // Convert derived task statuses to TaskStatus for progress bar
116
+ const taskStatuses: TaskStatus[] = state.tasks.map((t) => derivedStatusToTaskStatus(t.status));
117
+ const progressBar = formatProgressBar(taskStatuses);
118
+
119
+ // Format: "001 my-project ✓✓●○○ (2/5)"
120
+ const projectNumber = String(project.number).padStart(3, '0');
121
+ const counts = `(${stats.completed}/${stats.total})`;
122
+ logger.info(`${projectNumber} ${project.name} ${progressBar} ${counts}`);
123
+ } catch {
124
+ // Failed to derive state - show minimal info
125
+ const projectNumber = String(project.number).padStart(3, '0');
126
+ logger.info(`${projectNumber} ${project.name}`);
127
+ }
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Convert DerivedTaskStatus to TaskStatus for terminal symbols.
133
+ * Note: DerivedTaskStatus doesn't have 'running' - tasks are either pending, completed, or failed.
134
+ */
135
+ function derivedStatusToTaskStatus(status: DerivedTaskStatus): TaskStatus {
136
+ switch (status) {
137
+ case 'pending':
138
+ return 'pending';
139
+ case 'completed':
140
+ return 'completed';
141
+ case 'failed':
142
+ return 'failed';
143
+ default:
144
+ return 'pending';
145
+ }
146
+ }