@pennyfarthing/core 7.9.5 → 8.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 (631) hide show
  1. package/LICENSE +14 -0
  2. package/README.md +209 -0
  3. package/package.json +32 -37
  4. package/{dist → packages/core/dist}/cli/utils/files.d.ts +6 -2
  5. package/{dist → packages/core/dist}/cli/utils/files.d.ts.map +1 -1
  6. package/{dist → packages/core/dist}/cli/utils/files.js +12 -4
  7. package/{dist → packages/core/dist}/cli/utils/files.js.map +1 -1
  8. package/{dist → packages/core/dist}/cli/workspace.test.js +3 -1
  9. package/{dist → packages/core/dist}/cli/workspace.test.js.map +1 -1
  10. package/pennyfarthing-dist/agents/README.md +12 -12
  11. package/pennyfarthing-dist/agents/architect.md +1 -1
  12. package/pennyfarthing-dist/agents/dev.md +2 -2
  13. package/pennyfarthing-dist/agents/devops.md +1 -1
  14. package/pennyfarthing-dist/agents/handoff.md +2 -2
  15. package/pennyfarthing-dist/agents/pm.md +1 -1
  16. package/pennyfarthing-dist/agents/reviewer.md +2 -2
  17. package/pennyfarthing-dist/agents/sm-setup.md +5 -5
  18. package/pennyfarthing-dist/agents/sm.md +6 -6
  19. package/pennyfarthing-dist/agents/tea.md +2 -2
  20. package/pennyfarthing-dist/agents/tech-writer.md +1 -1
  21. package/pennyfarthing-dist/agents/testing-runner.md +4 -4
  22. package/pennyfarthing-dist/agents/ux-designer.md +1 -1
  23. package/pennyfarthing-dist/agents/workflow-status-check.md +2 -2
  24. package/pennyfarthing-dist/commands/architect.md +1 -1
  25. package/pennyfarthing-dist/commands/create-branches-from-story.md +5 -5
  26. package/pennyfarthing-dist/commands/dev.md +1 -1
  27. package/pennyfarthing-dist/commands/devops.md +1 -1
  28. package/pennyfarthing-dist/commands/health-check.md +3 -3
  29. package/pennyfarthing-dist/commands/orchestrator.md +1 -1
  30. package/pennyfarthing-dist/commands/parallel-work.md +4 -4
  31. package/pennyfarthing-dist/commands/pm.md +1 -1
  32. package/pennyfarthing-dist/commands/release.md +1 -1
  33. package/pennyfarthing-dist/commands/repo-status.md +2 -2
  34. package/pennyfarthing-dist/commands/retro.md +2 -2
  35. package/pennyfarthing-dist/commands/reviewer.md +1 -1
  36. package/pennyfarthing-dist/commands/set-theme.md +1 -1
  37. package/pennyfarthing-dist/commands/sm.md +1 -1
  38. package/pennyfarthing-dist/commands/sprint.md +7 -7
  39. package/pennyfarthing-dist/commands/start-epic.md +3 -3
  40. package/pennyfarthing-dist/commands/sync-epic-to-jira.md +4 -4
  41. package/pennyfarthing-dist/commands/tea.md +1 -1
  42. package/pennyfarthing-dist/commands/tech-writer.md +1 -1
  43. package/pennyfarthing-dist/commands/ux-designer.md +1 -1
  44. package/pennyfarthing-dist/commands/work.md +1 -1
  45. package/pennyfarthing-dist/guides/agent-behavior.md +11 -11
  46. package/pennyfarthing-dist/guides/agent-tag-taxonomy.md +1 -1
  47. package/pennyfarthing-dist/guides/agent-template-strategic.md +1 -1
  48. package/pennyfarthing-dist/guides/agent-template-tactical.md +2 -2
  49. package/pennyfarthing-dist/guides/worktree-mode.md +3 -3
  50. package/pennyfarthing-dist/guides/xml-tags.md +1 -1
  51. package/pennyfarthing-dist/personas/themes/mash.yaml +2 -2
  52. package/pennyfarthing-dist/personas/themes/star-trek-tos.yaml +1 -1
  53. package/pennyfarthing-dist/scripts/README.md +6 -6
  54. package/pennyfarthing-dist/scripts/core/README.md +3 -4
  55. package/pennyfarthing-dist/scripts/core/phase-check-start.sh +12 -17
  56. package/pennyfarthing-dist/scripts/cyclist/is-cyclist.sh +1 -1
  57. package/pennyfarthing-dist/scripts/git/README.md +2 -2
  58. package/pennyfarthing-dist/scripts/git/install-git-hooks.sh +3 -15
  59. package/pennyfarthing-dist/scripts/git/release.sh +1 -1
  60. package/pennyfarthing-dist/scripts/health/drift-detection.sh +1 -1
  61. package/pennyfarthing-dist/scripts/hooks/README.md +1 -1
  62. package/pennyfarthing-dist/scripts/hooks/__pycache__/question_reflector_check.cpython-314.pyc +0 -0
  63. package/pennyfarthing-dist/scripts/hooks/post-merge.sh +3 -12
  64. package/pennyfarthing-dist/scripts/hooks/pre-push.sh +3 -15
  65. package/pennyfarthing-dist/scripts/jira/README.md +2 -2
  66. package/pennyfarthing-dist/scripts/jira/create-jira-epic.sh +1 -1
  67. package/pennyfarthing-dist/scripts/jira/create-jira-story.sh +1 -1
  68. package/pennyfarthing-dist/scripts/jira/jira-reconcile.sh +2 -2
  69. package/pennyfarthing-dist/scripts/lib/find-root.sh +45 -25
  70. package/pennyfarthing-dist/scripts/misc/README.md +2 -2
  71. package/pennyfarthing-dist/scripts/misc/backlog.sh +3 -17
  72. package/pennyfarthing-dist/scripts/misc/generate-skill-docs.sh +4 -30
  73. package/pennyfarthing-dist/scripts/misc/log-skill-usage.sh +1 -1
  74. package/pennyfarthing-dist/scripts/misc/run-ci.sh +3 -10
  75. package/pennyfarthing-dist/scripts/misc/skill-usage-report.sh +1 -1
  76. package/pennyfarthing-dist/scripts/misc/uninstall.sh +0 -1
  77. package/pennyfarthing-dist/scripts/portraits/generate-portraits.py +19 -2
  78. package/pennyfarthing-dist/scripts/sprint/README.md +2 -2
  79. package/pennyfarthing-dist/scripts/sprint/archive-story.sh +5 -5
  80. package/pennyfarthing-dist/scripts/sprint/available-stories.sh +2 -2
  81. package/pennyfarthing-dist/scripts/sprint/check-story.sh +1 -1
  82. package/pennyfarthing-dist/scripts/sprint/get-epic-field.sh +5 -5
  83. package/pennyfarthing-dist/scripts/sprint/get-story-field.sh +5 -5
  84. package/pennyfarthing-dist/scripts/sprint/list-future.sh +2 -2
  85. package/pennyfarthing-dist/scripts/sprint/new-sprint.sh +3 -3
  86. package/pennyfarthing-dist/scripts/sprint/promote-epic.sh +4 -4
  87. package/pennyfarthing-dist/scripts/sprint/sprint-common.sh +11 -17
  88. package/pennyfarthing-dist/scripts/sprint/sprint-info.sh +2 -2
  89. package/pennyfarthing-dist/scripts/sprint/sprint-metrics.sh +3 -14
  90. package/pennyfarthing-dist/scripts/sprint/sprint-status.sh +4 -4
  91. package/pennyfarthing-dist/scripts/story/README.md +2 -2
  92. package/pennyfarthing-dist/scripts/test/README.md +1 -1
  93. package/pennyfarthing-dist/scripts/theme/README.md +1 -1
  94. package/pennyfarthing-dist/scripts/workflow/README.md +2 -2
  95. package/pennyfarthing-dist/scripts/workflow/finish-story.sh +1 -1
  96. package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +1 -1
  97. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.sh +1 -1
  98. package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +2 -2
  99. package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +2 -2
  100. package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +2 -2
  101. package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +2 -2
  102. package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +2 -2
  103. package/pennyfarthing-dist/skills/cyclist/SKILL.md +2 -2
  104. package/pennyfarthing-dist/skills/jira/SKILL.md +15 -15
  105. package/pennyfarthing-dist/skills/judge/SKILL.md +1 -1
  106. package/pennyfarthing-dist/skills/sprint/scripts/archive-story.sh +3 -3
  107. package/pennyfarthing-dist/skills/sprint/scripts/available-stories.sh +2 -2
  108. package/pennyfarthing-dist/skills/sprint/scripts/check-story.sh +2 -2
  109. package/pennyfarthing-dist/skills/sprint/scripts/create-jira-epic.sh +1 -1
  110. package/pennyfarthing-dist/skills/sprint/scripts/new-sprint.sh +3 -3
  111. package/pennyfarthing-dist/skills/sprint/scripts/promote-epic.sh +4 -4
  112. package/pennyfarthing-dist/skills/sprint/scripts/sprint-info.sh +2 -2
  113. package/pennyfarthing-dist/skills/sprint/scripts/sprint-status.sh +2 -2
  114. package/pennyfarthing-dist/skills/sprint/scripts/sync-epic-jira.sh +4 -4
  115. package/pennyfarthing-dist/skills/sprint/skill.md +30 -30
  116. package/pennyfarthing-dist/skills/story/skill.md +16 -16
  117. package/pennyfarthing-dist/skills/workflow/scripts/list-workflows.sh +2 -2
  118. package/pennyfarthing-dist/skills/workflow/scripts/resume-workflow.sh +2 -2
  119. package/pennyfarthing-dist/skills/workflow/scripts/show-workflow.sh +2 -2
  120. package/pennyfarthing-dist/skills/workflow/scripts/start-workflow.sh +2 -2
  121. package/pennyfarthing-dist/skills/workflow/scripts/workflow-status.sh +2 -2
  122. package/pennyfarthing-dist/skills/workflow/skill.md +14 -14
  123. package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-05-import-to-future.md +2 -2
  124. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-01-analyze.md +2 -2
  125. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-04-verify.md +1 -1
  126. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-05-complete.md +1 -1
  127. package/pennyfarthing_scripts/README.md +66 -0
  128. package/pennyfarthing_scripts/__init__.py +17 -0
  129. package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  130. package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
  131. package/pennyfarthing_scripts/__pycache__/config.cpython-314.pyc +0 -0
  132. package/pennyfarthing_scripts/__pycache__/jira.cpython-314.pyc +0 -0
  133. package/pennyfarthing_scripts/__pycache__/jira_epic_creation.cpython-314.pyc +0 -0
  134. package/pennyfarthing_scripts/__pycache__/jira_sync.cpython-314.pyc +0 -0
  135. package/pennyfarthing_scripts/__pycache__/jira_sync_story.cpython-314.pyc +0 -0
  136. package/pennyfarthing_scripts/__pycache__/sprint.cpython-314.pyc +0 -0
  137. package/pennyfarthing_scripts/__pycache__/workflow.cpython-311.pyc +0 -0
  138. package/pennyfarthing_scripts/__pycache__/workflow.cpython-314.pyc +0 -0
  139. package/pennyfarthing_scripts/bellmode_hook.py +154 -0
  140. package/pennyfarthing_scripts/brownfield/__init__.py +35 -0
  141. package/pennyfarthing_scripts/brownfield/__main__.py +7 -0
  142. package/pennyfarthing_scripts/brownfield/__pycache__/__init__.cpython-314.pyc +0 -0
  143. package/pennyfarthing_scripts/brownfield/__pycache__/__main__.cpython-314.pyc +0 -0
  144. package/pennyfarthing_scripts/brownfield/__pycache__/cli.cpython-314.pyc +0 -0
  145. package/pennyfarthing_scripts/brownfield/__pycache__/discover.cpython-314.pyc +0 -0
  146. package/pennyfarthing_scripts/brownfield/cli.py +131 -0
  147. package/pennyfarthing_scripts/brownfield/discover.py +753 -0
  148. package/pennyfarthing_scripts/common/__init__.py +49 -0
  149. package/pennyfarthing_scripts/common/__pycache__/__init__.cpython-314.pyc +0 -0
  150. package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
  151. package/pennyfarthing_scripts/common/__pycache__/output.cpython-314.pyc +0 -0
  152. package/pennyfarthing_scripts/common/config.py +91 -0
  153. package/pennyfarthing_scripts/common/output.py +180 -0
  154. package/pennyfarthing_scripts/config.py +21 -0
  155. package/pennyfarthing_scripts/git/__init__.py +29 -0
  156. package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
  157. package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
  158. package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
  159. package/pennyfarthing_scripts/git/create_branches.py +439 -0
  160. package/pennyfarthing_scripts/git/status_all.py +310 -0
  161. package/pennyfarthing_scripts/hooks.py +455 -0
  162. package/pennyfarthing_scripts/jira/__init__.py +93 -0
  163. package/pennyfarthing_scripts/jira/__main__.py +10 -0
  164. package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-314.pyc +0 -0
  165. package/pennyfarthing_scripts/jira/__pycache__/__main__.cpython-314.pyc +0 -0
  166. package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
  167. package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-314.pyc +0 -0
  168. package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
  169. package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
  170. package/pennyfarthing_scripts/jira/__pycache__/compat.cpython-314.pyc +0 -0
  171. package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-314.pyc +0 -0
  172. package/pennyfarthing_scripts/jira/__pycache__/mappings.cpython-314.pyc +0 -0
  173. package/pennyfarthing_scripts/jira/__pycache__/models.cpython-314.pyc +0 -0
  174. package/pennyfarthing_scripts/jira/__pycache__/story.cpython-314.pyc +0 -0
  175. package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-314.pyc +0 -0
  176. package/pennyfarthing_scripts/jira/bidirectional.py +561 -0
  177. package/pennyfarthing_scripts/jira/claim.py +211 -0
  178. package/pennyfarthing_scripts/jira/cli.py +150 -0
  179. package/pennyfarthing_scripts/jira/client.py +613 -0
  180. package/pennyfarthing_scripts/jira/epic.py +176 -0
  181. package/pennyfarthing_scripts/jira/story.py +219 -0
  182. package/pennyfarthing_scripts/jira/sync.py +350 -0
  183. package/pennyfarthing_scripts/jira_bidirectional_sync.py +37 -0
  184. package/pennyfarthing_scripts/jira_epic_creation.py +30 -0
  185. package/pennyfarthing_scripts/jira_sync.py +36 -0
  186. package/pennyfarthing_scripts/jira_sync_story.py +30 -0
  187. package/pennyfarthing_scripts/output.py +37 -0
  188. package/pennyfarthing_scripts/preflight/__init__.py +17 -0
  189. package/pennyfarthing_scripts/preflight/__main__.py +10 -0
  190. package/pennyfarthing_scripts/preflight/__pycache__/__init__.cpython-314.pyc +0 -0
  191. package/pennyfarthing_scripts/preflight/__pycache__/__main__.cpython-314.pyc +0 -0
  192. package/pennyfarthing_scripts/preflight/__pycache__/cli.cpython-314.pyc +0 -0
  193. package/pennyfarthing_scripts/preflight/__pycache__/finish.cpython-314.pyc +0 -0
  194. package/pennyfarthing_scripts/preflight/cli.py +141 -0
  195. package/pennyfarthing_scripts/preflight/finish.py +382 -0
  196. package/pennyfarthing_scripts/pretooluse_hook.py +142 -0
  197. package/pennyfarthing_scripts/prime/__init__.py +125 -0
  198. package/pennyfarthing_scripts/prime/__main__.py +8 -0
  199. package/pennyfarthing_scripts/prime/__pycache__/__init__.cpython-314.pyc +0 -0
  200. package/pennyfarthing_scripts/prime/__pycache__/__main__.cpython-314.pyc +0 -0
  201. package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
  202. package/pennyfarthing_scripts/prime/__pycache__/loader.cpython-314.pyc +0 -0
  203. package/pennyfarthing_scripts/prime/__pycache__/models.cpython-314.pyc +0 -0
  204. package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
  205. package/pennyfarthing_scripts/prime/__pycache__/session.cpython-314.pyc +0 -0
  206. package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  207. package/pennyfarthing_scripts/prime/cli.py +375 -0
  208. package/pennyfarthing_scripts/prime/loader.py +239 -0
  209. package/pennyfarthing_scripts/prime/models.py +169 -0
  210. package/pennyfarthing_scripts/prime/persona.py +288 -0
  211. package/pennyfarthing_scripts/prime/session.py +183 -0
  212. package/pennyfarthing_scripts/prime/workflow.py +275 -0
  213. package/pennyfarthing_scripts/sprint/__init__.py +66 -0
  214. package/pennyfarthing_scripts/sprint/__main__.py +10 -0
  215. package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
  216. package/pennyfarthing_scripts/sprint/__pycache__/__main__.cpython-314.pyc +0 -0
  217. package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
  218. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  219. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  220. package/pennyfarthing_scripts/sprint/__pycache__/status.cpython-314.pyc +0 -0
  221. package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-314.pyc +0 -0
  222. package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-314.pyc +0 -0
  223. package/pennyfarthing_scripts/sprint/archive.py +108 -0
  224. package/pennyfarthing_scripts/sprint/cli.py +124 -0
  225. package/pennyfarthing_scripts/sprint/loader.py +193 -0
  226. package/pennyfarthing_scripts/sprint/status.py +122 -0
  227. package/pennyfarthing_scripts/sprint/validator.py +405 -0
  228. package/pennyfarthing_scripts/sprint/work.py +192 -0
  229. package/pennyfarthing_scripts/story/__init__.py +67 -0
  230. package/pennyfarthing_scripts/story/__main__.py +10 -0
  231. package/pennyfarthing_scripts/story/cli.py +105 -0
  232. package/pennyfarthing_scripts/story/create.py +167 -0
  233. package/pennyfarthing_scripts/story/size.py +113 -0
  234. package/pennyfarthing_scripts/story/template.py +151 -0
  235. package/pennyfarthing_scripts/swebench.py +216 -0
  236. package/pennyfarthing_scripts/tests/__init__.py +1 -0
  237. package/pennyfarthing_scripts/tests/__pycache__/__init__.cpython-314.pyc +0 -0
  238. package/pennyfarthing_scripts/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
  239. package/pennyfarthing_scripts/tests/__pycache__/test_brownfield.cpython-314-pytest-9.0.2.pyc +0 -0
  240. package/pennyfarthing_scripts/tests/__pycache__/test_git_utils.cpython-314-pytest-9.0.2.pyc +0 -0
  241. package/pennyfarthing_scripts/tests/__pycache__/test_prime.cpython-314-pytest-9.0.2.pyc +0 -0
  242. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
  243. package/pennyfarthing_scripts/tests/conftest.py +106 -0
  244. package/pennyfarthing_scripts/tests/test_brownfield.py +842 -0
  245. package/pennyfarthing_scripts/tests/test_cli_modules.py +245 -0
  246. package/pennyfarthing_scripts/tests/test_common.py +180 -0
  247. package/pennyfarthing_scripts/tests/test_git_utils.py +866 -0
  248. package/pennyfarthing_scripts/tests/test_jira_package.py +334 -0
  249. package/pennyfarthing_scripts/tests/test_package_structure.py +372 -0
  250. package/pennyfarthing_scripts/tests/test_prime.py +1050 -0
  251. package/pennyfarthing_scripts/tests/test_sprint_package.py +236 -0
  252. package/pennyfarthing_scripts/tests/test_sprint_validator.py +675 -0
  253. package/pennyfarthing_scripts/tests/test_story_package.py +156 -0
  254. package/pennyfarthing_scripts/welcome_hook.py +157 -0
  255. package/pennyfarthing_scripts/workflow.py +183 -0
  256. package/pennyfarthing-dist/personas/BENCHMARK-METHODOLOGY.md +0 -105
  257. package/pennyfarthing-dist/personas/OCEAN-BENCHMARKING.md +0 -210
  258. package/pennyfarthing-dist/personas/TRAIL-OCEAN-MAPPING.md +0 -168
  259. package/pennyfarthing-dist/personas/ZEITGEIST-ANALYSIS.md +0 -171
  260. package/pennyfarthing-dist/personas/attributes.yaml +0 -69
  261. package/pennyfarthing-dist/personas/scripts/add-zeitgeist-calibrated.py +0 -81
  262. package/pennyfarthing-dist/personas/scripts/add-zeitgeist-scores.sh +0 -56
  263. package/pennyfarthing-dist/personas/zeitgeist-scores.yaml +0 -1172
  264. package/pennyfarthing-dist/scripts/core/run.sh +0 -92
  265. /package/{bin → packages/core/bin}/pennyfarthing.js +0 -0
  266. /package/{dist → packages/core/dist}/bmad/context-reader.d.ts +0 -0
  267. /package/{dist → packages/core/dist}/bmad/context-reader.d.ts.map +0 -0
  268. /package/{dist → packages/core/dist}/bmad/context-reader.js +0 -0
  269. /package/{dist → packages/core/dist}/bmad/context-reader.js.map +0 -0
  270. /package/{dist → packages/core/dist}/bmad/context-reader.test.d.ts +0 -0
  271. /package/{dist → packages/core/dist}/bmad/context-reader.test.d.ts.map +0 -0
  272. /package/{dist → packages/core/dist}/bmad/context-reader.test.js +0 -0
  273. /package/{dist → packages/core/dist}/bmad/context-reader.test.js.map +0 -0
  274. /package/{dist → packages/core/dist}/bmad/epics-parser.d.ts +0 -0
  275. /package/{dist → packages/core/dist}/bmad/epics-parser.d.ts.map +0 -0
  276. /package/{dist → packages/core/dist}/bmad/epics-parser.js +0 -0
  277. /package/{dist → packages/core/dist}/bmad/epics-parser.js.map +0 -0
  278. /package/{dist → packages/core/dist}/bmad/epics-parser.test.d.ts +0 -0
  279. /package/{dist → packages/core/dist}/bmad/epics-parser.test.d.ts.map +0 -0
  280. /package/{dist → packages/core/dist}/bmad/epics-parser.test.js +0 -0
  281. /package/{dist → packages/core/dist}/bmad/epics-parser.test.js.map +0 -0
  282. /package/{dist → packages/core/dist}/bmad/index.d.ts +0 -0
  283. /package/{dist → packages/core/dist}/bmad/index.d.ts.map +0 -0
  284. /package/{dist → packages/core/dist}/bmad/index.js +0 -0
  285. /package/{dist → packages/core/dist}/bmad/index.js.map +0 -0
  286. /package/{dist → packages/core/dist}/bmad/status-sync.d.ts +0 -0
  287. /package/{dist → packages/core/dist}/bmad/status-sync.d.ts.map +0 -0
  288. /package/{dist → packages/core/dist}/bmad/status-sync.js +0 -0
  289. /package/{dist → packages/core/dist}/bmad/status-sync.js.map +0 -0
  290. /package/{dist → packages/core/dist}/bmad/status-sync.test.d.ts +0 -0
  291. /package/{dist → packages/core/dist}/bmad/status-sync.test.d.ts.map +0 -0
  292. /package/{dist → packages/core/dist}/bmad/status-sync.test.js +0 -0
  293. /package/{dist → packages/core/dist}/bmad/status-sync.test.js.map +0 -0
  294. /package/{dist → packages/core/dist}/bmad/story-exporter.d.ts +0 -0
  295. /package/{dist → packages/core/dist}/bmad/story-exporter.d.ts.map +0 -0
  296. /package/{dist → packages/core/dist}/bmad/story-exporter.js +0 -0
  297. /package/{dist → packages/core/dist}/bmad/story-exporter.js.map +0 -0
  298. /package/{dist → packages/core/dist}/bmad/story-exporter.test.d.ts +0 -0
  299. /package/{dist → packages/core/dist}/bmad/story-exporter.test.d.ts.map +0 -0
  300. /package/{dist → packages/core/dist}/bmad/story-exporter.test.js +0 -0
  301. /package/{dist → packages/core/dist}/bmad/story-exporter.test.js.map +0 -0
  302. /package/{dist → packages/core/dist}/bmad/story-parser.d.ts +0 -0
  303. /package/{dist → packages/core/dist}/bmad/story-parser.d.ts.map +0 -0
  304. /package/{dist → packages/core/dist}/bmad/story-parser.js +0 -0
  305. /package/{dist → packages/core/dist}/bmad/story-parser.js.map +0 -0
  306. /package/{dist → packages/core/dist}/bmad/story-parser.test.d.ts +0 -0
  307. /package/{dist → packages/core/dist}/bmad/story-parser.test.d.ts.map +0 -0
  308. /package/{dist → packages/core/dist}/bmad/story-parser.test.js +0 -0
  309. /package/{dist → packages/core/dist}/bmad/story-parser.test.js.map +0 -0
  310. /package/{dist → packages/core/dist}/cli/commands/command.d.ts +0 -0
  311. /package/{dist → packages/core/dist}/cli/commands/command.d.ts.map +0 -0
  312. /package/{dist → packages/core/dist}/cli/commands/command.js +0 -0
  313. /package/{dist → packages/core/dist}/cli/commands/command.js.map +0 -0
  314. /package/{dist → packages/core/dist}/cli/commands/cyclist.d.ts +0 -0
  315. /package/{dist → packages/core/dist}/cli/commands/cyclist.d.ts.map +0 -0
  316. /package/{dist → packages/core/dist}/cli/commands/cyclist.js +0 -0
  317. /package/{dist → packages/core/dist}/cli/commands/cyclist.js.map +0 -0
  318. /package/{dist → packages/core/dist}/cli/commands/cyclist.test.d.ts +0 -0
  319. /package/{dist → packages/core/dist}/cli/commands/cyclist.test.d.ts.map +0 -0
  320. /package/{dist → packages/core/dist}/cli/commands/cyclist.test.js +0 -0
  321. /package/{dist → packages/core/dist}/cli/commands/cyclist.test.js.map +0 -0
  322. /package/{dist → packages/core/dist}/cli/commands/doctor-legacy.test.d.ts +0 -0
  323. /package/{dist → packages/core/dist}/cli/commands/doctor-legacy.test.d.ts.map +0 -0
  324. /package/{dist → packages/core/dist}/cli/commands/doctor-legacy.test.js +0 -0
  325. /package/{dist → packages/core/dist}/cli/commands/doctor-legacy.test.js.map +0 -0
  326. /package/{dist → packages/core/dist}/cli/commands/doctor.d.ts +0 -0
  327. /package/{dist → packages/core/dist}/cli/commands/doctor.d.ts.map +0 -0
  328. /package/{dist → packages/core/dist}/cli/commands/doctor.js +0 -0
  329. /package/{dist → packages/core/dist}/cli/commands/doctor.js.map +0 -0
  330. /package/{dist → packages/core/dist}/cli/commands/init.d.ts +0 -0
  331. /package/{dist → packages/core/dist}/cli/commands/init.d.ts.map +0 -0
  332. /package/{dist → packages/core/dist}/cli/commands/init.js +0 -0
  333. /package/{dist → packages/core/dist}/cli/commands/init.js.map +0 -0
  334. /package/{dist → packages/core/dist}/cli/commands/skill.d.ts +0 -0
  335. /package/{dist → packages/core/dist}/cli/commands/skill.d.ts.map +0 -0
  336. /package/{dist → packages/core/dist}/cli/commands/skill.js +0 -0
  337. /package/{dist → packages/core/dist}/cli/commands/skill.js.map +0 -0
  338. /package/{dist → packages/core/dist}/cli/commands/theme.d.ts +0 -0
  339. /package/{dist → packages/core/dist}/cli/commands/theme.d.ts.map +0 -0
  340. /package/{dist → packages/core/dist}/cli/commands/theme.js +0 -0
  341. /package/{dist → packages/core/dist}/cli/commands/theme.js.map +0 -0
  342. /package/{dist → packages/core/dist}/cli/commands/uninstall.d.ts +0 -0
  343. /package/{dist → packages/core/dist}/cli/commands/uninstall.d.ts.map +0 -0
  344. /package/{dist → packages/core/dist}/cli/commands/uninstall.js +0 -0
  345. /package/{dist → packages/core/dist}/cli/commands/uninstall.js.map +0 -0
  346. /package/{dist → packages/core/dist}/cli/commands/update.d.ts +0 -0
  347. /package/{dist → packages/core/dist}/cli/commands/update.d.ts.map +0 -0
  348. /package/{dist → packages/core/dist}/cli/commands/update.js +0 -0
  349. /package/{dist → packages/core/dist}/cli/commands/update.js.map +0 -0
  350. /package/{dist → packages/core/dist}/cli/commands/version.d.ts +0 -0
  351. /package/{dist → packages/core/dist}/cli/commands/version.d.ts.map +0 -0
  352. /package/{dist → packages/core/dist}/cli/commands/version.js +0 -0
  353. /package/{dist → packages/core/dist}/cli/commands/version.js.map +0 -0
  354. /package/{dist → packages/core/dist}/cli/customization.test.d.ts +0 -0
  355. /package/{dist → packages/core/dist}/cli/customization.test.d.ts.map +0 -0
  356. /package/{dist → packages/core/dist}/cli/customization.test.js +0 -0
  357. /package/{dist → packages/core/dist}/cli/customization.test.js.map +0 -0
  358. /package/{dist → packages/core/dist}/cli/cyclist-migration.test.d.ts +0 -0
  359. /package/{dist → packages/core/dist}/cli/cyclist-migration.test.d.ts.map +0 -0
  360. /package/{dist → packages/core/dist}/cli/cyclist-migration.test.js +0 -0
  361. /package/{dist → packages/core/dist}/cli/cyclist-migration.test.js.map +0 -0
  362. /package/{dist → packages/core/dist}/cli/index.d.ts +0 -0
  363. /package/{dist → packages/core/dist}/cli/index.d.ts.map +0 -0
  364. /package/{dist → packages/core/dist}/cli/index.js +0 -0
  365. /package/{dist → packages/core/dist}/cli/index.js.map +0 -0
  366. /package/{dist → packages/core/dist}/cli/ocean-profiles.test.d.ts +0 -0
  367. /package/{dist → packages/core/dist}/cli/ocean-profiles.test.d.ts.map +0 -0
  368. /package/{dist → packages/core/dist}/cli/ocean-profiles.test.js +0 -0
  369. /package/{dist → packages/core/dist}/cli/ocean-profiles.test.js.map +0 -0
  370. /package/{dist → packages/core/dist}/cli/theme-maker.test.d.ts +0 -0
  371. /package/{dist → packages/core/dist}/cli/theme-maker.test.d.ts.map +0 -0
  372. /package/{dist → packages/core/dist}/cli/theme-maker.test.js +0 -0
  373. /package/{dist → packages/core/dist}/cli/theme-maker.test.js.map +0 -0
  374. /package/{dist → packages/core/dist}/cli/utils/constants.d.ts +0 -0
  375. /package/{dist → packages/core/dist}/cli/utils/constants.d.ts.map +0 -0
  376. /package/{dist → packages/core/dist}/cli/utils/constants.js +0 -0
  377. /package/{dist → packages/core/dist}/cli/utils/constants.js.map +0 -0
  378. /package/{dist → packages/core/dist}/cli/utils/logger.d.ts +0 -0
  379. /package/{dist → packages/core/dist}/cli/utils/logger.d.ts.map +0 -0
  380. /package/{dist → packages/core/dist}/cli/utils/logger.js +0 -0
  381. /package/{dist → packages/core/dist}/cli/utils/logger.js.map +0 -0
  382. /package/{dist → packages/core/dist}/cli/utils/manifest.d.ts +0 -0
  383. /package/{dist → packages/core/dist}/cli/utils/manifest.d.ts.map +0 -0
  384. /package/{dist → packages/core/dist}/cli/utils/manifest.js +0 -0
  385. /package/{dist → packages/core/dist}/cli/utils/manifest.js.map +0 -0
  386. /package/{dist → packages/core/dist}/cli/utils/node-modules.d.ts +0 -0
  387. /package/{dist → packages/core/dist}/cli/utils/node-modules.d.ts.map +0 -0
  388. /package/{dist → packages/core/dist}/cli/utils/node-modules.js +0 -0
  389. /package/{dist → packages/core/dist}/cli/utils/node-modules.js.map +0 -0
  390. /package/{dist → packages/core/dist}/cli/utils/prompts.d.ts +0 -0
  391. /package/{dist → packages/core/dist}/cli/utils/prompts.d.ts.map +0 -0
  392. /package/{dist → packages/core/dist}/cli/utils/prompts.js +0 -0
  393. /package/{dist → packages/core/dist}/cli/utils/prompts.js.map +0 -0
  394. /package/{dist → packages/core/dist}/cli/utils/symlinks.d.ts +0 -0
  395. /package/{dist → packages/core/dist}/cli/utils/symlinks.d.ts.map +0 -0
  396. /package/{dist → packages/core/dist}/cli/utils/symlinks.js +0 -0
  397. /package/{dist → packages/core/dist}/cli/utils/symlinks.js.map +0 -0
  398. /package/{dist → packages/core/dist}/cli/utils/themes.d.ts +0 -0
  399. /package/{dist → packages/core/dist}/cli/utils/themes.d.ts.map +0 -0
  400. /package/{dist → packages/core/dist}/cli/utils/themes.js +0 -0
  401. /package/{dist → packages/core/dist}/cli/utils/themes.js.map +0 -0
  402. /package/{dist → packages/core/dist}/cli/utils/themes.test.d.ts +0 -0
  403. /package/{dist → packages/core/dist}/cli/utils/themes.test.d.ts.map +0 -0
  404. /package/{dist → packages/core/dist}/cli/utils/themes.test.js +0 -0
  405. /package/{dist → packages/core/dist}/cli/utils/themes.test.js.map +0 -0
  406. /package/{dist → packages/core/dist}/cli/utils/version.d.ts +0 -0
  407. /package/{dist → packages/core/dist}/cli/utils/version.d.ts.map +0 -0
  408. /package/{dist → packages/core/dist}/cli/utils/version.js +0 -0
  409. /package/{dist → packages/core/dist}/cli/utils/version.js.map +0 -0
  410. /package/{dist → packages/core/dist}/cli/workspace.test.d.ts +0 -0
  411. /package/{dist → packages/core/dist}/cli/workspace.test.d.ts.map +0 -0
  412. /package/{dist → packages/core/dist}/index.d.ts +0 -0
  413. /package/{dist → packages/core/dist}/index.d.ts.map +0 -0
  414. /package/{dist → packages/core/dist}/index.js +0 -0
  415. /package/{dist → packages/core/dist}/index.js.map +0 -0
  416. /package/{dist → packages/core/dist}/jira/jira-epic-creation.d.ts +0 -0
  417. /package/{dist → packages/core/dist}/jira/jira-epic-creation.d.ts.map +0 -0
  418. /package/{dist → packages/core/dist}/jira/jira-epic-creation.js +0 -0
  419. /package/{dist → packages/core/dist}/jira/jira-epic-creation.js.map +0 -0
  420. /package/{dist → packages/core/dist}/jira/jira-epic-creation.test.d.ts +0 -0
  421. /package/{dist → packages/core/dist}/jira/jira-epic-creation.test.d.ts.map +0 -0
  422. /package/{dist → packages/core/dist}/jira/jira-epic-creation.test.js +0 -0
  423. /package/{dist → packages/core/dist}/jira/jira-epic-creation.test.js.map +0 -0
  424. /package/{dist → packages/core/dist}/jira/jira-sprint-sync.d.ts +0 -0
  425. /package/{dist → packages/core/dist}/jira/jira-sprint-sync.d.ts.map +0 -0
  426. /package/{dist → packages/core/dist}/jira/jira-sprint-sync.js +0 -0
  427. /package/{dist → packages/core/dist}/jira/jira-sprint-sync.js.map +0 -0
  428. /package/{dist → packages/core/dist}/jira/jira-sprint-sync.test.d.ts +0 -0
  429. /package/{dist → packages/core/dist}/jira/jira-sprint-sync.test.d.ts.map +0 -0
  430. /package/{dist → packages/core/dist}/jira/jira-sprint-sync.test.js +0 -0
  431. /package/{dist → packages/core/dist}/jira/jira-sprint-sync.test.js.map +0 -0
  432. /package/{dist → packages/core/dist}/permissions/index.d.ts +0 -0
  433. /package/{dist → packages/core/dist}/permissions/index.d.ts.map +0 -0
  434. /package/{dist → packages/core/dist}/permissions/index.js +0 -0
  435. /package/{dist → packages/core/dist}/permissions/index.js.map +0 -0
  436. /package/{dist → packages/core/dist}/permissions/permission-schema.d.ts +0 -0
  437. /package/{dist → packages/core/dist}/permissions/permission-schema.d.ts.map +0 -0
  438. /package/{dist → packages/core/dist}/permissions/permission-schema.js +0 -0
  439. /package/{dist → packages/core/dist}/permissions/permission-schema.js.map +0 -0
  440. /package/{dist → packages/core/dist}/permissions/permission-schema.test.d.ts +0 -0
  441. /package/{dist → packages/core/dist}/permissions/permission-schema.test.d.ts.map +0 -0
  442. /package/{dist → packages/core/dist}/permissions/permission-schema.test.js +0 -0
  443. /package/{dist → packages/core/dist}/permissions/permission-schema.test.js.map +0 -0
  444. /package/{dist → packages/core/dist}/scripts/add-ocean-profiles.d.ts +0 -0
  445. /package/{dist → packages/core/dist}/scripts/add-ocean-profiles.d.ts.map +0 -0
  446. /package/{dist → packages/core/dist}/scripts/add-ocean-profiles.js +0 -0
  447. /package/{dist → packages/core/dist}/scripts/add-ocean-profiles.js.map +0 -0
  448. /package/{dist → packages/core/dist}/scripts/benchmark-integration.d.ts +0 -0
  449. /package/{dist → packages/core/dist}/scripts/benchmark-integration.d.ts.map +0 -0
  450. /package/{dist → packages/core/dist}/scripts/benchmark-integration.js +0 -0
  451. /package/{dist → packages/core/dist}/scripts/benchmark-integration.js.map +0 -0
  452. /package/{dist → packages/core/dist}/scripts/benchmark-integration.test.d.ts +0 -0
  453. /package/{dist → packages/core/dist}/scripts/benchmark-integration.test.d.ts.map +0 -0
  454. /package/{dist → packages/core/dist}/scripts/benchmark-integration.test.js +0 -0
  455. /package/{dist → packages/core/dist}/scripts/benchmark-integration.test.js.map +0 -0
  456. /package/{dist → packages/core/dist}/scripts/debugging-scenarios.test.d.ts +0 -0
  457. /package/{dist → packages/core/dist}/scripts/debugging-scenarios.test.d.ts.map +0 -0
  458. /package/{dist → packages/core/dist}/scripts/debugging-scenarios.test.js +0 -0
  459. /package/{dist → packages/core/dist}/scripts/debugging-scenarios.test.js.map +0 -0
  460. /package/{dist → packages/core/dist}/scripts/generate-all-spiders.d.ts +0 -0
  461. /package/{dist → packages/core/dist}/scripts/generate-all-spiders.d.ts.map +0 -0
  462. /package/{dist → packages/core/dist}/scripts/generate-all-spiders.js +0 -0
  463. /package/{dist → packages/core/dist}/scripts/generate-all-spiders.js.map +0 -0
  464. /package/{dist → packages/core/dist}/scripts/generate-report.d.ts +0 -0
  465. /package/{dist → packages/core/dist}/scripts/generate-report.d.ts.map +0 -0
  466. /package/{dist → packages/core/dist}/scripts/generate-report.js +0 -0
  467. /package/{dist → packages/core/dist}/scripts/generate-report.js.map +0 -0
  468. /package/{dist → packages/core/dist}/scripts/generate-report.test.d.ts +0 -0
  469. /package/{dist → packages/core/dist}/scripts/generate-report.test.d.ts.map +0 -0
  470. /package/{dist → packages/core/dist}/scripts/generate-report.test.js +0 -0
  471. /package/{dist → packages/core/dist}/scripts/generate-report.test.js.map +0 -0
  472. /package/{dist → packages/core/dist}/scripts/generate-spider-report.d.ts +0 -0
  473. /package/{dist → packages/core/dist}/scripts/generate-spider-report.d.ts.map +0 -0
  474. /package/{dist → packages/core/dist}/scripts/generate-spider-report.js +0 -0
  475. /package/{dist → packages/core/dist}/scripts/generate-spider-report.js.map +0 -0
  476. /package/{dist → packages/core/dist}/scripts/generate-spider-report.test.d.ts +0 -0
  477. /package/{dist → packages/core/dist}/scripts/generate-spider-report.test.d.ts.map +0 -0
  478. /package/{dist → packages/core/dist}/scripts/generate-spider-report.test.js +0 -0
  479. /package/{dist → packages/core/dist}/scripts/generate-spider-report.test.js.map +0 -0
  480. /package/{dist → packages/core/dist}/scripts/generate-spider.d.ts +0 -0
  481. /package/{dist → packages/core/dist}/scripts/generate-spider.d.ts.map +0 -0
  482. /package/{dist → packages/core/dist}/scripts/generate-spider.js +0 -0
  483. /package/{dist → packages/core/dist}/scripts/generate-spider.js.map +0 -0
  484. /package/{dist → packages/core/dist}/scripts/generate-spider.test.d.ts +0 -0
  485. /package/{dist → packages/core/dist}/scripts/generate-spider.test.d.ts.map +0 -0
  486. /package/{dist → packages/core/dist}/scripts/generate-spider.test.js +0 -0
  487. /package/{dist → packages/core/dist}/scripts/generate-spider.test.js.map +0 -0
  488. /package/{dist → packages/core/dist}/scripts/job-fair-aggregator.d.ts +0 -0
  489. /package/{dist → packages/core/dist}/scripts/job-fair-aggregator.d.ts.map +0 -0
  490. /package/{dist → packages/core/dist}/scripts/job-fair-aggregator.js +0 -0
  491. /package/{dist → packages/core/dist}/scripts/job-fair-aggregator.js.map +0 -0
  492. /package/{dist → packages/core/dist}/scripts/job-fair-aggregator.test.d.ts +0 -0
  493. /package/{dist → packages/core/dist}/scripts/job-fair-aggregator.test.d.ts.map +0 -0
  494. /package/{dist → packages/core/dist}/scripts/job-fair-aggregator.test.js +0 -0
  495. /package/{dist → packages/core/dist}/scripts/job-fair-aggregator.test.js.map +0 -0
  496. /package/{dist → packages/core/dist}/scripts/run-ci.test.d.ts +0 -0
  497. /package/{dist → packages/core/dist}/scripts/run-ci.test.d.ts.map +0 -0
  498. /package/{dist → packages/core/dist}/scripts/run-ci.test.js +0 -0
  499. /package/{dist → packages/core/dist}/scripts/run-ci.test.js.map +0 -0
  500. /package/{dist → packages/core/dist}/scripts/theme-detail.test.d.ts +0 -0
  501. /package/{dist → packages/core/dist}/scripts/theme-detail.test.d.ts.map +0 -0
  502. /package/{dist → packages/core/dist}/scripts/theme-detail.test.js +0 -0
  503. /package/{dist → packages/core/dist}/scripts/theme-detail.test.js.map +0 -0
  504. /package/{dist → packages/core/dist}/scripts/validate-ocean-profiles.d.ts +0 -0
  505. /package/{dist → packages/core/dist}/scripts/validate-ocean-profiles.d.ts.map +0 -0
  506. /package/{dist → packages/core/dist}/scripts/validate-ocean-profiles.js +0 -0
  507. /package/{dist → packages/core/dist}/scripts/validate-ocean-profiles.js.map +0 -0
  508. /package/{dist → packages/core/dist}/workflow/gate-handler.d.ts +0 -0
  509. /package/{dist → packages/core/dist}/workflow/gate-handler.d.ts.map +0 -0
  510. /package/{dist → packages/core/dist}/workflow/gate-handler.js +0 -0
  511. /package/{dist → packages/core/dist}/workflow/gate-handler.js.map +0 -0
  512. /package/{dist → packages/core/dist}/workflow/gate-handler.test.d.ts +0 -0
  513. /package/{dist → packages/core/dist}/workflow/gate-handler.test.d.ts.map +0 -0
  514. /package/{dist → packages/core/dist}/workflow/gate-handler.test.js +0 -0
  515. /package/{dist → packages/core/dist}/workflow/gate-handler.test.js.map +0 -0
  516. /package/{dist → packages/core/dist}/workflow/generic-sm-finish.d.ts +0 -0
  517. /package/{dist → packages/core/dist}/workflow/generic-sm-finish.d.ts.map +0 -0
  518. /package/{dist → packages/core/dist}/workflow/generic-sm-finish.js +0 -0
  519. /package/{dist → packages/core/dist}/workflow/generic-sm-finish.js.map +0 -0
  520. /package/{dist → packages/core/dist}/workflow/generic-sm-setup.d.ts +0 -0
  521. /package/{dist → packages/core/dist}/workflow/generic-sm-setup.d.ts.map +0 -0
  522. /package/{dist → packages/core/dist}/workflow/generic-sm-setup.js +0 -0
  523. /package/{dist → packages/core/dist}/workflow/generic-sm-setup.js.map +0 -0
  524. /package/{dist → packages/core/dist}/workflow/handoff.d.ts +0 -0
  525. /package/{dist → packages/core/dist}/workflow/handoff.d.ts.map +0 -0
  526. /package/{dist → packages/core/dist}/workflow/handoff.js +0 -0
  527. /package/{dist → packages/core/dist}/workflow/handoff.js.map +0 -0
  528. /package/{dist → packages/core/dist}/workflow/handoff.test.d.ts +0 -0
  529. /package/{dist → packages/core/dist}/workflow/handoff.test.d.ts.map +0 -0
  530. /package/{dist → packages/core/dist}/workflow/handoff.test.js +0 -0
  531. /package/{dist → packages/core/dist}/workflow/handoff.test.js.map +0 -0
  532. /package/{dist → packages/core/dist}/workflow/index.d.ts +0 -0
  533. /package/{dist → packages/core/dist}/workflow/index.d.ts.map +0 -0
  534. /package/{dist → packages/core/dist}/workflow/index.js +0 -0
  535. /package/{dist → packages/core/dist}/workflow/index.js.map +0 -0
  536. /package/{dist → packages/core/dist}/workflow/session-state.d.ts +0 -0
  537. /package/{dist → packages/core/dist}/workflow/session-state.d.ts.map +0 -0
  538. /package/{dist → packages/core/dist}/workflow/session-state.js +0 -0
  539. /package/{dist → packages/core/dist}/workflow/session-state.js.map +0 -0
  540. /package/{dist → packages/core/dist}/workflow/session-state.test.d.ts +0 -0
  541. /package/{dist → packages/core/dist}/workflow/session-state.test.d.ts.map +0 -0
  542. /package/{dist → packages/core/dist}/workflow/session-state.test.js +0 -0
  543. /package/{dist → packages/core/dist}/workflow/session-state.test.js.map +0 -0
  544. /package/{dist → packages/core/dist}/workflow/sm-subagents.test.d.ts +0 -0
  545. /package/{dist → packages/core/dist}/workflow/sm-subagents.test.d.ts.map +0 -0
  546. /package/{dist → packages/core/dist}/workflow/sm-subagents.test.js +0 -0
  547. /package/{dist → packages/core/dist}/workflow/sm-subagents.test.js.map +0 -0
  548. /package/{dist → packages/core/dist}/workflow/step-parser.d.ts +0 -0
  549. /package/{dist → packages/core/dist}/workflow/step-parser.d.ts.map +0 -0
  550. /package/{dist → packages/core/dist}/workflow/step-parser.js +0 -0
  551. /package/{dist → packages/core/dist}/workflow/step-parser.js.map +0 -0
  552. /package/{dist → packages/core/dist}/workflow/step-parser.test.d.ts +0 -0
  553. /package/{dist → packages/core/dist}/workflow/step-parser.test.d.ts.map +0 -0
  554. /package/{dist → packages/core/dist}/workflow/step-parser.test.js +0 -0
  555. /package/{dist → packages/core/dist}/workflow/step-parser.test.js.map +0 -0
  556. /package/{dist → packages/core/dist}/workflow/story-workflow-routing.test.d.ts +0 -0
  557. /package/{dist → packages/core/dist}/workflow/story-workflow-routing.test.d.ts.map +0 -0
  558. /package/{dist → packages/core/dist}/workflow/story-workflow-routing.test.js +0 -0
  559. /package/{dist → packages/core/dist}/workflow/story-workflow-routing.test.js.map +0 -0
  560. /package/{dist → packages/core/dist}/workflow/test-cache.d.ts +0 -0
  561. /package/{dist → packages/core/dist}/workflow/test-cache.d.ts.map +0 -0
  562. /package/{dist → packages/core/dist}/workflow/test-cache.js +0 -0
  563. /package/{dist → packages/core/dist}/workflow/test-cache.js.map +0 -0
  564. /package/{dist → packages/core/dist}/workflow/test-cache.test.d.ts +0 -0
  565. /package/{dist → packages/core/dist}/workflow/test-cache.test.d.ts.map +0 -0
  566. /package/{dist → packages/core/dist}/workflow/test-cache.test.js +0 -0
  567. /package/{dist → packages/core/dist}/workflow/test-cache.test.js.map +0 -0
  568. /package/{dist → packages/core/dist}/workflow/trimodal.d.ts +0 -0
  569. /package/{dist → packages/core/dist}/workflow/trimodal.d.ts.map +0 -0
  570. /package/{dist → packages/core/dist}/workflow/trimodal.js +0 -0
  571. /package/{dist → packages/core/dist}/workflow/trimodal.js.map +0 -0
  572. /package/{dist → packages/core/dist}/workflow/trimodal.test.d.ts +0 -0
  573. /package/{dist → packages/core/dist}/workflow/trimodal.test.d.ts.map +0 -0
  574. /package/{dist → packages/core/dist}/workflow/trimodal.test.js +0 -0
  575. /package/{dist → packages/core/dist}/workflow/trimodal.test.js.map +0 -0
  576. /package/{dist → packages/core/dist}/workflow/variable-resolver.d.ts +0 -0
  577. /package/{dist → packages/core/dist}/workflow/variable-resolver.d.ts.map +0 -0
  578. /package/{dist → packages/core/dist}/workflow/variable-resolver.js +0 -0
  579. /package/{dist → packages/core/dist}/workflow/variable-resolver.js.map +0 -0
  580. /package/{dist → packages/core/dist}/workflow/variable-resolver.test.d.ts +0 -0
  581. /package/{dist → packages/core/dist}/workflow/variable-resolver.test.d.ts.map +0 -0
  582. /package/{dist → packages/core/dist}/workflow/variable-resolver.test.js +0 -0
  583. /package/{dist → packages/core/dist}/workflow/variable-resolver.test.js.map +0 -0
  584. /package/{dist → packages/core/dist}/workflow/workflow-executor.d.ts +0 -0
  585. /package/{dist → packages/core/dist}/workflow/workflow-executor.d.ts.map +0 -0
  586. /package/{dist → packages/core/dist}/workflow/workflow-executor.js +0 -0
  587. /package/{dist → packages/core/dist}/workflow/workflow-executor.js.map +0 -0
  588. /package/{dist → packages/core/dist}/workflow/workflow-executor.test.d.ts +0 -0
  589. /package/{dist → packages/core/dist}/workflow/workflow-executor.test.d.ts.map +0 -0
  590. /package/{dist → packages/core/dist}/workflow/workflow-executor.test.js +0 -0
  591. /package/{dist → packages/core/dist}/workflow/workflow-executor.test.js.map +0 -0
  592. /package/{dist → packages/core/dist}/workflow/workflow-loader.d.ts +0 -0
  593. /package/{dist → packages/core/dist}/workflow/workflow-loader.d.ts.map +0 -0
  594. /package/{dist → packages/core/dist}/workflow/workflow-loader.js +0 -0
  595. /package/{dist → packages/core/dist}/workflow/workflow-loader.js.map +0 -0
  596. /package/{dist → packages/core/dist}/workflow/workflow-loader.test.d.ts +0 -0
  597. /package/{dist → packages/core/dist}/workflow/workflow-loader.test.d.ts.map +0 -0
  598. /package/{dist → packages/core/dist}/workflow/workflow-loader.test.js +0 -0
  599. /package/{dist → packages/core/dist}/workflow/workflow-loader.test.js.map +0 -0
  600. /package/{dist → packages/core/dist}/workflow/workflow-migration.test.d.ts +0 -0
  601. /package/{dist → packages/core/dist}/workflow/workflow-migration.test.d.ts.map +0 -0
  602. /package/{dist → packages/core/dist}/workflow/workflow-migration.test.js +0 -0
  603. /package/{dist → packages/core/dist}/workflow/workflow-migration.test.js.map +0 -0
  604. /package/{dist → packages/core/dist}/workflow/workflow-permissions.d.ts +0 -0
  605. /package/{dist → packages/core/dist}/workflow/workflow-permissions.d.ts.map +0 -0
  606. /package/{dist → packages/core/dist}/workflow/workflow-permissions.js +0 -0
  607. /package/{dist → packages/core/dist}/workflow/workflow-permissions.js.map +0 -0
  608. /package/{dist → packages/core/dist}/workflow/workflow-permissions.test.d.ts +0 -0
  609. /package/{dist → packages/core/dist}/workflow/workflow-permissions.test.d.ts.map +0 -0
  610. /package/{dist → packages/core/dist}/workflow/workflow-permissions.test.js +0 -0
  611. /package/{dist → packages/core/dist}/workflow/workflow-permissions.test.js.map +0 -0
  612. /package/{dist → packages/core/dist}/workflow/workflow-router.d.ts +0 -0
  613. /package/{dist → packages/core/dist}/workflow/workflow-router.d.ts.map +0 -0
  614. /package/{dist → packages/core/dist}/workflow/workflow-router.js +0 -0
  615. /package/{dist → packages/core/dist}/workflow/workflow-router.js.map +0 -0
  616. /package/{dist → packages/core/dist}/workflow/workflow-router.test.d.ts +0 -0
  617. /package/{dist → packages/core/dist}/workflow/workflow-router.test.d.ts.map +0 -0
  618. /package/{dist → packages/core/dist}/workflow/workflow-router.test.js +0 -0
  619. /package/{dist → packages/core/dist}/workflow/workflow-router.test.js.map +0 -0
  620. /package/{dist → packages/core/dist}/workflow/workflow-schema.d.ts +0 -0
  621. /package/{dist → packages/core/dist}/workflow/workflow-schema.d.ts.map +0 -0
  622. /package/{dist → packages/core/dist}/workflow/workflow-schema.js +0 -0
  623. /package/{dist → packages/core/dist}/workflow/workflow-schema.js.map +0 -0
  624. /package/{dist → packages/core/dist}/workflow/workflow-schema.test.d.ts +0 -0
  625. /package/{dist → packages/core/dist}/workflow/workflow-schema.test.d.ts.map +0 -0
  626. /package/{dist → packages/core/dist}/workflow/workflow-schema.test.js +0 -0
  627. /package/{dist → packages/core/dist}/workflow/workflow-schema.test.js.map +0 -0
  628. /package/{dist → packages/core/dist}/workflow/workflow-stepped-schema.test.d.ts +0 -0
  629. /package/{dist → packages/core/dist}/workflow/workflow-stepped-schema.test.d.ts.map +0 -0
  630. /package/{dist → packages/core/dist}/workflow/workflow-stepped-schema.test.js +0 -0
  631. /package/{dist → packages/core/dist}/workflow/workflow-stepped-schema.test.js.map +0 -0
@@ -0,0 +1,866 @@
1
+ """Tests for git/ utility modules.
2
+
3
+ Story: MSSCI-12402 - Port git utility scripts to Python
4
+
5
+ TDD RED phase: All tests should FAIL until implementation.
6
+
7
+ Acceptance Criteria:
8
+ 1. Python script implements git-status-all functionality with parallel execution
9
+ 2. Python script implements create-feature-branches functionality with parallel execution
10
+ 3. Both scripts use asyncio.gather for parallelism
11
+ 4. Error handling is robust and provides clear messages
12
+ 5. Output formatting is consistent across platforms
13
+ 6. Cross-platform compatibility verified
14
+ 7. Tests pass with 100% coverage
15
+ 8. Performance is equivalent or better than bash versions
16
+ """
17
+
18
+ import asyncio
19
+ import tempfile
20
+ from pathlib import Path
21
+ from typing import Any
22
+ from unittest.mock import AsyncMock, MagicMock, patch
23
+
24
+ import pytest
25
+
26
+ from pennyfarthing_scripts.git.status_all import (
27
+ RepoStatus,
28
+ format_status_brief,
29
+ format_status_full,
30
+ format_summary,
31
+ get_all_repo_status,
32
+ get_repo_status,
33
+ )
34
+ from pennyfarthing_scripts.git.create_branches import (
35
+ BranchAction,
36
+ BranchResult,
37
+ create_feature_branches,
38
+ create_or_checkout_branch,
39
+ detect_worktree,
40
+ filter_repos,
41
+ format_results,
42
+ )
43
+
44
+
45
+ # =============================================================================
46
+ # Test Fixtures
47
+ # =============================================================================
48
+
49
+
50
+ @pytest.fixture
51
+ def temp_git_repo(tmp_path: Path) -> Path:
52
+ """Create a temporary git repository for testing."""
53
+ import subprocess
54
+
55
+ repo_path = tmp_path / "test-repo"
56
+ repo_path.mkdir()
57
+
58
+ # Initialize git repo
59
+ subprocess.run(["git", "init"], cwd=repo_path, capture_output=True)
60
+ subprocess.run(
61
+ ["git", "config", "user.email", "test@test.com"], cwd=repo_path, capture_output=True
62
+ )
63
+ subprocess.run(
64
+ ["git", "config", "user.name", "Test User"], cwd=repo_path, capture_output=True
65
+ )
66
+
67
+ # Create initial commit
68
+ (repo_path / "README.md").write_text("# Test Repo")
69
+ subprocess.run(["git", "add", "."], cwd=repo_path, capture_output=True)
70
+ subprocess.run(
71
+ ["git", "commit", "-m", "Initial commit"], cwd=repo_path, capture_output=True
72
+ )
73
+
74
+ # Create develop branch
75
+ subprocess.run(["git", "branch", "develop"], cwd=repo_path, capture_output=True)
76
+
77
+ return repo_path
78
+
79
+
80
+ @pytest.fixture
81
+ def sample_repos(tmp_path: Path) -> list[tuple[str, Path]]:
82
+ """Create sample repos list for testing."""
83
+ return [
84
+ ("repo-a", tmp_path / "repo-a"),
85
+ ("repo-b", tmp_path / "repo-b"),
86
+ ]
87
+
88
+
89
+ @pytest.fixture
90
+ def clean_repo_status() -> RepoStatus:
91
+ """A clean repo with no changes."""
92
+ return RepoStatus(
93
+ name="clean-repo",
94
+ path=Path("/test/clean-repo"),
95
+ branch="develop",
96
+ changes=[],
97
+ unpushed_commits=[],
98
+ )
99
+
100
+
101
+ @pytest.fixture
102
+ def dirty_repo_status() -> RepoStatus:
103
+ """A repo with uncommitted changes."""
104
+ return RepoStatus(
105
+ name="dirty-repo",
106
+ path=Path("/test/dirty-repo"),
107
+ branch="feature/test",
108
+ changes=[
109
+ " M src/file1.py",
110
+ "?? src/newfile.py",
111
+ ],
112
+ unpushed_commits=[],
113
+ )
114
+
115
+
116
+ @pytest.fixture
117
+ def repo_with_unpushed() -> RepoStatus:
118
+ """A repo with unpushed commits."""
119
+ return RepoStatus(
120
+ name="unpushed-repo",
121
+ path=Path("/test/unpushed-repo"),
122
+ branch="feature/test",
123
+ changes=[],
124
+ unpushed_commits=[
125
+ "abc1234 Add new feature",
126
+ "def5678 Fix bug",
127
+ ],
128
+ )
129
+
130
+
131
+ # =============================================================================
132
+ # AC1: git-status-all functionality
133
+ # =============================================================================
134
+
135
+
136
+ class TestRepoStatus:
137
+ """Tests for RepoStatus dataclass."""
138
+
139
+ def test_is_clean_with_no_changes(self, clean_repo_status: RepoStatus) -> None:
140
+ """is_clean should be True when no changes."""
141
+ assert clean_repo_status.is_clean is True
142
+
143
+ def test_is_clean_with_changes(self, dirty_repo_status: RepoStatus) -> None:
144
+ """is_clean should be False when there are changes."""
145
+ assert dirty_repo_status.is_clean is False
146
+
147
+ def test_has_unpushed_with_no_commits(self, clean_repo_status: RepoStatus) -> None:
148
+ """has_unpushed should be False when no unpushed commits."""
149
+ assert clean_repo_status.has_unpushed is False
150
+
151
+ def test_has_unpushed_with_commits(self, repo_with_unpushed: RepoStatus) -> None:
152
+ """has_unpushed should be True when there are unpushed commits."""
153
+ assert repo_with_unpushed.has_unpushed is True
154
+
155
+
156
+ class TestGetRepoStatus:
157
+ """Tests for get_repo_status function."""
158
+
159
+ @pytest.mark.asyncio
160
+ async def test_returns_repo_status(self, temp_git_repo: Path) -> None:
161
+ """get_repo_status should return a RepoStatus object."""
162
+ result = await get_repo_status("test-repo", temp_git_repo)
163
+
164
+ assert isinstance(result, RepoStatus)
165
+ assert result.name == "test-repo"
166
+ assert result.path == temp_git_repo
167
+
168
+ @pytest.mark.asyncio
169
+ async def test_captures_current_branch(self, temp_git_repo: Path) -> None:
170
+ """get_repo_status should capture the current branch name."""
171
+ result = await get_repo_status("test-repo", temp_git_repo)
172
+
173
+ assert result.branch is not None
174
+ assert isinstance(result.branch, str)
175
+
176
+ @pytest.mark.asyncio
177
+ async def test_handles_detached_head(self, temp_git_repo: Path) -> None:
178
+ """get_repo_status should handle detached HEAD state."""
179
+ # When in detached HEAD, branch should be "detached" or similar
180
+ result = await get_repo_status("test-repo", temp_git_repo)
181
+
182
+ assert result.branch is not None # Should not crash
183
+
184
+ @pytest.mark.asyncio
185
+ async def test_captures_uncommitted_changes(self, temp_git_repo: Path) -> None:
186
+ """get_repo_status should capture uncommitted changes."""
187
+ result = await get_repo_status("test-repo", temp_git_repo)
188
+
189
+ assert isinstance(result.changes, list)
190
+
191
+ @pytest.mark.asyncio
192
+ async def test_captures_unpushed_commits(self, temp_git_repo: Path) -> None:
193
+ """get_repo_status should capture unpushed commits."""
194
+ result = await get_repo_status("test-repo", temp_git_repo)
195
+
196
+ assert isinstance(result.unpushed_commits, list)
197
+
198
+ @pytest.mark.asyncio
199
+ async def test_handles_nonexistent_path(self) -> None:
200
+ """get_repo_status should handle non-existent paths gracefully."""
201
+ result = await get_repo_status("missing-repo", Path("/nonexistent/path"))
202
+
203
+ assert result.error is not None
204
+
205
+ @pytest.mark.asyncio
206
+ async def test_handles_non_git_directory(self, tmp_path: Path) -> None:
207
+ """get_repo_status should handle non-git directories gracefully."""
208
+ result = await get_repo_status("not-git", tmp_path)
209
+
210
+ assert result.error is not None
211
+
212
+
213
+ # =============================================================================
214
+ # AC3: asyncio.gather for parallelism
215
+ # =============================================================================
216
+
217
+
218
+ class TestGetAllRepoStatus:
219
+ """Tests for get_all_repo_status function."""
220
+
221
+ @pytest.mark.asyncio
222
+ async def test_returns_list_of_statuses(
223
+ self, sample_repos: list[tuple[str, Path]]
224
+ ) -> None:
225
+ """get_all_repo_status should return list of RepoStatus objects."""
226
+ results = await get_all_repo_status(sample_repos)
227
+
228
+ assert isinstance(results, list)
229
+ assert len(results) == len(sample_repos)
230
+ assert all(isinstance(r, RepoStatus) for r in results)
231
+
232
+ @pytest.mark.asyncio
233
+ async def test_preserves_order(self, sample_repos: list[tuple[str, Path]]) -> None:
234
+ """get_all_repo_status should preserve input order."""
235
+ results = await get_all_repo_status(sample_repos)
236
+
237
+ for i, (name, _) in enumerate(sample_repos):
238
+ assert results[i].name == name
239
+
240
+ @pytest.mark.asyncio
241
+ async def test_uses_asyncio_gather(
242
+ self, sample_repos: list[tuple[str, Path]]
243
+ ) -> None:
244
+ """get_all_repo_status should use asyncio.gather for parallelism."""
245
+ with patch(
246
+ "pennyfarthing_scripts.git.status_all.get_repo_status",
247
+ new_callable=AsyncMock,
248
+ ) as mock_get_status:
249
+ mock_get_status.return_value = RepoStatus(
250
+ name="test",
251
+ path=Path("/test"),
252
+ branch="main",
253
+ changes=[],
254
+ unpushed_commits=[],
255
+ )
256
+
257
+ await get_all_repo_status(sample_repos)
258
+
259
+ # Should be called once per repo
260
+ assert mock_get_status.call_count == len(sample_repos)
261
+
262
+ @pytest.mark.asyncio
263
+ async def test_handles_empty_repos_list(self) -> None:
264
+ """get_all_repo_status should handle empty repos list."""
265
+ results = await get_all_repo_status([])
266
+
267
+ assert results == []
268
+
269
+ @pytest.mark.asyncio
270
+ async def test_continues_on_individual_errors(
271
+ self, sample_repos: list[tuple[str, Path]]
272
+ ) -> None:
273
+ """get_all_repo_status should continue even if one repo has errors."""
274
+ # Even if first repo fails, second should still be processed
275
+ results = await get_all_repo_status(sample_repos)
276
+
277
+ assert len(results) == len(sample_repos)
278
+
279
+
280
+ # =============================================================================
281
+ # AC5: Output formatting
282
+ # =============================================================================
283
+
284
+
285
+ class TestFormatStatusBrief:
286
+ """Tests for format_status_brief function."""
287
+
288
+ def test_formats_clean_repo(self, clean_repo_status: RepoStatus) -> None:
289
+ """format_status_brief should show checkmark for clean repos."""
290
+ output = format_status_brief([clean_repo_status])
291
+
292
+ assert clean_repo_status.name in output
293
+ assert clean_repo_status.branch in output
294
+
295
+ def test_formats_dirty_repo(self, dirty_repo_status: RepoStatus) -> None:
296
+ """format_status_brief should show M indicator for dirty repos."""
297
+ output = format_status_brief([dirty_repo_status])
298
+
299
+ assert dirty_repo_status.name in output
300
+ assert "M" in output # Modified indicator
301
+
302
+ def test_formats_unpushed_repo(self, repo_with_unpushed: RepoStatus) -> None:
303
+ """format_status_brief should show unpushed count."""
304
+ output = format_status_brief([repo_with_unpushed])
305
+
306
+ assert repo_with_unpushed.name in output
307
+ assert "2" in output # 2 unpushed commits
308
+
309
+ def test_one_line_per_repo(
310
+ self, clean_repo_status: RepoStatus, dirty_repo_status: RepoStatus
311
+ ) -> None:
312
+ """format_status_brief should output one line per repo."""
313
+ output = format_status_brief([clean_repo_status, dirty_repo_status])
314
+ lines = [line for line in output.strip().split("\n") if line]
315
+
316
+ assert len(lines) == 2
317
+
318
+
319
+ class TestFormatStatusFull:
320
+ """Tests for format_status_full function."""
321
+
322
+ def test_includes_repo_header(self, clean_repo_status: RepoStatus) -> None:
323
+ """format_status_full should include repo name header."""
324
+ output = format_status_full([clean_repo_status])
325
+
326
+ assert clean_repo_status.name in output
327
+
328
+ def test_includes_branch_name(self, clean_repo_status: RepoStatus) -> None:
329
+ """format_status_full should include branch name."""
330
+ output = format_status_full([clean_repo_status])
331
+
332
+ assert clean_repo_status.branch in output
333
+
334
+ def test_lists_changes(self, dirty_repo_status: RepoStatus) -> None:
335
+ """format_status_full should list file changes."""
336
+ output = format_status_full([dirty_repo_status])
337
+
338
+ # Should include at least one change
339
+ assert "file1.py" in output or "Changes" in output
340
+
341
+ def test_truncates_many_changes(self) -> None:
342
+ """format_status_full should truncate if more than 10 changes."""
343
+ many_changes = RepoStatus(
344
+ name="many-changes",
345
+ path=Path("/test"),
346
+ branch="main",
347
+ changes=[f" M file{i}.py" for i in range(15)],
348
+ unpushed_commits=[],
349
+ )
350
+
351
+ output = format_status_full([many_changes])
352
+
353
+ assert "more" in output.lower() or "..." in output
354
+
355
+ def test_lists_unpushed_commits(self, repo_with_unpushed: RepoStatus) -> None:
356
+ """format_status_full should list unpushed commits."""
357
+ output = format_status_full([repo_with_unpushed])
358
+
359
+ assert "Unpushed" in output or "unpushed" in output
360
+
361
+
362
+ class TestFormatSummary:
363
+ """Tests for format_summary function."""
364
+
365
+ def test_shows_all_clean(self, clean_repo_status: RepoStatus) -> None:
366
+ """format_summary should indicate when all repos are clean."""
367
+ output = format_summary([clean_repo_status])
368
+
369
+ assert "clean" in output.lower() or "pushed" in output.lower()
370
+
371
+ def test_shows_change_count(
372
+ self, dirty_repo_status: RepoStatus, clean_repo_status: RepoStatus
373
+ ) -> None:
374
+ """format_summary should show total uncommitted changes."""
375
+ output = format_summary([dirty_repo_status, clean_repo_status])
376
+
377
+ # dirty_repo_status has 2 changes
378
+ assert "2" in output or "change" in output.lower()
379
+
380
+ def test_shows_unpushed_count(self, repo_with_unpushed: RepoStatus) -> None:
381
+ """format_summary should show total unpushed commits."""
382
+ output = format_summary([repo_with_unpushed])
383
+
384
+ # repo_with_unpushed has 2 unpushed commits
385
+ assert "2" in output or "unpushed" in output.lower()
386
+
387
+
388
+ # =============================================================================
389
+ # AC2: create-feature-branches functionality
390
+ # =============================================================================
391
+
392
+
393
+ class TestBranchResult:
394
+ """Tests for BranchResult dataclass."""
395
+
396
+ def test_created_action(self) -> None:
397
+ """BranchResult should represent created branch."""
398
+ result = BranchResult(
399
+ name="test-repo",
400
+ path=Path("/test"),
401
+ branch="feature/new",
402
+ action=BranchAction.CREATED,
403
+ current_branch="feature/new",
404
+ )
405
+
406
+ assert result.action == BranchAction.CREATED
407
+
408
+ def test_error_action(self) -> None:
409
+ """BranchResult should represent error with message."""
410
+ result = BranchResult(
411
+ name="test-repo",
412
+ path=Path("/test"),
413
+ branch="feature/new",
414
+ action=BranchAction.ERROR,
415
+ error="Failed to create branch",
416
+ )
417
+
418
+ assert result.action == BranchAction.ERROR
419
+ assert result.error is not None
420
+
421
+
422
+ class TestCreateOrCheckoutBranch:
423
+ """Tests for create_or_checkout_branch function."""
424
+
425
+ @pytest.mark.asyncio
426
+ async def test_returns_branch_result(self, temp_git_repo: Path) -> None:
427
+ """create_or_checkout_branch should return a BranchResult."""
428
+ result = await create_or_checkout_branch(
429
+ "test-repo", temp_git_repo, "feature/test"
430
+ )
431
+
432
+ assert isinstance(result, BranchResult)
433
+
434
+ @pytest.mark.asyncio
435
+ async def test_creates_new_branch_from_develop(self, temp_git_repo: Path) -> None:
436
+ """create_or_checkout_branch should create new branch from develop."""
437
+ result = await create_or_checkout_branch(
438
+ "test-repo", temp_git_repo, "feature/new-branch"
439
+ )
440
+
441
+ # Should create branch (no remote in test repo, so creates locally)
442
+ assert result.action in (BranchAction.CREATED, BranchAction.ERROR)
443
+ if result.action == BranchAction.CREATED:
444
+ assert result.current_branch == "feature/new-branch"
445
+
446
+ @pytest.mark.asyncio
447
+ async def test_checks_out_existing_local_branch(self, temp_git_repo: Path) -> None:
448
+ """create_or_checkout_branch should checkout existing local branch."""
449
+ import subprocess
450
+
451
+ # Create the branch first
452
+ subprocess.run(
453
+ ["git", "checkout", "-b", "existing-branch"],
454
+ cwd=temp_git_repo,
455
+ capture_output=True,
456
+ )
457
+ subprocess.run(
458
+ ["git", "checkout", "main"],
459
+ cwd=temp_git_repo,
460
+ capture_output=True,
461
+ )
462
+
463
+ result = await create_or_checkout_branch(
464
+ "test-repo", temp_git_repo, "existing-branch"
465
+ )
466
+
467
+ assert result.action in (
468
+ BranchAction.CHECKED_OUT_LOCAL,
469
+ BranchAction.CREATED,
470
+ BranchAction.ERROR, # May fail if no remote
471
+ )
472
+
473
+ @pytest.mark.asyncio
474
+ async def test_tracks_remote_branch(self, temp_git_repo: Path) -> None:
475
+ """create_or_checkout_branch should track remote if branch exists there."""
476
+ result = await create_or_checkout_branch(
477
+ "test-repo", temp_git_repo, "remote-branch"
478
+ )
479
+
480
+ # In test repo without remote, should either create or error
481
+ assert result.action in (
482
+ BranchAction.CHECKED_OUT_REMOTE,
483
+ BranchAction.CREATED,
484
+ BranchAction.ERROR, # May fail if no remote to fetch from
485
+ )
486
+
487
+ @pytest.mark.asyncio
488
+ async def test_handles_missing_directory(self) -> None:
489
+ """create_or_checkout_branch should handle missing directory."""
490
+ result = await create_or_checkout_branch(
491
+ "missing-repo", Path("/nonexistent"), "feature/test"
492
+ )
493
+
494
+ assert result.action == BranchAction.SKIPPED
495
+
496
+ @pytest.mark.asyncio
497
+ async def test_fetches_before_branching(self, temp_git_repo: Path) -> None:
498
+ """create_or_checkout_branch should fetch from origin before creating."""
499
+ result = await create_or_checkout_branch(
500
+ "test-repo", temp_git_repo, "feature/test"
501
+ )
502
+
503
+ # Should not error (fetch happens internally)
504
+ assert result.action != BranchAction.ERROR or "fetch" not in (
505
+ result.error or ""
506
+ ).lower()
507
+
508
+ @pytest.mark.asyncio
509
+ async def test_includes_commit_info(self, temp_git_repo: Path) -> None:
510
+ """create_or_checkout_branch should include latest commit info."""
511
+ result = await create_or_checkout_branch(
512
+ "test-repo", temp_git_repo, "feature/test"
513
+ )
514
+
515
+ # commit_info should be populated on success
516
+ if result.action != BranchAction.SKIPPED:
517
+ assert result.commit_info is not None or result.error is not None
518
+
519
+
520
+ class TestCreateFeatureBranches:
521
+ """Tests for create_feature_branches function."""
522
+
523
+ @pytest.mark.asyncio
524
+ async def test_returns_list_of_results(
525
+ self, sample_repos: list[tuple[str, Path]]
526
+ ) -> None:
527
+ """create_feature_branches should return list of BranchResult objects."""
528
+ results = await create_feature_branches(sample_repos, "feature/test")
529
+
530
+ assert isinstance(results, list)
531
+ assert len(results) == len(sample_repos)
532
+ assert all(isinstance(r, BranchResult) for r in results)
533
+
534
+ @pytest.mark.asyncio
535
+ async def test_uses_asyncio_gather(
536
+ self, sample_repos: list[tuple[str, Path]]
537
+ ) -> None:
538
+ """create_feature_branches should use asyncio.gather for parallelism."""
539
+ with patch(
540
+ "pennyfarthing_scripts.git.create_branches.create_or_checkout_branch",
541
+ new_callable=AsyncMock,
542
+ ) as mock_create:
543
+ mock_create.return_value = BranchResult(
544
+ name="test",
545
+ path=Path("/test"),
546
+ branch="feature/test",
547
+ action=BranchAction.CREATED,
548
+ )
549
+
550
+ await create_feature_branches(sample_repos, "feature/test")
551
+
552
+ assert mock_create.call_count == len(sample_repos)
553
+
554
+ @pytest.mark.asyncio
555
+ async def test_preserves_order(
556
+ self, sample_repos: list[tuple[str, Path]]
557
+ ) -> None:
558
+ """create_feature_branches should preserve input order."""
559
+ results = await create_feature_branches(sample_repos, "feature/test")
560
+
561
+ for i, (name, _) in enumerate(sample_repos):
562
+ assert results[i].name == name
563
+
564
+
565
+ # =============================================================================
566
+ # Worktree Detection
567
+ # =============================================================================
568
+
569
+
570
+ class TestDetectWorktree:
571
+ """Tests for detect_worktree function."""
572
+
573
+ def test_detects_main_checkout(self, tmp_path: Path) -> None:
574
+ """detect_worktree should detect main checkout (not worktree)."""
575
+ is_worktree, name, base_path = detect_worktree(tmp_path)
576
+
577
+ assert is_worktree is False
578
+ assert name is None
579
+
580
+ def test_detects_worktree(self, tmp_path: Path) -> None:
581
+ """detect_worktree should detect worktree from path."""
582
+ worktree_path = tmp_path / "worktrees" / "my-feature"
583
+ worktree_path.mkdir(parents=True)
584
+
585
+ is_worktree, name, base_path = detect_worktree(worktree_path)
586
+
587
+ assert is_worktree is True
588
+ assert name == "my-feature"
589
+
590
+
591
+ # =============================================================================
592
+ # Repo Filtering
593
+ # =============================================================================
594
+
595
+
596
+ class TestFilterRepos:
597
+ """Tests for filter_repos function."""
598
+
599
+ def test_filter_all_returns_all(
600
+ self, sample_repos: list[tuple[str, Path]]
601
+ ) -> None:
602
+ """filter_repos with 'all' should return all repos."""
603
+ result = filter_repos(sample_repos, "all")
604
+
605
+ assert len(result) == len(sample_repos)
606
+
607
+ def test_filter_api_returns_api_only(self) -> None:
608
+ """filter_repos with 'api' should return only API repos."""
609
+ repos = [
610
+ ("Pennyfarthing-api", Path("/api")),
611
+ ("Pennyfarthing-ui", Path("/ui")),
612
+ ]
613
+
614
+ result = filter_repos(repos, "api")
615
+
616
+ assert len(result) == 1
617
+ assert result[0][0] == "Pennyfarthing-api"
618
+
619
+ def test_filter_ui_returns_ui_only(self) -> None:
620
+ """filter_repos with 'ui' should return only UI repos."""
621
+ repos = [
622
+ ("Pennyfarthing-api", Path("/api")),
623
+ ("Pennyfarthing-ui", Path("/ui")),
624
+ ]
625
+
626
+ result = filter_repos(repos, "ui")
627
+
628
+ assert len(result) == 1
629
+ assert result[0][0] == "Pennyfarthing-ui"
630
+
631
+
632
+ # =============================================================================
633
+ # AC4: Error handling
634
+ # =============================================================================
635
+
636
+
637
+ class TestErrorHandling:
638
+ """Tests for error handling across git utilities."""
639
+
640
+ @pytest.mark.asyncio
641
+ async def test_status_handles_git_command_failure(self) -> None:
642
+ """get_repo_status should handle git command failures gracefully."""
643
+ result = await get_repo_status("test", Path("/nonexistent"))
644
+
645
+ assert result.error is not None
646
+ assert isinstance(result.error, str)
647
+
648
+ @pytest.mark.asyncio
649
+ async def test_branch_handles_git_command_failure(self) -> None:
650
+ """create_or_checkout_branch should handle git command failures."""
651
+ result = await create_or_checkout_branch(
652
+ "test", Path("/nonexistent"), "feature/test"
653
+ )
654
+
655
+ assert result.action in (BranchAction.SKIPPED, BranchAction.ERROR)
656
+
657
+ @pytest.mark.asyncio
658
+ async def test_status_all_handles_partial_failures(self) -> None:
659
+ """get_all_repo_status should return results even with partial failures."""
660
+ repos = [
661
+ ("good-repo", Path(".")), # Current dir should work
662
+ ("bad-repo", Path("/nonexistent")),
663
+ ]
664
+
665
+ results = await get_all_repo_status(repos)
666
+
667
+ assert len(results) == 2
668
+ # One should have error, one should not
669
+
670
+ @pytest.mark.asyncio
671
+ async def test_branches_handles_partial_failures(self) -> None:
672
+ """create_feature_branches should return results even with partial failures."""
673
+ repos = [
674
+ ("good-repo", Path(".")),
675
+ ("bad-repo", Path("/nonexistent")),
676
+ ]
677
+
678
+ results = await create_feature_branches(repos, "feature/test")
679
+
680
+ assert len(results) == 2
681
+
682
+
683
+ # =============================================================================
684
+ # AC5: Output formatting - Results
685
+ # =============================================================================
686
+
687
+
688
+ class TestFormatResults:
689
+ """Tests for format_results function."""
690
+
691
+ def test_includes_branch_name(self) -> None:
692
+ """format_results should include the target branch name."""
693
+ results = [
694
+ BranchResult(
695
+ name="test-repo",
696
+ path=Path("/test"),
697
+ branch="feature/test",
698
+ action=BranchAction.CREATED,
699
+ current_branch="feature/test",
700
+ )
701
+ ]
702
+
703
+ output = format_results(results, "feature/test")
704
+
705
+ assert "feature/test" in output
706
+
707
+ def test_shows_action_taken(self) -> None:
708
+ """format_results should show what action was taken."""
709
+ results = [
710
+ BranchResult(
711
+ name="test-repo",
712
+ path=Path("/test"),
713
+ branch="feature/test",
714
+ action=BranchAction.CREATED,
715
+ current_branch="feature/test",
716
+ )
717
+ ]
718
+
719
+ output = format_results(results, "feature/test")
720
+
721
+ assert "created" in output.lower() or "Created" in output
722
+
723
+ def test_shows_verification_summary(self) -> None:
724
+ """format_results should include verification summary."""
725
+ results = [
726
+ BranchResult(
727
+ name="repo-a",
728
+ path=Path("/a"),
729
+ branch="feature/test",
730
+ action=BranchAction.CREATED,
731
+ current_branch="feature/test",
732
+ ),
733
+ BranchResult(
734
+ name="repo-b",
735
+ path=Path("/b"),
736
+ branch="feature/test",
737
+ action=BranchAction.CHECKED_OUT_LOCAL,
738
+ current_branch="feature/test",
739
+ ),
740
+ ]
741
+
742
+ output = format_results(results, "feature/test")
743
+
744
+ # Should have some kind of summary/verification section
745
+ assert "Verification" in output or "Summary" in output or "Done" in output
746
+
747
+ def test_shows_errors(self) -> None:
748
+ """format_results should show errors clearly."""
749
+ results = [
750
+ BranchResult(
751
+ name="bad-repo",
752
+ path=Path("/bad"),
753
+ branch="feature/test",
754
+ action=BranchAction.ERROR,
755
+ error="Git command failed",
756
+ )
757
+ ]
758
+
759
+ output = format_results(results, "feature/test")
760
+
761
+ assert "error" in output.lower() or "Error" in output
762
+
763
+
764
+ # =============================================================================
765
+ # AC6: Cross-platform compatibility
766
+ # =============================================================================
767
+
768
+
769
+ class TestCrossPlatformCompatibility:
770
+ """Tests for cross-platform compatibility."""
771
+
772
+ def test_paths_use_pathlib(self) -> None:
773
+ """All path handling should use pathlib.Path."""
774
+ # RepoStatus uses Path
775
+ status = RepoStatus(
776
+ name="test",
777
+ path=Path("/test"),
778
+ branch="main",
779
+ changes=[],
780
+ unpushed_commits=[],
781
+ )
782
+ assert isinstance(status.path, Path)
783
+
784
+ # BranchResult uses Path
785
+ result = BranchResult(
786
+ name="test",
787
+ path=Path("/test"),
788
+ branch="main",
789
+ action=BranchAction.CREATED,
790
+ )
791
+ assert isinstance(result.path, Path)
792
+
793
+ def test_no_shell_specific_commands(self) -> None:
794
+ """Implementation should not use shell-specific commands."""
795
+ # This is more of a code review check, but we can verify the modules exist
796
+ import pennyfarthing_scripts.git.status_all as status_mod
797
+ import pennyfarthing_scripts.git.create_branches as branch_mod
798
+
799
+ # Modules should exist and be importable
800
+ assert status_mod is not None
801
+ assert branch_mod is not None
802
+
803
+
804
+ # =============================================================================
805
+ # AC7/AC8: Performance (asyncio.gather verification)
806
+ # =============================================================================
807
+
808
+
809
+ class TestAsyncPerformance:
810
+ """Tests verifying async parallel execution."""
811
+
812
+ @pytest.mark.asyncio
813
+ async def test_status_runs_in_parallel(self) -> None:
814
+ """get_all_repo_status should run git commands in parallel."""
815
+ # Create mock that tracks call timing
816
+ call_times = []
817
+
818
+ async def mock_get_status(name: str, path: Path) -> RepoStatus:
819
+ import time
820
+
821
+ call_times.append(time.time())
822
+ await asyncio.sleep(0.01) # Small delay
823
+ return RepoStatus(
824
+ name=name, path=path, branch="main", changes=[], unpushed_commits=[]
825
+ )
826
+
827
+ repos = [(f"repo-{i}", Path(f"/repo-{i}")) for i in range(3)]
828
+
829
+ with patch(
830
+ "pennyfarthing_scripts.git.status_all.get_repo_status",
831
+ side_effect=mock_get_status,
832
+ ):
833
+ await get_all_repo_status(repos)
834
+
835
+ # All calls should happen nearly simultaneously (within 0.05s of each other)
836
+ if len(call_times) >= 2:
837
+ time_spread = max(call_times) - min(call_times)
838
+ assert time_spread < 0.05 # Should be nearly simultaneous
839
+
840
+ @pytest.mark.asyncio
841
+ async def test_branch_creation_runs_in_parallel(self) -> None:
842
+ """create_feature_branches should run git commands in parallel."""
843
+ call_times = []
844
+
845
+ async def mock_create_branch(
846
+ name: str, path: Path, branch: str
847
+ ) -> BranchResult:
848
+ import time
849
+
850
+ call_times.append(time.time())
851
+ await asyncio.sleep(0.01)
852
+ return BranchResult(
853
+ name=name, path=path, branch=branch, action=BranchAction.CREATED
854
+ )
855
+
856
+ repos = [(f"repo-{i}", Path(f"/repo-{i}")) for i in range(3)]
857
+
858
+ with patch(
859
+ "pennyfarthing_scripts.git.create_branches.create_or_checkout_branch",
860
+ side_effect=mock_create_branch,
861
+ ):
862
+ await create_feature_branches(repos, "feature/test")
863
+
864
+ if len(call_times) >= 2:
865
+ time_spread = max(call_times) - min(call_times)
866
+ assert time_spread < 0.05