rafcode 1.2.0 → 2.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 (398) hide show
  1. package/CLAUDE.md +118 -22
  2. package/RAF/aaaabt-verbose-chronicle/decisions.md +25 -0
  3. package/RAF/aaaabt-verbose-chronicle/input.md +3 -0
  4. package/RAF/aaaabt-verbose-chronicle/outcomes/01-amend-iteration-references.md +25 -0
  5. package/RAF/aaaabt-verbose-chronicle/outcomes/02-verbose-task-name-display.md +31 -0
  6. package/RAF/aaaabt-verbose-chronicle/outcomes/03-verbose-streaming-fix.md +48 -0
  7. package/RAF/aaaabt-verbose-chronicle/outcomes/04-commit-verification-before-halt.md +56 -0
  8. package/RAF/aaaabt-verbose-chronicle/plans/01-amend-iteration-references.md +35 -0
  9. package/RAF/aaaabt-verbose-chronicle/plans/02-verbose-task-name-display.md +38 -0
  10. package/RAF/aaaabt-verbose-chronicle/plans/03-verbose-streaming-fix.md +45 -0
  11. package/RAF/aaaabt-verbose-chronicle/plans/04-commit-verification-before-halt.md +62 -0
  12. package/RAF/aaaacu-worktree-weaver/decisions.md +88 -0
  13. package/RAF/aaaacu-worktree-weaver/input.md +27 -0
  14. package/RAF/aaaacu-worktree-weaver/outcomes/01-git-worktree-utilities.md +41 -0
  15. package/RAF/aaaacu-worktree-weaver/outcomes/02-worktree-plan-command.md +44 -0
  16. package/RAF/aaaacu-worktree-weaver/outcomes/03-worktree-do-command.md +58 -0
  17. package/RAF/aaaacu-worktree-weaver/outcomes/04-auto-merge-and-cleanup.md +40 -0
  18. package/RAF/aaaacu-worktree-weaver/outcomes/05-worktree-tests.md +43 -0
  19. package/RAF/aaaacu-worktree-weaver/outcomes/06-update-documentation.md +33 -0
  20. package/RAF/aaaacu-worktree-weaver/plans/01-git-worktree-utilities.md +51 -0
  21. package/RAF/aaaacu-worktree-weaver/plans/02-worktree-plan-command.md +93 -0
  22. package/RAF/aaaacu-worktree-weaver/plans/03-worktree-do-command.md +81 -0
  23. package/RAF/aaaacu-worktree-weaver/plans/04-auto-merge-and-cleanup.md +51 -0
  24. package/RAF/aaaacu-worktree-weaver/plans/05-worktree-tests.md +52 -0
  25. package/RAF/aaaacu-worktree-weaver/plans/06-update-documentation.md +55 -0
  26. package/RAF/aaaacv-trim-the-fat/decisions.md +34 -0
  27. package/RAF/aaaacv-trim-the-fat/input.md +5 -0
  28. package/RAF/aaaacv-trim-the-fat/outcomes/01-add-worktree-support-to-status.md +43 -0
  29. package/RAF/aaaacv-trim-the-fat/outcomes/02-remove-multi-project-from-do.md +50 -0
  30. package/RAF/aaaacv-trim-the-fat/outcomes/03-commit-artifacts-on-amend.md +35 -0
  31. package/RAF/aaaacv-trim-the-fat/outcomes/04-worktree-aware-exit-messages.md +36 -0
  32. package/RAF/aaaacv-trim-the-fat/plans/01-add-worktree-support-to-status.md +43 -0
  33. package/RAF/aaaacv-trim-the-fat/plans/02-remove-multi-project-from-do.md +44 -0
  34. package/RAF/aaaacv-trim-the-fat/plans/03-commit-artifacts-on-amend.md +38 -0
  35. package/RAF/aaaacv-trim-the-fat/plans/04-worktree-aware-exit-messages.md +38 -0
  36. package/RAF/aaaacw-prune-cycle/decisions.md +25 -0
  37. package/RAF/aaaacw-prune-cycle/input.md +5 -0
  38. package/RAF/aaaacw-prune-cycle/outcomes/01-create-worktree-from-branch.md +32 -0
  39. package/RAF/aaaacw-prune-cycle/outcomes/02-cleanup-worktree-on-success.md +33 -0
  40. package/RAF/aaaacw-prune-cycle/outcomes/03-amend-recreate-worktree.md +40 -0
  41. package/RAF/aaaacw-prune-cycle/plans/01-create-worktree-from-branch.md +31 -0
  42. package/RAF/aaaacw-prune-cycle/plans/02-cleanup-worktree-on-success.md +38 -0
  43. package/RAF/aaaacw-prune-cycle/plans/03-amend-recreate-worktree.md +50 -0
  44. package/RAF/aaaacx-epoch-shift/decisions.md +25 -0
  45. package/RAF/aaaacx-epoch-shift/input.md +1 -0
  46. package/RAF/aaaacx-epoch-shift/outcomes/01-epoch-id-generation.md +34 -0
  47. package/RAF/aaaacx-epoch-shift/outcomes/02-update-pattern-matching.md +32 -0
  48. package/RAF/aaaacx-epoch-shift/outcomes/03-update-tests.md +59 -0
  49. package/RAF/aaaacx-epoch-shift/outcomes/04-update-documentation.md +30 -0
  50. package/RAF/aaaacx-epoch-shift/plans/01-epoch-id-generation.md +40 -0
  51. package/RAF/aaaacx-epoch-shift/plans/02-update-pattern-matching.md +60 -0
  52. package/RAF/aaaacx-epoch-shift/plans/03-update-tests.md +48 -0
  53. package/RAF/aaaacx-epoch-shift/plans/04-update-documentation.md +44 -0
  54. package/RAF/ahmpro-merge-guardian/decisions.md +25 -0
  55. package/RAF/ahmpro-merge-guardian/input.md +4 -0
  56. package/RAF/ahmpro-merge-guardian/outcomes/001-fix-amend-worktree-commit.md +45 -0
  57. package/RAF/ahmpro-merge-guardian/outcomes/002-base36-task-ids.md +55 -0
  58. package/RAF/ahmpro-merge-guardian/outcomes/003-worktree-pr-creation.md +41 -0
  59. package/RAF/ahmpro-merge-guardian/outcomes/004-post-execution-picker.md +53 -0
  60. package/RAF/ahmpro-merge-guardian/plans/001-fix-amend-worktree-commit.md +39 -0
  61. package/RAF/ahmpro-merge-guardian/plans/002-base36-task-ids.md +43 -0
  62. package/RAF/ahmpro-merge-guardian/plans/003-worktree-pr-creation.md +43 -0
  63. package/RAF/ahmpro-merge-guardian/plans/004-post-execution-picker.md +51 -0
  64. package/RAF/ahnbcu-letterjam/decisions.md +13 -0
  65. package/RAF/ahnbcu-letterjam/input.md +3 -0
  66. package/RAF/ahnbcu-letterjam/outcomes/01-base26-encoding.md +42 -0
  67. package/RAF/ahnbcu-letterjam/outcomes/02-update-tests.md +38 -0
  68. package/RAF/ahnbcu-letterjam/outcomes/03-migrate-command.md +51 -0
  69. package/RAF/ahnbcu-letterjam/outcomes/04-update-documentation.md +29 -0
  70. package/RAF/ahnbcu-letterjam/plans/01-base26-encoding.md +36 -0
  71. package/RAF/ahnbcu-letterjam/plans/02-update-tests.md +37 -0
  72. package/RAF/ahnbcu-letterjam/plans/03-migrate-command.md +49 -0
  73. package/RAF/ahnbcu-letterjam/plans/04-update-documentation.md +39 -0
  74. package/RAF/ahnwrk-worktree-weaver/decisions.md +19 -0
  75. package/RAF/ahnwrk-worktree-weaver/input.md +4 -0
  76. package/RAF/ahnwrk-worktree-weaver/outcomes/01-remove-co-authored-by.md +20 -0
  77. package/RAF/ahnwrk-worktree-weaver/outcomes/02-update-task-status-format.md +21 -0
  78. package/RAF/ahnwrk-worktree-weaver/outcomes/03-worktree-auto-discovery.md +34 -0
  79. package/RAF/ahnwrk-worktree-weaver/outcomes/04-fix-pr-description.md +30 -0
  80. package/RAF/ahnwrk-worktree-weaver/plans/01-remove-co-authored-by.md +26 -0
  81. package/RAF/ahnwrk-worktree-weaver/plans/02-update-task-status-format.md +27 -0
  82. package/RAF/ahnwrk-worktree-weaver/plans/03-worktree-auto-discovery.md +37 -0
  83. package/RAF/ahnwrk-worktree-weaver/plans/04-fix-pr-description.md +50 -0
  84. package/README.md +56 -10
  85. package/dist/commands/do.d.ts +15 -0
  86. package/dist/commands/do.d.ts.map +1 -1
  87. package/dist/commands/do.js +400 -131
  88. package/dist/commands/do.js.map +1 -1
  89. package/dist/commands/migrate.d.ts +14 -0
  90. package/dist/commands/migrate.d.ts.map +1 -0
  91. package/dist/commands/migrate.js +228 -0
  92. package/dist/commands/migrate.js.map +1 -0
  93. package/dist/commands/plan.d.ts.map +1 -1
  94. package/dist/commands/plan.js +237 -40
  95. package/dist/commands/plan.js.map +1 -1
  96. package/dist/commands/status.d.ts.map +1 -1
  97. package/dist/commands/status.js +221 -47
  98. package/dist/commands/status.js.map +1 -1
  99. package/dist/core/claude-runner.d.ts +52 -1
  100. package/dist/core/claude-runner.d.ts.map +1 -1
  101. package/dist/core/claude-runner.js +195 -17
  102. package/dist/core/claude-runner.js.map +1 -1
  103. package/dist/core/git.d.ts +29 -5
  104. package/dist/core/git.d.ts.map +1 -1
  105. package/dist/core/git.js +95 -18
  106. package/dist/core/git.js.map +1 -1
  107. package/dist/core/project-manager.d.ts.map +1 -1
  108. package/dist/core/project-manager.js +2 -2
  109. package/dist/core/project-manager.js.map +1 -1
  110. package/dist/core/pull-request.d.ts +84 -0
  111. package/dist/core/pull-request.d.ts.map +1 -0
  112. package/dist/core/pull-request.js +414 -0
  113. package/dist/core/pull-request.js.map +1 -0
  114. package/dist/core/state-derivation.d.ts +3 -3
  115. package/dist/core/state-derivation.d.ts.map +1 -1
  116. package/dist/core/state-derivation.js +18 -14
  117. package/dist/core/state-derivation.js.map +1 -1
  118. package/dist/core/worktree.d.ts +120 -0
  119. package/dist/core/worktree.d.ts.map +1 -0
  120. package/dist/core/worktree.js +322 -0
  121. package/dist/core/worktree.js.map +1 -0
  122. package/dist/index.js +2 -0
  123. package/dist/index.js.map +1 -1
  124. package/dist/parsers/stream-renderer.d.ts +42 -0
  125. package/dist/parsers/stream-renderer.d.ts.map +1 -0
  126. package/dist/parsers/stream-renderer.js +100 -0
  127. package/dist/parsers/stream-renderer.js.map +1 -0
  128. package/dist/prompts/amend.d.ts +1 -0
  129. package/dist/prompts/amend.d.ts.map +1 -1
  130. package/dist/prompts/amend.js +25 -10
  131. package/dist/prompts/amend.js.map +1 -1
  132. package/dist/prompts/execution.js +5 -5
  133. package/dist/prompts/execution.js.map +1 -1
  134. package/dist/prompts/planning.d.ts +1 -0
  135. package/dist/prompts/planning.d.ts.map +1 -1
  136. package/dist/prompts/planning.js +11 -10
  137. package/dist/prompts/planning.js.map +1 -1
  138. package/dist/types/config.d.ts +5 -0
  139. package/dist/types/config.d.ts.map +1 -1
  140. package/dist/ui/project-picker.d.ts +34 -3
  141. package/dist/ui/project-picker.d.ts.map +1 -1
  142. package/dist/ui/project-picker.js +78 -10
  143. package/dist/ui/project-picker.js.map +1 -1
  144. package/dist/utils/paths.d.ts +38 -43
  145. package/dist/utils/paths.d.ts.map +1 -1
  146. package/dist/utils/paths.js +123 -193
  147. package/dist/utils/paths.js.map +1 -1
  148. package/dist/utils/terminal-symbols.d.ts +2 -2
  149. package/dist/utils/terminal-symbols.js +3 -3
  150. package/dist/utils/terminal-symbols.js.map +1 -1
  151. package/dist/utils/validation.d.ts.map +1 -1
  152. package/dist/utils/validation.js +2 -8
  153. package/dist/utils/validation.js.map +1 -1
  154. package/package.json +1 -1
  155. package/src/commands/do.ts +471 -142
  156. package/src/commands/migrate.ts +269 -0
  157. package/src/commands/plan.ts +264 -40
  158. package/src/commands/status.ts +252 -45
  159. package/src/core/claude-runner.ts +270 -17
  160. package/src/core/git.ts +99 -19
  161. package/src/core/project-manager.ts +2 -1
  162. package/src/core/pull-request.ts +480 -0
  163. package/src/core/state-derivation.ts +18 -14
  164. package/src/core/worktree.ts +357 -0
  165. package/src/index.ts +2 -0
  166. package/src/parsers/stream-renderer.ts +139 -0
  167. package/src/prompts/amend.ts +27 -9
  168. package/src/prompts/execution.ts +5 -5
  169. package/src/prompts/planning.ts +12 -10
  170. package/src/types/config.ts +6 -0
  171. package/src/ui/project-picker.ts +110 -10
  172. package/src/utils/paths.ts +129 -214
  173. package/src/utils/terminal-symbols.ts +3 -3
  174. package/src/utils/validation.ts +2 -9
  175. package/tests/unit/amend-prompt.test.ts +85 -0
  176. package/tests/unit/claude-runner.test.ts +567 -1
  177. package/tests/unit/commit-planning-artifacts-worktree.test.ts +327 -0
  178. package/tests/unit/commit-planning-artifacts.test.ts +303 -35
  179. package/tests/unit/dependency-integration.test.ts +95 -95
  180. package/tests/unit/do-blocked-tasks.test.ts +53 -53
  181. package/tests/unit/do-command.test.ts +39 -132
  182. package/tests/unit/do-rerun.test.ts +65 -65
  183. package/tests/unit/do-worktree-cleanup.test.ts +151 -0
  184. package/tests/unit/execution-prompt.test.ts +71 -60
  185. package/tests/unit/failure-history.test.ts +19 -19
  186. package/tests/unit/git-commit-helpers.test.ts +103 -0
  187. package/tests/unit/git-stash.test.ts +4 -4
  188. package/tests/unit/migrate-command.test.ts +197 -0
  189. package/tests/unit/outcome-content.test.ts +20 -20
  190. package/tests/unit/paths.test.ts +269 -467
  191. package/tests/unit/plan-amend-worktree-recreate.test.ts +246 -0
  192. package/tests/unit/plan-command.test.ts +144 -93
  193. package/tests/unit/planning-prompt.test.ts +41 -4
  194. package/tests/unit/post-execution-picker.test.ts +251 -0
  195. package/tests/unit/project-manager.test.ts +20 -8
  196. package/tests/unit/project-picker.test.ts +425 -42
  197. package/tests/unit/pull-request.test.ts +852 -0
  198. package/tests/unit/state-derivation.test.ts +138 -137
  199. package/tests/unit/status-command.test.ts +344 -76
  200. package/tests/unit/stream-renderer.test.ts +286 -0
  201. package/tests/unit/terminal-symbols.test.ts +4 -4
  202. package/tests/unit/worktree-integration.test.ts +405 -0
  203. package/tests/unit/worktree.test.ts +523 -0
  204. package/tests/unit/do-multiproject.test.ts +0 -270
  205. /package/RAF/{001-raf-task-improvements → aaaaab-raf-task-improvements}/input.md +0 -0
  206. /package/RAF/{001-raf-task-improvements/outcomes/001-add-decisions-folder.md → aaaaab-raf-task-improvements/outcomes/01-add-decisions-folder.md} +0 -0
  207. /package/RAF/{001-raf-task-improvements/outcomes/002-fix-write-error-on-shutdown.md → aaaaab-raf-task-improvements/outcomes/02-fix-write-error-on-shutdown.md} +0 -0
  208. /package/RAF/{001-raf-task-improvements/outcomes/003-stash-changes-on-failure.md → aaaaab-raf-task-improvements/outcomes/03-stash-changes-on-failure.md} +0 -0
  209. /package/RAF/{001-raf-task-improvements/outcomes/004-add-project-name-to-commits.md → aaaaab-raf-task-improvements/outcomes/04-add-project-name-to-commits.md} +0 -0
  210. /package/RAF/{001-raf-task-improvements/outcomes/005-add-running-time-display.md → aaaaab-raf-task-improvements/outcomes/05-add-running-time-display.md} +0 -0
  211. /package/RAF/{001-raf-task-improvements/outcomes/006-add-task-name-to-logs.md → aaaaab-raf-task-improvements/outcomes/06-add-task-name-to-logs.md} +0 -0
  212. /package/RAF/{001-raf-task-improvements/outcomes/007-show-model-at-task-start.md → aaaaab-raf-task-improvements/outcomes/07-show-model-at-task-start.md} +0 -0
  213. /package/RAF/{001-raf-task-improvements/outcomes/009-remove-editor-placeholder-text.md → aaaaab-raf-task-improvements/outcomes/09-remove-editor-placeholder-text.md} +0 -0
  214. /package/RAF/{001-raf-task-improvements → aaaaab-raf-task-improvements}/outcomes/SUMMARY.md +0 -0
  215. /package/RAF/{001-raf-task-improvements/plans/001-add-decisions-folder.md → aaaaab-raf-task-improvements/plans/01-add-decisions-folder.md} +0 -0
  216. /package/RAF/{001-raf-task-improvements/plans/002-fix-write-error-on-shutdown.md → aaaaab-raf-task-improvements/plans/02-fix-write-error-on-shutdown.md} +0 -0
  217. /package/RAF/{001-raf-task-improvements/plans/003-stash-changes-on-failure.md → aaaaab-raf-task-improvements/plans/03-stash-changes-on-failure.md} +0 -0
  218. /package/RAF/{001-raf-task-improvements/plans/004-add-project-name-to-commits.md → aaaaab-raf-task-improvements/plans/04-add-project-name-to-commits.md} +0 -0
  219. /package/RAF/{001-raf-task-improvements/plans/005-add-running-time-display.md → aaaaab-raf-task-improvements/plans/05-add-running-time-display.md} +0 -0
  220. /package/RAF/{001-raf-task-improvements/plans/006-add-task-name-to-logs.md → aaaaab-raf-task-improvements/plans/06-add-task-name-to-logs.md} +0 -0
  221. /package/RAF/{001-raf-task-improvements/plans/009-remove-editor-placeholder-text.md → aaaaab-raf-task-improvements/plans/09-remove-editor-placeholder-text.md} +0 -0
  222. /package/RAF/{002-raf-task-improvements-execution → aaaaac-raf-task-improvements-execution}/decisions/DECISIONS.md +0 -0
  223. /package/RAF/{002-raf-task-improvements-execution → aaaaac-raf-task-improvements-execution}/input.md +0 -0
  224. /package/RAF/{002-raf-task-improvements-execution/outcomes/001-commit-show-model-at-task-start.md → aaaaac-raf-task-improvements-execution/outcomes/01-commit-show-model-at-task-start.md} +0 -0
  225. /package/RAF/{002-raf-task-improvements-execution/outcomes/002-delete-skipped-plan.md → aaaaac-raf-task-improvements-execution/outcomes/02-delete-skipped-plan.md} +0 -0
  226. /package/RAF/{002-raf-task-improvements-execution → aaaaac-raf-task-improvements-execution}/outcomes/SUMMARY.md +0 -0
  227. /package/RAF/{002-raf-task-improvements-execution/plans/001-commit-show-model-at-task-start.md → aaaaac-raf-task-improvements-execution/plans/01-commit-show-model-at-task-start.md} +0 -0
  228. /package/RAF/{002-raf-task-improvements-execution/plans/002-delete-skipped-plan.md → aaaaac-raf-task-improvements-execution/plans/02-delete-skipped-plan.md} +0 -0
  229. /package/RAF/{003-multi-project-execution → aaaaad-multi-project-execution}/decisions/DECISIONS.md +0 -0
  230. /package/RAF/{003-multi-project-execution → aaaaad-multi-project-execution}/input.md +0 -0
  231. /package/RAF/{003-multi-project-execution/outcomes/001-remove-state-json.md → aaaaad-multi-project-execution/outcomes/01-remove-state-json.md} +0 -0
  232. /package/RAF/{003-multi-project-execution/outcomes/002-update-raf-status.md → aaaaad-multi-project-execution/outcomes/02-update-raf-status.md} +0 -0
  233. /package/RAF/{003-multi-project-execution/outcomes/003-simplify-git-logic.md → aaaaad-multi-project-execution/outcomes/03-simplify-git-logic.md} +0 -0
  234. /package/RAF/{003-multi-project-execution/outcomes/004-auto-commit-planning.md → aaaaad-multi-project-execution/outcomes/04-auto-commit-planning.md} +0 -0
  235. /package/RAF/{003-multi-project-execution/outcomes/005-rerun-failed-tasks.md → aaaaad-multi-project-execution/outcomes/05-rerun-failed-tasks.md} +0 -0
  236. /package/RAF/{003-multi-project-execution/outcomes/006-multi-project-execution.md → aaaaad-multi-project-execution/outcomes/06-multi-project-execution.md} +0 -0
  237. /package/RAF/{003-multi-project-execution/outcomes/007-verify-timeout.md → aaaaad-multi-project-execution/outcomes/07-verify-timeout.md} +0 -0
  238. /package/RAF/{003-multi-project-execution/outcomes/008-move-decisions-file.md → aaaaad-multi-project-execution/outcomes/08-move-decisions-file.md} +0 -0
  239. /package/RAF/{003-multi-project-execution → aaaaad-multi-project-execution}/outcomes/SUMMARY.md +0 -0
  240. /package/RAF/{003-multi-project-execution/plans/001-remove-state-json.md → aaaaad-multi-project-execution/plans/01-remove-state-json.md} +0 -0
  241. /package/RAF/{003-multi-project-execution/plans/002-update-raf-status.md → aaaaad-multi-project-execution/plans/02-update-raf-status.md} +0 -0
  242. /package/RAF/{003-multi-project-execution/plans/003-simplify-git-logic.md → aaaaad-multi-project-execution/plans/03-simplify-git-logic.md} +0 -0
  243. /package/RAF/{003-multi-project-execution/plans/004-auto-commit-planning.md → aaaaad-multi-project-execution/plans/04-auto-commit-planning.md} +0 -0
  244. /package/RAF/{003-multi-project-execution/plans/005-rerun-failed-tasks.md → aaaaad-multi-project-execution/plans/05-rerun-failed-tasks.md} +0 -0
  245. /package/RAF/{003-multi-project-execution/plans/006-multi-project-execution.md → aaaaad-multi-project-execution/plans/06-multi-project-execution.md} +0 -0
  246. /package/RAF/{003-multi-project-execution/plans/007-verify-timeout.md → aaaaad-multi-project-execution/plans/07-verify-timeout.md} +0 -0
  247. /package/RAF/{003-multi-project-execution/plans/008-move-decisions-file.md → aaaaad-multi-project-execution/plans/08-move-decisions-file.md} +0 -0
  248. /package/RAF/{004-task-naming-optimization → aaaaae-task-naming-optimization}/decisions.md +0 -0
  249. /package/RAF/{004-task-naming-optimization → aaaaae-task-naming-optimization}/input.md +0 -0
  250. /package/RAF/{004-task-naming-optimization/outcomes/001-remove-summary-file.md → aaaaae-task-naming-optimization/outcomes/01-remove-summary-file.md} +0 -0
  251. /package/RAF/{004-task-naming-optimization/outcomes/002-base36-project-numbering.md → aaaaae-task-naming-optimization/outcomes/02-base36-project-numbering.md} +0 -0
  252. /package/RAF/{004-task-naming-optimization/outcomes/003-improve-haiku-prompt.md → aaaaae-task-naming-optimization/outcomes/03-improve-haiku-prompt.md} +0 -0
  253. /package/RAF/{004-task-naming-optimization → aaaaae-task-naming-optimization}/outcomes/SUMMARY.md +0 -0
  254. /package/RAF/{004-task-naming-optimization/plans/001-remove-summary-file.md → aaaaae-task-naming-optimization/plans/01-remove-summary-file.md} +0 -0
  255. /package/RAF/{004-task-naming-optimization/plans/002-base36-project-numbering.md → aaaaae-task-naming-optimization/plans/02-base36-project-numbering.md} +0 -0
  256. /package/RAF/{004-task-naming-optimization/plans/003-improve-haiku-prompt.md → aaaaae-task-naming-optimization/plans/03-improve-haiku-prompt.md} +0 -0
  257. /package/RAF/{005-task-naming-improvements → aaaaaf-task-naming-improvements}/decisions.md +0 -0
  258. /package/RAF/{005-task-naming-improvements → aaaaaf-task-naming-improvements}/input.md +0 -0
  259. /package/RAF/{005-task-naming-improvements/outcomes/001-enhance-identifier-resolution.md → aaaaaf-task-naming-improvements/outcomes/01-enhance-identifier-resolution.md} +0 -0
  260. /package/RAF/{005-task-naming-improvements/outcomes/002-add-identifier-support-to-status.md → aaaaaf-task-naming-improvements/outcomes/02-add-identifier-support-to-status.md} +0 -0
  261. /package/RAF/{005-task-naming-improvements/outcomes/003-update-do-for-full-folder-names.md → aaaaaf-task-naming-improvements/outcomes/03-update-do-for-full-folder-names.md} +0 -0
  262. /package/RAF/{005-task-naming-improvements/outcomes/004-implement-amend-flag-for-plan.md → aaaaaf-task-naming-improvements/outcomes/04-implement-amend-flag-for-plan.md} +0 -0
  263. /package/RAF/{005-task-naming-improvements/outcomes/005-commit-outcomes-on-complete.md → aaaaaf-task-naming-improvements/outcomes/05-commit-outcomes-on-complete.md} +0 -0
  264. /package/RAF/{005-task-naming-improvements/outcomes/006-update-execution-prompt-commit-schema.md → aaaaaf-task-naming-improvements/outcomes/06-update-execution-prompt-commit-schema.md} +0 -0
  265. /package/RAF/{005-task-naming-improvements/outcomes/007-allow-pending-task-amendments.md → aaaaaf-task-naming-improvements/outcomes/07-allow-pending-task-amendments.md} +0 -0
  266. /package/RAF/{005-task-naming-improvements/outcomes/008-fix-timeout-label.md → aaaaaf-task-naming-improvements/outcomes/08-fix-timeout-label.md} +0 -0
  267. /package/RAF/{005-task-naming-improvements/plans/001-enhance-identifier-resolution.md → aaaaaf-task-naming-improvements/plans/01-enhance-identifier-resolution.md} +0 -0
  268. /package/RAF/{005-task-naming-improvements/plans/002-add-identifier-support-to-status.md → aaaaaf-task-naming-improvements/plans/02-add-identifier-support-to-status.md} +0 -0
  269. /package/RAF/{005-task-naming-improvements/plans/003-update-do-for-full-folder-names.md → aaaaaf-task-naming-improvements/plans/03-update-do-for-full-folder-names.md} +0 -0
  270. /package/RAF/{005-task-naming-improvements/plans/004-implement-amend-flag-for-plan.md → aaaaaf-task-naming-improvements/plans/04-implement-amend-flag-for-plan.md} +0 -0
  271. /package/RAF/{005-task-naming-improvements/plans/005-commit-outcomes-on-complete.md → aaaaaf-task-naming-improvements/plans/05-commit-outcomes-on-complete.md} +0 -0
  272. /package/RAF/{005-task-naming-improvements/plans/006-update-execution-prompt-commit-schema.md → aaaaaf-task-naming-improvements/plans/06-update-execution-prompt-commit-schema.md} +0 -0
  273. /package/RAF/{005-task-naming-improvements/plans/007-allow-pending-task-amendments.md → aaaaaf-task-naming-improvements/plans/07-allow-pending-task-amendments.md} +0 -0
  274. /package/RAF/{005-task-naming-improvements/plans/008-fix-timeout-label.md → aaaaaf-task-naming-improvements/plans/08-fix-timeout-label.md} +0 -0
  275. /package/RAF/{006-fix-double-summary-headers → aaaaag-fix-double-summary-headers}/decisions.md +0 -0
  276. /package/RAF/{006-fix-double-summary-headers → aaaaag-fix-double-summary-headers}/input.md +0 -0
  277. /package/RAF/{006-fix-double-summary-headers/outcomes/001-fix-double-summary-headers.md → aaaaag-fix-double-summary-headers/outcomes/01-fix-double-summary-headers.md} +0 -0
  278. /package/RAF/{006-fix-double-summary-headers/outcomes/002-update-readme-for-npm.md → aaaaag-fix-double-summary-headers/outcomes/02-update-readme-for-npm.md} +0 -0
  279. /package/RAF/{006-fix-double-summary-headers/outcomes/003-npm-publish-instructions.md → aaaaag-fix-double-summary-headers/outcomes/03-npm-publish-instructions.md} +0 -0
  280. /package/RAF/{006-fix-double-summary-headers/outcomes/004-flexible-project-lookup.md → aaaaag-fix-double-summary-headers/outcomes/04-flexible-project-lookup.md} +0 -0
  281. /package/RAF/{006-fix-double-summary-headers/plans/001-fix-double-summary-headers.md → aaaaag-fix-double-summary-headers/plans/01-fix-double-summary-headers.md} +0 -0
  282. /package/RAF/{006-fix-double-summary-headers/plans/002-update-readme-for-npm.md → aaaaag-fix-double-summary-headers/plans/02-update-readme-for-npm.md} +0 -0
  283. /package/RAF/{006-fix-double-summary-headers/plans/003-npm-publish-instructions.md → aaaaag-fix-double-summary-headers/plans/03-npm-publish-instructions.md} +0 -0
  284. /package/RAF/{006-fix-double-summary-headers/plans/004-flexible-project-lookup.md → aaaaag-fix-double-summary-headers/plans/04-flexible-project-lookup.md} +0 -0
  285. /package/RAF/{007-improve-outcome-format → aaaaah-improve-outcome-format}/decisions.md +0 -0
  286. /package/RAF/{007-improve-outcome-format → aaaaah-improve-outcome-format}/input.md +0 -0
  287. /package/RAF/{007-improve-outcome-format/outcomes/001-update-execution-prompt.md → aaaaah-improve-outcome-format/outcomes/01-update-execution-prompt.md} +0 -0
  288. /package/RAF/{007-improve-outcome-format/outcomes/002-update-state-derivation.md → aaaaah-improve-outcome-format/outcomes/02-update-state-derivation.md} +0 -0
  289. /package/RAF/{007-improve-outcome-format/outcomes/003-update-do-command-outcome-handling.md → aaaaah-improve-outcome-format/outcomes/03-update-do-command-outcome-handling.md} +0 -0
  290. /package/RAF/{007-improve-outcome-format/outcomes/004-implement-failure-analysis.md → aaaaah-improve-outcome-format/outcomes/04-implement-failure-analysis.md} +0 -0
  291. /package/RAF/{007-improve-outcome-format/outcomes/005-update-documentation.md → aaaaah-improve-outcome-format/outcomes/05-update-documentation.md} +0 -0
  292. /package/RAF/{007-improve-outcome-format/plans/001-update-execution-prompt.md → aaaaah-improve-outcome-format/plans/01-update-execution-prompt.md} +0 -0
  293. /package/RAF/{007-improve-outcome-format/plans/002-update-state-derivation.md → aaaaah-improve-outcome-format/plans/02-update-state-derivation.md} +0 -0
  294. /package/RAF/{007-improve-outcome-format/plans/003-update-do-command-outcome-handling.md → aaaaah-improve-outcome-format/plans/03-update-do-command-outcome-handling.md} +0 -0
  295. /package/RAF/{007-improve-outcome-format/plans/004-implement-failure-analysis.md → aaaaah-improve-outcome-format/plans/04-implement-failure-analysis.md} +0 -0
  296. /package/RAF/{007-improve-outcome-format/plans/005-update-documentation.md → aaaaah-improve-outcome-format/plans/05-update-documentation.md} +0 -0
  297. /package/RAF/{008-beautiful-do → aaaaai-beautiful-do}/decisions.md +0 -0
  298. /package/RAF/{008-beautiful-do → aaaaai-beautiful-do}/input.md +0 -0
  299. /package/RAF/{008-beautiful-do/outcomes/001-terminal-symbols.md → aaaaai-beautiful-do/outcomes/01-terminal-symbols.md} +0 -0
  300. /package/RAF/{008-beautiful-do/outcomes/002-refactor-do-output.md → aaaaai-beautiful-do/outcomes/02-refactor-do-output.md} +0 -0
  301. /package/RAF/{008-beautiful-do/outcomes/003-refactor-status-output.md → aaaaai-beautiful-do/outcomes/03-refactor-status-output.md} +0 -0
  302. /package/RAF/{008-beautiful-do/outcomes/004-simplify-logger.md → aaaaai-beautiful-do/outcomes/04-simplify-logger.md} +0 -0
  303. /package/RAF/{008-beautiful-do/outcomes/005-add-tests.md → aaaaai-beautiful-do/outcomes/05-add-tests.md} +0 -0
  304. /package/RAF/{008-beautiful-do/plans/001-terminal-symbols.md → aaaaai-beautiful-do/plans/01-terminal-symbols.md} +0 -0
  305. /package/RAF/{008-beautiful-do/plans/002-refactor-do-output.md → aaaaai-beautiful-do/plans/02-refactor-do-output.md} +0 -0
  306. /package/RAF/{008-beautiful-do/plans/003-refactor-status-output.md → aaaaai-beautiful-do/plans/03-refactor-status-output.md} +0 -0
  307. /package/RAF/{008-beautiful-do/plans/004-simplify-logger.md → aaaaai-beautiful-do/plans/04-simplify-logger.md} +0 -0
  308. /package/RAF/{008-beautiful-do/plans/005-add-tests.md → aaaaai-beautiful-do/plans/05-add-tests.md} +0 -0
  309. /package/RAF/{009-system-promt-ammend → aaaaaj-system-promt-ammend}/decisions.md +0 -0
  310. /package/RAF/{009-system-promt-ammend → aaaaaj-system-promt-ammend}/input.md +0 -0
  311. /package/RAF/{009-system-promt-ammend/outcomes/001-model-override.md → aaaaaj-system-promt-ammend/outcomes/01-model-override.md} +0 -0
  312. /package/RAF/{009-system-promt-ammend/outcomes/002-system-prompt-append.md → aaaaaj-system-promt-ammend/outcomes/02-system-prompt-append.md} +0 -0
  313. /package/RAF/{009-system-promt-ammend/outcomes/003-retry-context.md → aaaaaj-system-promt-ammend/outcomes/03-retry-context.md} +0 -0
  314. /package/RAF/{009-system-promt-ammend/plans/001-model-override.md → aaaaaj-system-promt-ammend/plans/01-model-override.md} +0 -0
  315. /package/RAF/{009-system-promt-ammend/plans/002-system-prompt-append.md → aaaaaj-system-promt-ammend/plans/02-system-prompt-append.md} +0 -0
  316. /package/RAF/{009-system-promt-ammend/plans/003-retry-context.md → aaaaaj-system-promt-ammend/plans/03-retry-context.md} +0 -0
  317. /package/RAF/{010-outcome-marker-fallback → aaaabk-outcome-marker-fallback}/decisions.md +0 -0
  318. /package/RAF/{010-outcome-marker-fallback → aaaabk-outcome-marker-fallback}/input.md +0 -0
  319. /package/RAF/{010-outcome-marker-fallback/outcomes/001-outcome-file-marker-fallback.md → aaaabk-outcome-marker-fallback/outcomes/01-outcome-file-marker-fallback.md} +0 -0
  320. /package/RAF/{010-outcome-marker-fallback/outcomes/002-creative-project-naming.md → aaaabk-outcome-marker-fallback/outcomes/02-creative-project-naming.md} +0 -0
  321. /package/RAF/{010-outcome-marker-fallback/plans/001-outcome-file-marker-fallback.md → aaaabk-outcome-marker-fallback/plans/01-outcome-file-marker-fallback.md} +0 -0
  322. /package/RAF/{010-outcome-marker-fallback/plans/002-creative-project-naming.md → aaaabk-outcome-marker-fallback/plans/02-creative-project-naming.md} +0 -0
  323. /package/RAF/{011-do-task-in-commit → aaaabl-do-task-in-commit}/decisions.md +0 -0
  324. /package/RAF/{011-do-task-in-commit → aaaabl-do-task-in-commit}/input.md +0 -0
  325. /package/RAF/{011-do-task-in-commit/outcomes/001-update-execution-prompt.md → aaaabl-do-task-in-commit/outcomes/01-update-execution-prompt.md} +0 -0
  326. /package/RAF/{011-do-task-in-commit/outcomes/002-update-tests.md → aaaabl-do-task-in-commit/outcomes/02-update-tests.md} +0 -0
  327. /package/RAF/{011-do-task-in-commit/outcomes/003-update-documentation.md → aaaabl-do-task-in-commit/outcomes/03-update-documentation.md} +0 -0
  328. /package/RAF/{011-do-task-in-commit/plans/001-update-execution-prompt.md → aaaabl-do-task-in-commit/plans/01-update-execution-prompt.md} +0 -0
  329. /package/RAF/{011-do-task-in-commit/plans/002-update-tests.md → aaaabl-do-task-in-commit/plans/02-update-tests.md} +0 -0
  330. /package/RAF/{011-do-task-in-commit/plans/003-update-documentation.md → aaaabl-do-task-in-commit/plans/03-update-documentation.md} +0 -0
  331. /package/RAF/{012-name-picker-buffet → aaaabm-name-picker-buffet}/decisions.md +0 -0
  332. /package/RAF/{012-name-picker-buffet → aaaabm-name-picker-buffet}/input.md +0 -0
  333. /package/RAF/{012-name-picker-buffet/outcomes/001-name-picker-for-raf-plan.md → aaaabm-name-picker-buffet/outcomes/01-name-picker-for-raf-plan.md} +0 -0
  334. /package/RAF/{012-name-picker-buffet/outcomes/002-interactive-project-picker-for-raf-do.md → aaaabm-name-picker-buffet/outcomes/02-interactive-project-picker-for-raf-do.md} +0 -0
  335. /package/RAF/{012-name-picker-buffet/outcomes/003-raf-status-truncation.md → aaaabm-name-picker-buffet/outcomes/03-raf-status-truncation.md} +0 -0
  336. /package/RAF/{012-name-picker-buffet/outcomes/004-failure-reason-details.md → aaaabm-name-picker-buffet/outcomes/04-failure-reason-details.md} +0 -0
  337. /package/RAF/{012-name-picker-buffet/outcomes/005-remove-raf-commits.md → aaaabm-name-picker-buffet/outcomes/05-remove-raf-commits.md} +0 -0
  338. /package/RAF/{012-name-picker-buffet/outcomes/006-update-execution-prompt-for-commits.md → aaaabm-name-picker-buffet/outcomes/06-update-execution-prompt-for-commits.md} +0 -0
  339. /package/RAF/{012-name-picker-buffet/outcomes/007-fix-plan-mode-user-prompt.md → aaaabm-name-picker-buffet/outcomes/07-fix-plan-mode-user-prompt.md} +0 -0
  340. /package/RAF/{012-name-picker-buffet/outcomes/008-add-auto-flag-for-plan-mode.md → aaaabm-name-picker-buffet/outcomes/08-add-auto-flag-for-plan-mode.md} +0 -0
  341. /package/RAF/{012-name-picker-buffet/plans/001-name-picker-for-raf-plan.md → aaaabm-name-picker-buffet/plans/01-name-picker-for-raf-plan.md} +0 -0
  342. /package/RAF/{012-name-picker-buffet/plans/002-interactive-project-picker-for-raf-do.md → aaaabm-name-picker-buffet/plans/02-interactive-project-picker-for-raf-do.md} +0 -0
  343. /package/RAF/{012-name-picker-buffet/plans/003-raf-status-truncation.md → aaaabm-name-picker-buffet/plans/03-raf-status-truncation.md} +0 -0
  344. /package/RAF/{012-name-picker-buffet/plans/004-failure-reason-details.md → aaaabm-name-picker-buffet/plans/04-failure-reason-details.md} +0 -0
  345. /package/RAF/{012-name-picker-buffet/plans/005-remove-raf-commits.md → aaaabm-name-picker-buffet/plans/05-remove-raf-commits.md} +0 -0
  346. /package/RAF/{012-name-picker-buffet/plans/006-update-execution-prompt-for-commits.md → aaaabm-name-picker-buffet/plans/06-update-execution-prompt-for-commits.md} +0 -0
  347. /package/RAF/{012-name-picker-buffet/plans/007-fix-plan-mode-user-prompt.md → aaaabm-name-picker-buffet/plans/07-fix-plan-mode-user-prompt.md} +0 -0
  348. /package/RAF/{012-name-picker-buffet/plans/008-add-auto-flag-for-plan-mode.md → aaaabm-name-picker-buffet/plans/08-add-auto-flag-for-plan-mode.md} +0 -0
  349. /package/RAF/{013-dependencies-watchdog → aaaabn-dependencies-watchdog}/decisions.md +0 -0
  350. /package/RAF/{013-dependencies-watchdog → aaaabn-dependencies-watchdog}/input.md +0 -0
  351. /package/RAF/{013-dependencies-watchdog/outcomes/001-define-dependency-syntax.md → aaaabn-dependencies-watchdog/outcomes/01-define-dependency-syntax.md} +0 -0
  352. /package/RAF/{013-dependencies-watchdog/outcomes/002-update-planning-prompts.md → aaaabn-dependencies-watchdog/outcomes/02-update-planning-prompts.md} +0 -0
  353. /package/RAF/{013-dependencies-watchdog/outcomes/003-parse-dependencies-update-state.md → aaaabn-dependencies-watchdog/outcomes/03-parse-dependencies-update-state.md} +0 -0
  354. /package/RAF/{013-dependencies-watchdog/outcomes/004-implement-dependency-checking-in-do.md → aaaabn-dependencies-watchdog/outcomes/04-implement-dependency-checking-in-do.md} +0 -0
  355. /package/RAF/{013-dependencies-watchdog/outcomes/005-update-execution-prompts.md → aaaabn-dependencies-watchdog/outcomes/05-update-execution-prompts.md} +0 -0
  356. /package/RAF/{013-dependencies-watchdog/outcomes/006-add-tests.md → aaaabn-dependencies-watchdog/outcomes/06-add-tests.md} +0 -0
  357. /package/RAF/{013-dependencies-watchdog/outcomes/007-add-act-alias.md → aaaabn-dependencies-watchdog/outcomes/07-add-act-alias.md} +0 -0
  358. /package/RAF/{013-dependencies-watchdog/outcomes/008-add-exit-message.md → aaaabn-dependencies-watchdog/outcomes/08-add-exit-message.md} +0 -0
  359. /package/RAF/{013-dependencies-watchdog/plans/001-define-dependency-syntax.md → aaaabn-dependencies-watchdog/plans/01-define-dependency-syntax.md} +0 -0
  360. /package/RAF/{013-dependencies-watchdog/plans/002-update-planning-prompts.md → aaaabn-dependencies-watchdog/plans/02-update-planning-prompts.md} +0 -0
  361. /package/RAF/{013-dependencies-watchdog/plans/003-parse-dependencies-update-state.md → aaaabn-dependencies-watchdog/plans/03-parse-dependencies-update-state.md} +0 -0
  362. /package/RAF/{013-dependencies-watchdog/plans/004-implement-dependency-checking-in-do.md → aaaabn-dependencies-watchdog/plans/04-implement-dependency-checking-in-do.md} +0 -0
  363. /package/RAF/{013-dependencies-watchdog/plans/005-update-execution-prompts.md → aaaabn-dependencies-watchdog/plans/05-update-execution-prompts.md} +0 -0
  364. /package/RAF/{013-dependencies-watchdog/plans/006-add-tests.md → aaaabn-dependencies-watchdog/plans/06-add-tests.md} +0 -0
  365. /package/RAF/{013-dependencies-watchdog/plans/007-add-act-alias.md → aaaabn-dependencies-watchdog/plans/07-add-act-alias.md} +0 -0
  366. /package/RAF/{013-dependencies-watchdog/plans/008-add-exit-message.md → aaaabn-dependencies-watchdog/plans/08-add-exit-message.md} +0 -0
  367. /package/RAF/{014-watchdog → aaaabo-watchdog}/decisions.md +0 -0
  368. /package/RAF/{014-watchdog → aaaabo-watchdog}/input.md +0 -0
  369. /package/RAF/{014-watchdog/outcomes/001-amend-flag-position.md → aaaabo-watchdog/outcomes/01-amend-flag-position.md} +0 -0
  370. /package/RAF/{014-watchdog/outcomes/002-details-only-on-failure.md → aaaabo-watchdog/outcomes/02-details-only-on-failure.md} +0 -0
  371. /package/RAF/{014-watchdog/plans/001-amend-flag-position.md → aaaabo-watchdog/plans/01-amend-flag-position.md} +0 -0
  372. /package/RAF/{014-watchdog/plans/002-details-only-on-failure.md → aaaabo-watchdog/plans/02-details-only-on-failure.md} +0 -0
  373. /package/RAF/{015-name-lottery → aaaabp-name-lottery}/decisions.md +0 -0
  374. /package/RAF/{015-name-lottery → aaaabp-name-lottery}/input.md +0 -0
  375. /package/RAF/{015-name-lottery/outcomes/001-auto-pick-project-name.md → aaaabp-name-lottery/outcomes/01-auto-pick-project-name.md} +0 -0
  376. /package/RAF/{015-name-lottery/outcomes/002-mention-plan-files-in-commit.md → aaaabp-name-lottery/outcomes/02-mention-plan-files-in-commit.md} +0 -0
  377. /package/RAF/{015-name-lottery/outcomes/003-fix-input-md-in-amend-flow.md → aaaabp-name-lottery/outcomes/03-fix-input-md-in-amend-flow.md} +0 -0
  378. /package/RAF/{015-name-lottery/plans/001-auto-pick-project-name.md → aaaabp-name-lottery/plans/01-auto-pick-project-name.md} +0 -0
  379. /package/RAF/{015-name-lottery/plans/002-mention-plan-files-in-commit.md → aaaabp-name-lottery/plans/02-mention-plan-files-in-commit.md} +0 -0
  380. /package/RAF/{015-name-lottery/plans/003-fix-input-md-in-amend-flow.md → aaaabp-name-lottery/plans/03-fix-input-md-in-amend-flow.md} +0 -0
  381. /package/RAF/{016-planning-scalpel → aaaabq-planning-scalpel}/decisions.md +0 -0
  382. /package/RAF/{016-planning-scalpel → aaaabq-planning-scalpel}/input.md +0 -0
  383. /package/RAF/{016-planning-scalpel/outcomes/001-update-git-commit-instructions.md → aaaabq-planning-scalpel/outcomes/01-update-git-commit-instructions.md} +0 -0
  384. /package/RAF/{016-planning-scalpel/plans/001-update-git-commit-instructions.md → aaaabq-planning-scalpel/plans/01-update-git-commit-instructions.md} +0 -0
  385. /package/RAF/{017-decision-vault → aaaabr-decision-vault}/decisions.md +0 -0
  386. /package/RAF/{017-decision-vault → aaaabr-decision-vault}/input.md +0 -0
  387. /package/RAF/{017-decision-vault/outcomes/001-create-git-commit-utility.md → aaaabr-decision-vault/outcomes/01-create-git-commit-utility.md} +0 -0
  388. /package/RAF/{017-decision-vault/outcomes/002-integrate-commit-into-plan.md → aaaabr-decision-vault/outcomes/02-integrate-commit-into-plan.md} +0 -0
  389. /package/RAF/{017-decision-vault/outcomes/003-add-tests-for-planning-commit.md → aaaabr-decision-vault/outcomes/03-add-tests-for-planning-commit.md} +0 -0
  390. /package/RAF/{017-decision-vault/plans/001-create-git-commit-utility.md → aaaabr-decision-vault/plans/01-create-git-commit-utility.md} +0 -0
  391. /package/RAF/{017-decision-vault/plans/002-integrate-commit-into-plan.md → aaaabr-decision-vault/plans/02-integrate-commit-into-plan.md} +0 -0
  392. /package/RAF/{017-decision-vault/plans/003-add-tests-for-planning-commit.md → aaaabr-decision-vault/plans/03-add-tests-for-planning-commit.md} +0 -0
  393. /package/RAF/{018-workflow-forge → aaaabs-workflow-forge}/decisions.md +0 -0
  394. /package/RAF/{018-workflow-forge → aaaabs-workflow-forge}/input.md +0 -0
  395. /package/RAF/{018-workflow-forge/outcomes/001-add-task-number-progress.md → aaaabs-workflow-forge/outcomes/01-add-task-number-progress.md} +0 -0
  396. /package/RAF/{018-workflow-forge/outcomes/002-update-plan-do-prompts.md → aaaabs-workflow-forge/outcomes/02-update-plan-do-prompts.md} +0 -0
  397. /package/RAF/{018-workflow-forge/plans/001-add-task-number-progress.md → aaaabs-workflow-forge/plans/01-add-task-number-progress.md} +0 -0
  398. /package/RAF/{018-workflow-forge/plans/002-update-plan-do-prompts.md → aaaabs-workflow-forge/plans/02-update-plan-do-prompts.md} +0 -0
@@ -11,10 +11,11 @@ import {
11
11
  resolveProjectIdentifier,
12
12
  resolveProjectIdentifierWithDetails,
13
13
  getDecisionsPath,
14
- encodeBase36,
15
- decodeBase36,
16
- isBase36Prefix,
14
+ encodeBase26,
15
+ decodeBase26,
16
+ isBase26Prefix,
17
17
  parseProjectPrefix,
18
+ RAF_EPOCH,
18
19
  } from '../../src/utils/paths.js';
19
20
 
20
21
  describe('Paths', () => {
@@ -28,52 +29,171 @@ describe('Paths', () => {
28
29
  fs.rmSync(tempDir, { recursive: true, force: true });
29
30
  });
30
31
 
31
- describe('getNextProjectNumber', () => {
32
- it('should return 1 for empty directory', () => {
33
- expect(getNextProjectNumber(tempDir)).toBe(1);
32
+ describe('encodeBase26', () => {
33
+ it('should encode 0 as aaaaaa', () => {
34
+ expect(encodeBase26(0)).toBe('aaaaaa');
35
+ });
36
+
37
+ it('should encode small numbers with a-padding', () => {
38
+ expect(encodeBase26(1)).toBe('aaaaab');
39
+ expect(encodeBase26(25)).toBe('aaaaaz');
40
+ expect(encodeBase26(26)).toBe('aaaaba');
41
+ });
42
+
43
+ it('should encode larger numbers correctly', () => {
44
+ expect(encodeBase26(1000)).toBe('aaabmm');
45
+ expect(encodeBase26(3456000)).toBe('ahoqlc');
46
+ });
47
+
48
+ it('should encode max 6-char value (26^6 - 1) as zzzzzz', () => {
49
+ expect(encodeBase26(26 ** 6 - 1)).toBe('zzzzzz');
50
+ });
51
+
52
+ it('should encode boundary value (26^5) correctly', () => {
53
+ expect(encodeBase26(26 ** 5)).toBe('baaaaa');
54
+ });
55
+
56
+ it('should throw for negative numbers', () => {
57
+ expect(() => encodeBase26(-1)).toThrow();
58
+ expect(() => encodeBase26(-100)).toThrow();
59
+ });
60
+
61
+ it('should produce 6-character strings', () => {
62
+ for (const num of [0, 1, 100, 10000, 1000000]) {
63
+ expect(encodeBase26(num)).toHaveLength(6);
64
+ }
65
+ });
66
+ });
67
+
68
+ describe('decodeBase26', () => {
69
+ it('should decode aaaaaa as 0', () => {
70
+ expect(decodeBase26('aaaaaa')).toBe(0);
71
+ });
72
+
73
+ it('should decode valid base26 strings', () => {
74
+ expect(decodeBase26('aaaaab')).toBe(1);
75
+ expect(decodeBase26('aaaaaz')).toBe(25);
76
+ expect(decodeBase26('aaaaba')).toBe(26);
77
+ expect(decodeBase26('aaabmm')).toBe(1000);
78
+ });
79
+
80
+ it('should return null for invalid format', () => {
81
+ expect(decodeBase26('abc')).toBeNull(); // Too short
82
+ expect(decodeBase26('abcdefg')).toBeNull(); // Too long
83
+ expect(decodeBase26('')).toBeNull(); // Empty
84
+ expect(decodeBase26('abc123')).toBeNull(); // Contains digits
85
+ });
86
+
87
+ it('should handle uppercase input', () => {
88
+ expect(decodeBase26('AAABMM')).toBe(1000);
89
+ expect(decodeBase26('AAAAAZ')).toBe(25);
90
+ });
91
+
92
+ it('should decode max value zzzzzz', () => {
93
+ expect(decodeBase26('zzzzzz')).toBe(26 ** 6 - 1);
94
+ });
95
+
96
+ it('should be inverse of encodeBase26', () => {
97
+ for (const num of [0, 1, 25, 26, 1000, 100000, 3456000, 26 ** 6 - 1]) {
98
+ expect(decodeBase26(encodeBase26(num))).toBe(num);
99
+ }
100
+ });
101
+ });
102
+
103
+ describe('isBase26Prefix', () => {
104
+ it('should return true for valid 6-char base26 prefixes', () => {
105
+ expect(isBase26Prefix('aaaaaa')).toBe(true);
106
+ expect(isBase26Prefix('abcdef')).toBe(true);
107
+ expect(isBase26Prefix('zzzzzz')).toBe(true);
108
+ expect(isBase26Prefix('aaabmm')).toBe(true);
34
109
  });
35
110
 
36
- it('should return 1 for non-existent directory', () => {
37
- expect(getNextProjectNumber('/non/existent/path')).toBe(1);
111
+ it('should return false for strings with digits', () => {
112
+ expect(isBase26Prefix('000000')).toBe(false);
113
+ expect(isBase26Prefix('00abc0')).toBe(false);
114
+ expect(isBase26Prefix('0000rs')).toBe(false);
38
115
  });
39
116
 
40
- it('should return next number after existing projects', () => {
41
- fs.mkdirSync(path.join(tempDir, '01-first'));
42
- fs.mkdirSync(path.join(tempDir, '02-second'));
117
+ it('should return false for wrong-length strings', () => {
118
+ expect(isBase26Prefix('abc')).toBe(false); // Too short
119
+ expect(isBase26Prefix('abcdefg')).toBe(false); // Too long
120
+ expect(isBase26Prefix('')).toBe(false); // Empty
121
+ });
43
122
 
44
- expect(getNextProjectNumber(tempDir)).toBe(3);
123
+ it('should handle uppercase', () => {
124
+ expect(isBase26Prefix('ABCDEF')).toBe(true);
125
+ expect(isBase26Prefix('ZZZZZZ')).toBe(true);
45
126
  });
127
+ });
46
128
 
47
- it('should handle gaps in numbering', () => {
48
- fs.mkdirSync(path.join(tempDir, '01-first'));
49
- fs.mkdirSync(path.join(tempDir, '05-fifth'));
129
+ describe('parseProjectPrefix', () => {
130
+ it('should parse valid base26 prefixes', () => {
131
+ expect(parseProjectPrefix('aaaaaa')).toBe(0);
132
+ expect(parseProjectPrefix('aaaaab')).toBe(1);
133
+ expect(parseProjectPrefix('aaabmm')).toBe(1000);
134
+ });
50
135
 
51
- expect(getNextProjectNumber(tempDir)).toBe(6);
136
+ it('should return null for invalid prefixes', () => {
137
+ expect(parseProjectPrefix('abc')).toBeNull(); // Too short
138
+ expect(parseProjectPrefix('')).toBeNull();
139
+ expect(parseProjectPrefix('000001')).toBeNull(); // Contains digits
140
+ expect(parseProjectPrefix('abcdefg')).toBeNull(); // Too long
52
141
  });
53
142
  });
54
143
 
55
144
  describe('formatProjectNumber', () => {
56
- it('should pad single digits to 3 digits', () => {
57
- expect(formatProjectNumber(1)).toBe('001');
58
- expect(formatProjectNumber(9)).toBe('009');
145
+ it('should format as 6-char a-padded base26', () => {
146
+ expect(formatProjectNumber(0)).toBe('aaaaaa');
147
+ expect(formatProjectNumber(1)).toBe('aaaaab');
148
+ expect(formatProjectNumber(1000)).toBe('aaabmm');
149
+ expect(formatProjectNumber(3456000)).toBe('ahoqlc');
150
+ });
151
+ });
152
+
153
+ describe('getNextProjectNumber', () => {
154
+ it('should return epoch-based ID for non-existent directory', () => {
155
+ const result = getNextProjectNumber('/non/existent/path');
156
+ const expected = Math.floor(Date.now() / 1000) - RAF_EPOCH;
157
+ // Allow 2-second tolerance
158
+ expect(result).toBeGreaterThanOrEqual(expected - 2);
159
+ expect(result).toBeLessThanOrEqual(expected + 2);
160
+ });
161
+
162
+ it('should return epoch-based ID for empty directory', () => {
163
+ const result = getNextProjectNumber(tempDir);
164
+ const expected = Math.floor(Date.now() / 1000) - RAF_EPOCH;
165
+ expect(result).toBeGreaterThanOrEqual(expected - 2);
166
+ expect(result).toBeLessThanOrEqual(expected + 2);
59
167
  });
60
168
 
61
- it('should pad double digits to 3 digits', () => {
62
- expect(formatProjectNumber(10)).toBe('010');
63
- expect(formatProjectNumber(99)).toBe('099');
169
+ it('should avoid collision with existing project', () => {
170
+ // Create a project with the current epoch ID
171
+ const currentId = Math.floor(Date.now() / 1000) - RAF_EPOCH;
172
+ const encoded = encodeBase26(currentId);
173
+ fs.mkdirSync(path.join(tempDir, `${encoded}-existing`));
174
+
175
+ const result = getNextProjectNumber(tempDir);
176
+ expect(result).toBeGreaterThan(currentId);
64
177
  });
65
178
 
66
- it('should not pad triple digits', () => {
67
- expect(formatProjectNumber(100)).toBe('100');
68
- expect(formatProjectNumber(999)).toBe('999');
179
+ it('should skip multiple collisions', () => {
180
+ const currentId = Math.floor(Date.now() / 1000) - RAF_EPOCH;
181
+ // Create consecutive IDs to force increment
182
+ for (let i = 0; i < 3; i++) {
183
+ const encoded = encodeBase26(currentId + i);
184
+ fs.mkdirSync(path.join(tempDir, `${encoded}-project${i}`));
185
+ }
186
+
187
+ const result = getNextProjectNumber(tempDir);
188
+ expect(result).toBeGreaterThanOrEqual(currentId + 3);
69
189
  });
70
190
  });
71
191
 
72
192
  describe('listProjects', () => {
73
- it('should list projects in order', () => {
74
- fs.mkdirSync(path.join(tempDir, '02-second'));
75
- fs.mkdirSync(path.join(tempDir, '01-first'));
76
- fs.mkdirSync(path.join(tempDir, '03-third'));
193
+ it('should list projects with 6-char prefix in order', () => {
194
+ fs.mkdirSync(path.join(tempDir, 'aaaaac-second'));
195
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-first'));
196
+ fs.mkdirSync(path.join(tempDir, 'aaaaad-third'));
77
197
 
78
198
  const projects = listProjects(tempDir);
79
199
  expect(projects).toHaveLength(3);
@@ -83,18 +203,15 @@ describe('Paths', () => {
83
203
  });
84
204
 
85
205
  it('should ignore non-project directories', () => {
86
- fs.mkdirSync(path.join(tempDir, '01-valid'));
87
- fs.mkdirSync(path.join(tempDir, 'not-a-project')); // 'not' is valid base36, but project name must follow hyphen
88
- fs.mkdirSync(path.join(tempDir, 'random')); // No hyphen, not a project
89
- fs.mkdirSync(path.join(tempDir, 'ab')); // Too short prefix
206
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-valid'));
207
+ fs.mkdirSync(path.join(tempDir, 'not-a-project'));
208
+ fs.mkdirSync(path.join(tempDir, 'random'));
209
+ fs.mkdirSync(path.join(tempDir, '001-too-short'));
90
210
 
91
211
  const projects = listProjects(tempDir);
92
- // 'not-a-project' is now recognized as base36 project (not = 18741)
93
- expect(projects).toHaveLength(2);
212
+ expect(projects).toHaveLength(1);
94
213
  expect(projects[0]?.name).toBe('valid');
95
214
  expect(projects[0]?.number).toBe(1);
96
- expect(projects[1]?.name).toBe('a-project');
97
- expect(projects[1]?.number).toBe(18741); // 'not' in base36
98
215
  });
99
216
 
100
217
  it('should return empty array for non-existent directory', () => {
@@ -104,577 +221,264 @@ describe('Paths', () => {
104
221
  });
105
222
 
106
223
  describe('extractProjectNumber', () => {
107
- it('should extract 3-digit project number from path', () => {
108
- expect(extractProjectNumber('/Users/foo/RAF/001-my-project')).toBe('001');
109
- expect(extractProjectNumber('/RAF/123-another-project')).toBe('123');
110
- });
111
-
112
- it('should extract 2-digit project number from path', () => {
113
- expect(extractProjectNumber('/RAF/01-first')).toBe('01');
114
- expect(extractProjectNumber('/RAF/99-last')).toBe('99');
224
+ it('should extract 6-char prefix from path', () => {
225
+ expect(extractProjectNumber('/RAF/aaaaab-my-project')).toBe('aaaaab');
226
+ expect(extractProjectNumber('/RAF/abcdef-another-project')).toBe('abcdef');
115
227
  });
116
228
 
117
229
  it('should return null for invalid paths', () => {
118
- expect(extractProjectNumber('/RAF/my-project')).toBeNull(); // 'my' is only 2 chars
230
+ expect(extractProjectNumber('/RAF/my-project')).toBeNull();
119
231
  expect(extractProjectNumber('')).toBeNull();
120
- // 'not' is a valid base36 prefix, so not-numbered returns 'not'
121
- expect(extractProjectNumber('/RAF/not-numbered')).toBe('not');
232
+ expect(extractProjectNumber('/RAF/001-too-short')).toBeNull();
233
+ expect(extractProjectNumber('/RAF/not-numbered')).toBeNull();
234
+ expect(extractProjectNumber('/RAF/000001-has-digits')).toBeNull();
122
235
  });
123
236
 
124
237
  it('should handle path with trailing slash', () => {
125
- expect(extractProjectNumber('/RAF/001-my-project/')).toBe('001');
238
+ expect(extractProjectNumber('/RAF/aaaaab-my-project/')).toBe('aaaaab');
126
239
  });
127
240
 
128
- it('should only match valid prefixes at the start of folder name', () => {
129
- // 'abc' is a valid base36 prefix, so abc-001-project returns 'abc'
130
- expect(extractProjectNumber('/RAF/abc-001-project')).toBe('abc');
131
- // 'project' is too long for a valid prefix (must be 2-3 digits or 3 char base36)
132
- expect(extractProjectNumber('/RAF/project-001')).toBeNull();
241
+ it('should handle uppercase prefixes', () => {
242
+ expect(extractProjectNumber('/RAF/ABCDEF-my-project')).toBe('abcdef');
133
243
  });
134
244
  });
135
245
 
136
246
  describe('extractProjectName', () => {
137
- it('should extract project name from 3-digit numbered path', () => {
138
- expect(extractProjectName('/Users/foo/RAF/001-my-project')).toBe('my-project');
139
- expect(extractProjectName('/RAF/123-another-project')).toBe('another-project');
140
- });
141
-
142
- it('should extract project name from 2-digit numbered path', () => {
143
- expect(extractProjectName('/RAF/01-first')).toBe('first');
144
- expect(extractProjectName('/RAF/99-last')).toBe('last');
247
+ it('should extract project name from 6-char prefix path', () => {
248
+ expect(extractProjectName('/RAF/aaaaab-my-project')).toBe('my-project');
249
+ expect(extractProjectName('/RAF/abcdef-another-project')).toBe('another-project');
145
250
  });
146
251
 
147
252
  it('should return null for invalid paths', () => {
148
- expect(extractProjectName('/RAF/my-project')).toBeNull(); // 'my' is only 2 chars
253
+ expect(extractProjectName('/RAF/my-project')).toBeNull();
149
254
  expect(extractProjectName('')).toBeNull();
150
- // 'not' is a valid base36 prefix, so not-numbered extracts 'numbered' as name
151
- expect(extractProjectName('/RAF/not-numbered')).toBe('numbered');
255
+ expect(extractProjectName('/RAF/001-too-short')).toBeNull();
152
256
  });
153
257
 
154
258
  it('should handle path with trailing slash', () => {
155
- expect(extractProjectName('/RAF/001-my-project/')).toBe('my-project');
156
- });
157
-
158
- it('should only match valid prefixes at the start of folder name', () => {
159
- // 'abc' is a valid base36 prefix, so abc-001-project extracts '001-project' as name
160
- expect(extractProjectName('/RAF/abc-001-project')).toBe('001-project');
161
- // 'project' is too long for a valid prefix
162
- expect(extractProjectName('/RAF/project-001')).toBeNull();
259
+ expect(extractProjectName('/RAF/aaaaab-my-project/')).toBe('my-project');
163
260
  });
164
261
 
165
262
  it('should handle project names with hyphens', () => {
166
- expect(extractProjectName('/RAF/001-my-complex-project-name')).toBe('my-complex-project-name');
263
+ expect(extractProjectName('/RAF/aaaaab-my-complex-project-name')).toBe('my-complex-project-name');
167
264
  });
168
265
 
169
266
  it('should handle project names with numbers', () => {
170
- expect(extractProjectName('/RAF/001-project-v2')).toBe('project-v2');
171
- expect(extractProjectName('/RAF/001-123-test')).toBe('123-test');
267
+ expect(extractProjectName('/RAF/aaaaab-project-v2')).toBe('project-v2');
172
268
  });
173
269
  });
174
270
 
175
271
  describe('extractTaskNameFromPlanFile', () => {
176
- it('should extract task name from 3-digit numbered plan file', () => {
177
- expect(extractTaskNameFromPlanFile('001-fix-login-bug.md')).toBe('fix-login-bug');
178
- expect(extractTaskNameFromPlanFile('123-add-feature.md')).toBe('add-feature');
272
+ it('should extract task name from 2-char base36 plan file', () => {
273
+ expect(extractTaskNameFromPlanFile('01-fix-login-bug.md')).toBe('fix-login-bug');
274
+ expect(extractTaskNameFromPlanFile('0a-add-feature.md')).toBe('add-feature');
179
275
  });
180
276
 
181
- it('should extract task name from 2-digit numbered plan file', () => {
277
+ it('should extract task name from various base36 plan files', () => {
182
278
  expect(extractTaskNameFromPlanFile('01-first-task.md')).toBe('first-task');
183
279
  expect(extractTaskNameFromPlanFile('99-last-task.md')).toBe('last-task');
184
280
  });
185
281
 
186
282
  it('should return null for invalid filenames', () => {
187
- expect(extractTaskNameFromPlanFile('my-task.md')).toBeNull();
283
+ expect(extractTaskNameFromPlanFile('abc-task.md')).toBeNull();
188
284
  expect(extractTaskNameFromPlanFile('not-numbered.md')).toBeNull();
189
285
  expect(extractTaskNameFromPlanFile('')).toBeNull();
190
286
  });
191
287
 
192
288
  it('should handle task names with hyphens', () => {
193
- expect(extractTaskNameFromPlanFile('001-my-complex-task-name.md')).toBe('my-complex-task-name');
289
+ expect(extractTaskNameFromPlanFile('01-my-complex-task-name.md')).toBe('my-complex-task-name');
194
290
  });
195
291
 
196
292
  it('should handle task names with numbers', () => {
197
- expect(extractTaskNameFromPlanFile('001-task-v2.md')).toBe('task-v2');
198
- expect(extractTaskNameFromPlanFile('001-123-test.md')).toBe('123-test');
293
+ expect(extractTaskNameFromPlanFile('01-task-v2.md')).toBe('task-v2');
294
+ expect(extractTaskNameFromPlanFile('01-123-test.md')).toBe('123-test');
199
295
  });
200
296
 
201
297
  it('should handle full paths', () => {
202
- expect(extractTaskNameFromPlanFile('/path/to/plans/002-fix-login-bug.md')).toBe('fix-login-bug');
298
+ expect(extractTaskNameFromPlanFile('/path/to/plans/02-fix-login-bug.md')).toBe('fix-login-bug');
203
299
  });
204
300
 
205
301
  it('should handle files without .md extension', () => {
206
- expect(extractTaskNameFromPlanFile('001-task-name')).toBe('task-name');
207
- });
208
- });
209
-
210
- describe('resolveProjectIdentifier', () => {
211
- it('should resolve project by exact number (3 digits)', () => {
212
- fs.mkdirSync(path.join(tempDir, '003-my-project'));
213
- const result = resolveProjectIdentifier(tempDir, '003');
214
- expect(result).toBe(path.join(tempDir, '003-my-project'));
302
+ expect(extractTaskNameFromPlanFile('01-task-name')).toBe('task-name');
215
303
  });
216
304
 
217
- it('should resolve project by number without leading zeros', () => {
218
- fs.mkdirSync(path.join(tempDir, '003-my-project'));
219
- const result = resolveProjectIdentifier(tempDir, '3');
220
- expect(result).toBe(path.join(tempDir, '003-my-project'));
305
+ it('should return null for single-character task ID', () => {
306
+ expect(extractTaskNameFromPlanFile('1-task.md')).toBeNull();
221
307
  });
308
+ });
222
309
 
223
- it('should resolve project by 2-digit number', () => {
224
- fs.mkdirSync(path.join(tempDir, '42-my-project'));
225
- const result = resolveProjectIdentifier(tempDir, '42');
226
- expect(result).toBe(path.join(tempDir, '42-my-project'));
310
+ describe('resolveProjectIdentifier', () => {
311
+ it('should resolve project by base26 prefix', () => {
312
+ fs.mkdirSync(path.join(tempDir, 'aaaaad-my-project'));
313
+ const result = resolveProjectIdentifier(tempDir, 'aaaaad');
314
+ expect(result).toBe(path.join(tempDir, 'aaaaad-my-project'));
227
315
  });
228
316
 
229
317
  it('should resolve project by name', () => {
230
- fs.mkdirSync(path.join(tempDir, '005-my-awesome-project'));
318
+ fs.mkdirSync(path.join(tempDir, 'aaaaaf-my-awesome-project'));
231
319
  const result = resolveProjectIdentifier(tempDir, 'my-awesome-project');
232
- expect(result).toBe(path.join(tempDir, '005-my-awesome-project'));
320
+ expect(result).toBe(path.join(tempDir, 'aaaaaf-my-awesome-project'));
233
321
  });
234
322
 
235
- it('should return null for non-existent project number', () => {
236
- fs.mkdirSync(path.join(tempDir, '001-first'));
237
- const result = resolveProjectIdentifier(tempDir, '999');
323
+ it('should return null for non-existent project prefix', () => {
324
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-first'));
325
+ const result = resolveProjectIdentifier(tempDir, 'aaabmm');
238
326
  expect(result).toBeNull();
239
327
  });
240
328
 
241
329
  it('should return null for non-existent project name', () => {
242
- fs.mkdirSync(path.join(tempDir, '001-first'));
330
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-first'));
243
331
  const result = resolveProjectIdentifier(tempDir, 'non-existent');
244
332
  expect(result).toBeNull();
245
333
  });
246
334
 
247
335
  it('should return null for non-existent directory', () => {
248
- const result = resolveProjectIdentifier('/non/existent/path', '001');
336
+ const result = resolveProjectIdentifier('/non/existent/path', 'aaaaab');
249
337
  expect(result).toBeNull();
250
338
  });
251
339
 
252
- it('should handle multiple projects and find correct one by number', () => {
253
- fs.mkdirSync(path.join(tempDir, '001-first'));
254
- fs.mkdirSync(path.join(tempDir, '002-second'));
255
- fs.mkdirSync(path.join(tempDir, '003-third'));
340
+ it('should handle multiple projects and find correct one by prefix', () => {
341
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-first'));
342
+ fs.mkdirSync(path.join(tempDir, 'aaaaac-second'));
343
+ fs.mkdirSync(path.join(tempDir, 'aaaaad-third'));
256
344
 
257
- expect(resolveProjectIdentifier(tempDir, '1')).toBe(path.join(tempDir, '001-first'));
258
- expect(resolveProjectIdentifier(tempDir, '2')).toBe(path.join(tempDir, '002-second'));
259
- expect(resolveProjectIdentifier(tempDir, '003')).toBe(path.join(tempDir, '003-third'));
345
+ expect(resolveProjectIdentifier(tempDir, 'aaaaab')).toBe(path.join(tempDir, 'aaaaab-first'));
346
+ expect(resolveProjectIdentifier(tempDir, 'aaaaac')).toBe(path.join(tempDir, 'aaaaac-second'));
347
+ expect(resolveProjectIdentifier(tempDir, 'aaaaad')).toBe(path.join(tempDir, 'aaaaad-third'));
260
348
  });
261
349
 
262
350
  it('should handle multiple projects and find correct one by name', () => {
263
- fs.mkdirSync(path.join(tempDir, '001-first'));
264
- fs.mkdirSync(path.join(tempDir, '002-second'));
265
- fs.mkdirSync(path.join(tempDir, '003-third'));
351
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-first'));
352
+ fs.mkdirSync(path.join(tempDir, 'aaaaac-second'));
353
+ fs.mkdirSync(path.join(tempDir, 'aaaaad-third'));
266
354
 
267
- expect(resolveProjectIdentifier(tempDir, 'first')).toBe(path.join(tempDir, '001-first'));
268
- expect(resolveProjectIdentifier(tempDir, 'second')).toBe(path.join(tempDir, '002-second'));
269
- expect(resolveProjectIdentifier(tempDir, 'third')).toBe(path.join(tempDir, '003-third'));
355
+ expect(resolveProjectIdentifier(tempDir, 'first')).toBe(path.join(tempDir, 'aaaaab-first'));
356
+ expect(resolveProjectIdentifier(tempDir, 'second')).toBe(path.join(tempDir, 'aaaaac-second'));
357
+ expect(resolveProjectIdentifier(tempDir, 'third')).toBe(path.join(tempDir, 'aaaaad-third'));
270
358
  });
271
359
 
272
360
  it('should not resolve partial name match', () => {
273
- fs.mkdirSync(path.join(tempDir, '001-my-project'));
361
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-my-project'));
274
362
  expect(resolveProjectIdentifier(tempDir, 'my')).toBeNull();
275
363
  expect(resolveProjectIdentifier(tempDir, 'project')).toBeNull();
276
364
  expect(resolveProjectIdentifier(tempDir, 'my-proj')).toBeNull();
277
365
  });
278
-
279
- it('should prefer number match when identifier is numeric', () => {
280
- fs.mkdirSync(path.join(tempDir, '005-project'));
281
- // Numeric identifier "5" should match project number 5
282
- const result = resolveProjectIdentifier(tempDir, '5');
283
- expect(result).toBe(path.join(tempDir, '005-project'));
284
- });
285
366
  });
286
367
 
287
368
  describe('getDecisionsPath', () => {
288
369
  it('should return decisions.md at project root', () => {
289
- const projectPath = '/Users/foo/RAF/001-my-project';
370
+ const projectPath = '/Users/foo/RAF/aaaaab-my-project';
290
371
  expect(getDecisionsPath(projectPath)).toBe(path.join(projectPath, 'decisions.md'));
291
372
  });
292
373
  });
293
374
 
294
- // Base36 project numbering tests
295
- describe('encodeBase36', () => {
296
- it('should encode 1000 as a00', () => {
297
- expect(encodeBase36(1000)).toBe('a00');
298
- });
299
-
300
- it('should encode 1001 as a01', () => {
301
- expect(encodeBase36(1001)).toBe('a01');
302
- });
303
-
304
- it('should encode 1035 as a0z', () => {
305
- expect(encodeBase36(1035)).toBe('a0z');
306
- });
307
-
308
- it('should encode 1036 as a10', () => {
309
- expect(encodeBase36(1036)).toBe('a10');
310
- });
311
-
312
- it('should encode 2296 as b00 (1000 + 1296)', () => {
313
- // 1296 = 36 * 36, so b00 = 1000 + 1296 = 2296
314
- expect(encodeBase36(2296)).toBe('b00');
315
- });
316
-
317
- it('should encode larger numbers correctly', () => {
318
- // c00 = 1000 + 2*1296 = 1000 + 2592 = 3592
319
- expect(encodeBase36(3592)).toBe('c00');
320
- });
321
-
322
- it('should throw for numbers less than 1000', () => {
323
- expect(() => encodeBase36(999)).toThrow();
324
- expect(() => encodeBase36(0)).toThrow();
325
- expect(() => encodeBase36(500)).toThrow();
326
- });
327
-
328
- it('should handle boundary between digits', () => {
329
- // a0z = 1035, a10 = 1036
330
- expect(encodeBase36(1035)).toBe('a0z');
331
- expect(encodeBase36(1036)).toBe('a10');
332
- // azz = 1000 + 35*36 + 35 = 1000 + 1260 + 35 = 2295
333
- expect(encodeBase36(2295)).toBe('azz');
334
- });
335
- });
336
-
337
- describe('decodeBase36', () => {
338
- it('should decode a00 as 1000', () => {
339
- expect(decodeBase36('a00')).toBe(1000);
340
- });
341
-
342
- it('should decode a01 as 1001', () => {
343
- expect(decodeBase36('a01')).toBe(1001);
344
- });
345
-
346
- it('should decode a0z as 1035', () => {
347
- expect(decodeBase36('a0z')).toBe(1035);
348
- });
349
-
350
- it('should decode a10 as 1036', () => {
351
- expect(decodeBase36('a10')).toBe(1036);
352
- });
353
-
354
- it('should decode b00 as 2296', () => {
355
- expect(decodeBase36('b00')).toBe(2296);
356
- });
357
-
358
- it('should return null for invalid format', () => {
359
- expect(decodeBase36('001')).toBeNull(); // Starts with digit
360
- expect(decodeBase36('1a0')).toBeNull(); // Starts with digit
361
- expect(decodeBase36('ab')).toBeNull(); // Too short
362
- expect(decodeBase36('abcd')).toBeNull(); // Too long
363
- expect(decodeBase36('')).toBeNull(); // Empty
364
- });
365
-
366
- it('should handle uppercase input', () => {
367
- expect(decodeBase36('A00')).toBe(1000);
368
- expect(decodeBase36('B00')).toBe(2296);
369
- });
370
-
371
- it('should be inverse of encodeBase36', () => {
372
- for (const num of [1000, 1001, 1035, 1036, 2000, 2296, 5000, 10000]) {
373
- expect(decodeBase36(encodeBase36(num))).toBe(num);
374
- }
375
- });
376
- });
377
-
378
- describe('isBase36Prefix', () => {
379
- it('should return true for valid base36 prefixes', () => {
380
- expect(isBase36Prefix('a00')).toBe(true);
381
- expect(isBase36Prefix('a01')).toBe(true);
382
- expect(isBase36Prefix('azz')).toBe(true);
383
- expect(isBase36Prefix('b00')).toBe(true);
384
- expect(isBase36Prefix('z99')).toBe(true);
385
- expect(isBase36Prefix('zzz')).toBe(true);
386
- });
387
-
388
- it('should return false for numeric prefixes', () => {
389
- expect(isBase36Prefix('001')).toBe(false);
390
- expect(isBase36Prefix('999')).toBe(false);
391
- expect(isBase36Prefix('123')).toBe(false);
392
- });
393
-
394
- it('should return false for invalid formats', () => {
395
- expect(isBase36Prefix('ab')).toBe(false); // Too short
396
- expect(isBase36Prefix('abcd')).toBe(false); // Too long
397
- expect(isBase36Prefix('1ab')).toBe(false); // Starts with digit
398
- expect(isBase36Prefix('')).toBe(false); // Empty
399
- });
400
-
401
- it('should handle uppercase', () => {
402
- expect(isBase36Prefix('A00')).toBe(true);
403
- expect(isBase36Prefix('ABC')).toBe(true);
404
- });
405
- });
406
-
407
- describe('parseProjectPrefix', () => {
408
- it('should parse numeric prefixes', () => {
409
- expect(parseProjectPrefix('001')).toBe(1);
410
- expect(parseProjectPrefix('123')).toBe(123);
411
- expect(parseProjectPrefix('999')).toBe(999);
412
- expect(parseProjectPrefix('01')).toBe(1);
413
- expect(parseProjectPrefix('99')).toBe(99);
414
- });
415
-
416
- it('should parse base36 prefixes', () => {
417
- expect(parseProjectPrefix('a00')).toBe(1000);
418
- expect(parseProjectPrefix('a01')).toBe(1001);
419
- expect(parseProjectPrefix('b00')).toBe(2296);
420
- });
421
-
422
- it('should return null for invalid prefixes', () => {
423
- expect(parseProjectPrefix('abc-')).toBeNull();
424
- expect(parseProjectPrefix('1')).toBeNull(); // Too short
425
- expect(parseProjectPrefix('1234')).toBeNull(); // Too long for numeric
426
- expect(parseProjectPrefix('')).toBeNull();
427
- });
428
- });
429
-
430
- describe('formatProjectNumber (base36)', () => {
431
- it('should use numeric format for numbers 1-999', () => {
432
- expect(formatProjectNumber(1)).toBe('001');
433
- expect(formatProjectNumber(999)).toBe('999');
434
- expect(formatProjectNumber(500)).toBe('500');
435
- });
436
-
437
- it('should use base36 format for numbers >= 1000', () => {
438
- expect(formatProjectNumber(1000)).toBe('a00');
439
- expect(formatProjectNumber(1001)).toBe('a01');
440
- expect(formatProjectNumber(2296)).toBe('b00');
441
- });
442
-
443
- it('should correctly format transition from 999 to 1000', () => {
444
- expect(formatProjectNumber(999)).toBe('999');
445
- expect(formatProjectNumber(1000)).toBe('a00');
446
- });
447
- });
448
-
449
- describe('extractProjectNumber (base36)', () => {
450
- it('should extract base36 project number from path', () => {
451
- expect(extractProjectNumber('/RAF/a00-my-project')).toBe('a00');
452
- expect(extractProjectNumber('/RAF/a01-another-project')).toBe('a01');
453
- expect(extractProjectNumber('/RAF/b00-third-project')).toBe('b00');
454
- });
455
-
456
- it('should handle uppercase base36 prefixes', () => {
457
- expect(extractProjectNumber('/RAF/A00-my-project')).toBe('a00');
458
- });
459
-
460
- it('should prefer numeric format when ambiguous', () => {
461
- // Numeric should be matched first
462
- expect(extractProjectNumber('/RAF/001-my-project')).toBe('001');
463
- });
464
- });
465
-
466
- describe('extractProjectName (base36)', () => {
467
- it('should extract project name from base36 numbered path', () => {
468
- expect(extractProjectName('/RAF/a00-my-project')).toBe('my-project');
469
- expect(extractProjectName('/RAF/b00-another-project')).toBe('another-project');
470
- });
471
-
472
- it('should handle uppercase base36 prefixes', () => {
473
- expect(extractProjectName('/RAF/A00-my-project')).toBe('my-project');
474
- });
475
- });
476
-
477
- describe('getNextProjectNumber (base36)', () => {
478
- it('should return next number after base36 projects', () => {
479
- fs.mkdirSync(path.join(tempDir, 'a00-project1000'));
480
- expect(getNextProjectNumber(tempDir)).toBe(1001);
481
- });
482
-
483
- it('should handle mixed numeric and base36 projects', () => {
484
- fs.mkdirSync(path.join(tempDir, '999-last-numeric'));
485
- fs.mkdirSync(path.join(tempDir, 'a00-first-base36'));
486
- expect(getNextProjectNumber(tempDir)).toBe(1001);
487
- });
488
-
489
- it('should find max across mixed formats', () => {
490
- fs.mkdirSync(path.join(tempDir, '001-first'));
491
- fs.mkdirSync(path.join(tempDir, 'a05-middle'));
492
- fs.mkdirSync(path.join(tempDir, '500-later'));
493
- // a05 = 1005, which is the max
494
- expect(getNextProjectNumber(tempDir)).toBe(1006);
495
- });
496
- });
497
-
498
- describe('listProjects (base36)', () => {
499
- it('should list base36 projects with correct number', () => {
500
- fs.mkdirSync(path.join(tempDir, 'a00-project1000'));
501
- const projects = listProjects(tempDir);
502
- expect(projects).toHaveLength(1);
503
- expect(projects[0]?.number).toBe(1000);
504
- expect(projects[0]?.name).toBe('project1000');
505
- });
506
-
507
- it('should sort mixed numeric and base36 projects correctly', () => {
508
- fs.mkdirSync(path.join(tempDir, 'a00-project1000'));
509
- fs.mkdirSync(path.join(tempDir, '001-first'));
510
- fs.mkdirSync(path.join(tempDir, '999-last-numeric'));
511
-
512
- const projects = listProjects(tempDir);
513
- expect(projects).toHaveLength(3);
514
- expect(projects[0]?.number).toBe(1);
515
- expect(projects[1]?.number).toBe(999);
516
- expect(projects[2]?.number).toBe(1000);
517
- });
518
- });
519
-
520
- describe('resolveProjectIdentifier (base36)', () => {
521
- it('should resolve base36 project by prefix', () => {
522
- fs.mkdirSync(path.join(tempDir, 'a00-my-project'));
523
- const result = resolveProjectIdentifier(tempDir, 'a00');
524
- expect(result).toBe(path.join(tempDir, 'a00-my-project'));
525
- });
526
-
527
- it('should resolve base36 project by numeric value', () => {
528
- fs.mkdirSync(path.join(tempDir, 'a00-my-project'));
529
- const result = resolveProjectIdentifier(tempDir, '1000');
530
- expect(result).toBe(path.join(tempDir, 'a00-my-project'));
531
- });
532
-
533
- it('should resolve base36 project by name', () => {
534
- fs.mkdirSync(path.join(tempDir, 'a00-my-project'));
535
- const result = resolveProjectIdentifier(tempDir, 'my-project');
536
- expect(result).toBe(path.join(tempDir, 'a00-my-project'));
537
- });
538
-
539
- it('should handle mixed projects correctly', () => {
540
- fs.mkdirSync(path.join(tempDir, '001-first'));
541
- fs.mkdirSync(path.join(tempDir, 'a00-second'));
542
-
543
- expect(resolveProjectIdentifier(tempDir, '1')).toBe(path.join(tempDir, '001-first'));
544
- expect(resolveProjectIdentifier(tempDir, '1000')).toBe(path.join(tempDir, 'a00-second'));
545
- expect(resolveProjectIdentifier(tempDir, 'a00')).toBe(path.join(tempDir, 'a00-second'));
546
- expect(resolveProjectIdentifier(tempDir, 'first')).toBe(path.join(tempDir, '001-first'));
547
- expect(resolveProjectIdentifier(tempDir, 'second')).toBe(path.join(tempDir, 'a00-second'));
548
- });
549
- });
550
-
551
375
  describe('resolveProjectIdentifier (full folder name)', () => {
552
- it('should resolve project by full numeric folder name', () => {
553
- fs.mkdirSync(path.join(tempDir, '001-fix-stuff'));
554
- const result = resolveProjectIdentifier(tempDir, '001-fix-stuff');
555
- expect(result).toBe(path.join(tempDir, '001-fix-stuff'));
556
- });
557
-
558
- it('should resolve project by full base36 folder name', () => {
559
- fs.mkdirSync(path.join(tempDir, 'a01-important-project'));
560
- const result = resolveProjectIdentifier(tempDir, 'a01-important-project');
561
- expect(result).toBe(path.join(tempDir, 'a01-important-project'));
376
+ it('should resolve project by full folder name', () => {
377
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-fix-stuff'));
378
+ const result = resolveProjectIdentifier(tempDir, 'aaaaab-fix-stuff');
379
+ expect(result).toBe(path.join(tempDir, 'aaaaab-fix-stuff'));
562
380
  });
563
381
 
564
382
  it('should resolve project with hyphens in name', () => {
565
- fs.mkdirSync(path.join(tempDir, '001-my-cool-project'));
566
- const result = resolveProjectIdentifier(tempDir, '001-my-cool-project');
567
- expect(result).toBe(path.join(tempDir, '001-my-cool-project'));
383
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-my-cool-project'));
384
+ const result = resolveProjectIdentifier(tempDir, 'aaaaab-my-cool-project');
385
+ expect(result).toBe(path.join(tempDir, 'aaaaab-my-cool-project'));
568
386
  });
569
387
 
570
388
  it('should return null for wrong prefix with correct name format', () => {
571
- fs.mkdirSync(path.join(tempDir, '001-correct-name'));
572
- // Full folder format but wrong prefix - should NOT match
573
- const result = resolveProjectIdentifier(tempDir, '002-correct-name');
389
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-correct-name'));
390
+ const result = resolveProjectIdentifier(tempDir, 'aaaaac-correct-name');
574
391
  expect(result).toBeNull();
575
392
  });
576
393
 
577
394
  it('should return null for correct prefix with wrong name format', () => {
578
- fs.mkdirSync(path.join(tempDir, '001-correct-name'));
579
- // Full folder format but wrong name - should NOT match
580
- const result = resolveProjectIdentifier(tempDir, '001-wrong-name');
395
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-correct-name'));
396
+ const result = resolveProjectIdentifier(tempDir, 'aaaaab-wrong-name');
581
397
  expect(result).toBeNull();
582
398
  });
583
399
 
584
400
  it('should handle case-insensitive folder matching', () => {
585
- fs.mkdirSync(path.join(tempDir, 'A01-My-Project'));
586
- // Lowercase identifier should still match
587
- const result = resolveProjectIdentifier(tempDir, 'a01-my-project');
588
- expect(result).toBe(path.join(tempDir, 'A01-My-Project'));
589
- });
590
-
591
- it('should resolve 2-digit prefix full folder names', () => {
592
- fs.mkdirSync(path.join(tempDir, '01-first-project'));
593
- const result = resolveProjectIdentifier(tempDir, '01-first-project');
594
- expect(result).toBe(path.join(tempDir, '01-first-project'));
401
+ fs.mkdirSync(path.join(tempDir, 'ABCDEF-My-Project'));
402
+ const result = resolveProjectIdentifier(tempDir, 'abcdef-my-project');
403
+ expect(result).toBe(path.join(tempDir, 'ABCDEF-My-Project'));
595
404
  });
596
405
 
597
406
  it('should still resolve by name alone after full folder check', () => {
598
- fs.mkdirSync(path.join(tempDir, '001-my-project'));
599
- // Name-only should still work via fallback
407
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-my-project'));
600
408
  const result = resolveProjectIdentifier(tempDir, 'my-project');
601
- expect(result).toBe(path.join(tempDir, '001-my-project'));
409
+ expect(result).toBe(path.join(tempDir, 'aaaaab-my-project'));
602
410
  });
603
411
 
604
- it('should still resolve by number alone after full folder check', () => {
605
- fs.mkdirSync(path.join(tempDir, '003-my-project'));
606
- // Number-only should still work via fallback
607
- const result = resolveProjectIdentifier(tempDir, '3');
608
- expect(result).toBe(path.join(tempDir, '003-my-project'));
412
+ it('should still resolve by prefix alone after full folder check', () => {
413
+ fs.mkdirSync(path.join(tempDir, 'aaaaad-my-project'));
414
+ const result = resolveProjectIdentifier(tempDir, 'aaaaad');
415
+ expect(result).toBe(path.join(tempDir, 'aaaaad-my-project'));
609
416
  });
610
417
 
611
418
  it('should prefer exact full folder match over name-only match', () => {
612
- // Create two projects where one's name is a full folder format
613
- fs.mkdirSync(path.join(tempDir, '001-project'));
614
- fs.mkdirSync(path.join(tempDir, '002-001-project')); // Name portion is "001-project"
419
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-project'));
420
+ fs.mkdirSync(path.join(tempDir, 'aaaaac-aaaaab-project'));
615
421
 
616
- // "001-project" as full folder name should match the first project
617
- const result = resolveProjectIdentifier(tempDir, '001-project');
618
- expect(result).toBe(path.join(tempDir, '001-project'));
422
+ const result = resolveProjectIdentifier(tempDir, 'aaaaab-project');
423
+ expect(result).toBe(path.join(tempDir, 'aaaaab-project'));
619
424
  });
620
425
  });
621
426
 
622
427
  describe('resolveProjectIdentifier (case-insensitive name matching)', () => {
623
428
  it('should match project name case-insensitively', () => {
624
- fs.mkdirSync(path.join(tempDir, '001-fix-double-summary-headers'));
429
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-fix-double-summary-headers'));
625
430
 
626
- // All these should match
627
431
  expect(resolveProjectIdentifier(tempDir, 'fix-double-summary-headers')).toBe(
628
- path.join(tempDir, '001-fix-double-summary-headers')
432
+ path.join(tempDir, 'aaaaab-fix-double-summary-headers')
629
433
  );
630
434
  expect(resolveProjectIdentifier(tempDir, 'Fix-Double-Summary-Headers')).toBe(
631
- path.join(tempDir, '001-fix-double-summary-headers')
435
+ path.join(tempDir, 'aaaaab-fix-double-summary-headers')
632
436
  );
633
437
  expect(resolveProjectIdentifier(tempDir, 'FIX-DOUBLE-SUMMARY-HEADERS')).toBe(
634
- path.join(tempDir, '001-fix-double-summary-headers')
438
+ path.join(tempDir, 'aaaaab-fix-double-summary-headers')
635
439
  );
636
440
  });
637
441
 
638
442
  it('should match mixed case project name', () => {
639
- fs.mkdirSync(path.join(tempDir, '001-MyProject'));
443
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-MyProject'));
640
444
 
641
445
  expect(resolveProjectIdentifier(tempDir, 'myproject')).toBe(
642
- path.join(tempDir, '001-MyProject')
446
+ path.join(tempDir, 'aaaaab-MyProject')
643
447
  );
644
448
  expect(resolveProjectIdentifier(tempDir, 'MYPROJECT')).toBe(
645
- path.join(tempDir, '001-MyProject')
449
+ path.join(tempDir, 'aaaaab-MyProject')
646
450
  );
647
451
  expect(resolveProjectIdentifier(tempDir, 'MyProject')).toBe(
648
- path.join(tempDir, '001-MyProject')
452
+ path.join(tempDir, 'aaaaab-MyProject')
649
453
  );
650
454
  });
651
455
  });
652
456
 
653
457
  describe('resolveProjectIdentifierWithDetails', () => {
654
458
  it('should return path for unique name match', () => {
655
- fs.mkdirSync(path.join(tempDir, '001-my-project'));
459
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-my-project'));
656
460
 
657
461
  const result = resolveProjectIdentifierWithDetails(tempDir, 'my-project');
658
- expect(result.path).toBe(path.join(tempDir, '001-my-project'));
462
+ expect(result.path).toBe(path.join(tempDir, 'aaaaab-my-project'));
659
463
  expect(result.error).toBeUndefined();
660
464
  expect(result.matches).toBeUndefined();
661
465
  });
662
466
 
663
467
  it('should return ambiguous error for multiple projects with same name', () => {
664
- fs.mkdirSync(path.join(tempDir, '001-my-project'));
665
- fs.mkdirSync(path.join(tempDir, '002-my-project'));
468
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-my-project'));
469
+ fs.mkdirSync(path.join(tempDir, 'aaaaac-my-project'));
666
470
 
667
471
  const result = resolveProjectIdentifierWithDetails(tempDir, 'my-project');
668
472
  expect(result.path).toBeNull();
669
473
  expect(result.error).toBe('ambiguous');
670
474
  expect(result.matches).toHaveLength(2);
671
- expect(result.matches?.[0]?.folder).toBe('001-my-project');
672
- expect(result.matches?.[1]?.folder).toBe('002-my-project');
475
+ expect(result.matches?.[0]?.folder).toBe('aaaaab-my-project');
476
+ expect(result.matches?.[1]?.folder).toBe('aaaaac-my-project');
673
477
  });
674
478
 
675
479
  it('should return ambiguous error for case-insensitive duplicate names', () => {
676
- fs.mkdirSync(path.join(tempDir, '001-MyProject'));
677
- fs.mkdirSync(path.join(tempDir, '002-myproject'));
480
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-MyProject'));
481
+ fs.mkdirSync(path.join(tempDir, 'aaaaac-myproject'));
678
482
 
679
483
  const result = resolveProjectIdentifierWithDetails(tempDir, 'myproject');
680
484
  expect(result.path).toBeNull();
@@ -683,7 +487,7 @@ describe('Paths', () => {
683
487
  });
684
488
 
685
489
  it('should return not_found error for non-existent project', () => {
686
- fs.mkdirSync(path.join(tempDir, '001-existing-project'));
490
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-existing-project'));
687
491
 
688
492
  const result = resolveProjectIdentifierWithDetails(tempDir, 'non-existent');
689
493
  expect(result.path).toBeNull();
@@ -697,38 +501,36 @@ describe('Paths', () => {
697
501
  expect(result.error).toBe('not_found');
698
502
  });
699
503
 
700
- it('should resolve by number even with duplicate names', () => {
701
- fs.mkdirSync(path.join(tempDir, '001-my-project'));
702
- fs.mkdirSync(path.join(tempDir, '002-my-project'));
504
+ it('should resolve by prefix even with duplicate names', () => {
505
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-my-project'));
506
+ fs.mkdirSync(path.join(tempDir, 'aaaaac-my-project'));
703
507
 
704
- // By number should still work unambiguously
705
- const result1 = resolveProjectIdentifierWithDetails(tempDir, '1');
706
- expect(result1.path).toBe(path.join(tempDir, '001-my-project'));
508
+ const result1 = resolveProjectIdentifierWithDetails(tempDir, 'aaaaab');
509
+ expect(result1.path).toBe(path.join(tempDir, 'aaaaab-my-project'));
707
510
  expect(result1.error).toBeUndefined();
708
511
 
709
- const result2 = resolveProjectIdentifierWithDetails(tempDir, '2');
710
- expect(result2.path).toBe(path.join(tempDir, '002-my-project'));
512
+ const result2 = resolveProjectIdentifierWithDetails(tempDir, 'aaaaac');
513
+ expect(result2.path).toBe(path.join(tempDir, 'aaaaac-my-project'));
711
514
  expect(result2.error).toBeUndefined();
712
515
  });
713
516
 
714
517
  it('should resolve by full folder name even with duplicate names', () => {
715
- fs.mkdirSync(path.join(tempDir, '001-my-project'));
716
- fs.mkdirSync(path.join(tempDir, '002-my-project'));
518
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-my-project'));
519
+ fs.mkdirSync(path.join(tempDir, 'aaaaac-my-project'));
717
520
 
718
- // Full folder name should work unambiguously
719
- const result1 = resolveProjectIdentifierWithDetails(tempDir, '001-my-project');
720
- expect(result1.path).toBe(path.join(tempDir, '001-my-project'));
521
+ const result1 = resolveProjectIdentifierWithDetails(tempDir, 'aaaaab-my-project');
522
+ expect(result1.path).toBe(path.join(tempDir, 'aaaaab-my-project'));
721
523
  expect(result1.error).toBeUndefined();
722
524
 
723
- const result2 = resolveProjectIdentifierWithDetails(tempDir, '002-my-project');
724
- expect(result2.path).toBe(path.join(tempDir, '002-my-project'));
525
+ const result2 = resolveProjectIdentifierWithDetails(tempDir, 'aaaaac-my-project');
526
+ expect(result2.path).toBe(path.join(tempDir, 'aaaaac-my-project'));
725
527
  expect(result2.error).toBeUndefined();
726
528
  });
727
529
 
728
530
  it('should sort matches by project number', () => {
729
- fs.mkdirSync(path.join(tempDir, '005-duplicate'));
730
- fs.mkdirSync(path.join(tempDir, '001-duplicate'));
731
- fs.mkdirSync(path.join(tempDir, '003-duplicate'));
531
+ fs.mkdirSync(path.join(tempDir, 'aaaaaf-duplicate'));
532
+ fs.mkdirSync(path.join(tempDir, 'aaaaab-duplicate'));
533
+ fs.mkdirSync(path.join(tempDir, 'aaaaad-duplicate'));
732
534
 
733
535
  const result = resolveProjectIdentifierWithDetails(tempDir, 'duplicate');
734
536
  expect(result.error).toBe('ambiguous');