@pennyfarthing/core 10.0.5 → 10.1.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 (303) hide show
  1. package/README.md +9 -7
  2. package/package.json +15 -11
  3. package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
  4. package/packages/core/dist/cli/commands/doctor.js +251 -4
  5. package/packages/core/dist/cli/commands/doctor.js.map +1 -1
  6. package/packages/core/dist/cli/commands/init.d.ts +7 -0
  7. package/packages/core/dist/cli/commands/init.d.ts.map +1 -1
  8. package/packages/core/dist/cli/commands/init.js +41 -8
  9. package/packages/core/dist/cli/commands/init.js.map +1 -1
  10. package/packages/core/dist/cli/commands/update.d.ts.map +1 -1
  11. package/packages/core/dist/cli/commands/update.js +26 -0
  12. package/packages/core/dist/cli/commands/update.js.map +1 -1
  13. package/packages/core/dist/cli/index.js +1 -1
  14. package/packages/core/dist/cli/index.js.map +1 -1
  15. package/packages/core/dist/cli/utils/python.d.ts +22 -0
  16. package/packages/core/dist/cli/utils/python.d.ts.map +1 -0
  17. package/packages/core/dist/cli/utils/python.js +102 -0
  18. package/packages/core/dist/cli/utils/python.js.map +1 -0
  19. package/packages/core/dist/cli/utils/settings.d.ts.map +1 -1
  20. package/packages/core/dist/cli/utils/settings.js +10 -0
  21. package/packages/core/dist/cli/utils/settings.js.map +1 -1
  22. package/pennyfarthing-dist/agents/README.md +1 -3
  23. package/pennyfarthing-dist/agents/architect.md +0 -6
  24. package/pennyfarthing-dist/agents/devops.md +0 -6
  25. package/pennyfarthing-dist/agents/orchestrator.md +0 -6
  26. package/pennyfarthing-dist/agents/pm.md +0 -6
  27. package/pennyfarthing-dist/agents/sm.md +0 -6
  28. package/pennyfarthing-dist/commands/architect.md +11 -3
  29. package/pennyfarthing-dist/commands/close-epic.md +24 -131
  30. package/pennyfarthing-dist/commands/create-theme.md +14 -24
  31. package/pennyfarthing-dist/commands/dev.md +11 -3
  32. package/pennyfarthing-dist/commands/devops.md +11 -3
  33. package/pennyfarthing-dist/commands/health-check.md +1 -3
  34. package/pennyfarthing-dist/commands/help.md +8 -12
  35. package/pennyfarthing-dist/commands/list-themes.md +14 -16
  36. package/pennyfarthing-dist/commands/orchestrator.md +11 -3
  37. package/pennyfarthing-dist/commands/parallel-work.md +1 -3
  38. package/pennyfarthing-dist/commands/pm.md +11 -3
  39. package/pennyfarthing-dist/commands/prime.md +6 -6
  40. package/pennyfarthing-dist/commands/reviewer.md +11 -3
  41. package/pennyfarthing-dist/commands/run-ci.md +1 -1
  42. package/pennyfarthing-dist/commands/set-theme.md +14 -51
  43. package/pennyfarthing-dist/commands/setup.md +1 -1
  44. package/pennyfarthing-dist/commands/show-theme.md +14 -16
  45. package/pennyfarthing-dist/commands/sm.md +11 -3
  46. package/pennyfarthing-dist/commands/tea.md +11 -3
  47. package/pennyfarthing-dist/commands/tech-writer.md +11 -3
  48. package/pennyfarthing-dist/commands/theme-maker.md +14 -671
  49. package/pennyfarthing-dist/commands/theme.md +95 -0
  50. package/pennyfarthing-dist/commands/ux-designer.md +11 -3
  51. package/pennyfarthing-dist/commands/work.md +3 -5
  52. package/pennyfarthing-dist/guides/agent-coordination.md +11 -13
  53. package/pennyfarthing-dist/guides/agent-template-tactical.md +2 -3
  54. package/pennyfarthing-dist/guides/command-tag-taxonomy.md +212 -0
  55. package/pennyfarthing-dist/guides/hooks.md +5 -5
  56. package/pennyfarthing-dist/guides/patterns/fan-out-fan-in-pattern.md +3 -3
  57. package/pennyfarthing-dist/guides/patterns/helper-delegation-pattern.md +9 -59
  58. package/pennyfarthing-dist/guides/patterns/tdd-flow-pattern.md +4 -5
  59. package/pennyfarthing-dist/guides/prime.md +2 -2
  60. package/pennyfarthing-dist/guides/skill-schema.md +4 -4
  61. package/pennyfarthing-dist/scripts/core/agent-session.sh +6 -2
  62. package/pennyfarthing-dist/scripts/core/check-context.sh +0 -0
  63. package/pennyfarthing-dist/scripts/core/handoff-marker.sh +0 -0
  64. package/pennyfarthing-dist/scripts/core/phase-check-start.sh +0 -0
  65. package/pennyfarthing-dist/scripts/core/prime.sh +8 -10
  66. package/pennyfarthing-dist/scripts/cyclist/is-cyclist.sh +0 -0
  67. package/pennyfarthing-dist/scripts/git/create-feature-branches.sh +0 -0
  68. package/pennyfarthing-dist/scripts/git/git-status-all.sh +0 -0
  69. package/pennyfarthing-dist/scripts/git/install-git-hooks.sh +8 -6
  70. package/pennyfarthing-dist/scripts/git/release.sh +0 -0
  71. package/pennyfarthing-dist/scripts/git/worktree-manager.sh +0 -0
  72. package/pennyfarthing-dist/scripts/health/drift-detection.sh +0 -0
  73. package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +0 -0
  74. package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +0 -0
  75. package/pennyfarthing-dist/scripts/hooks/context-warning.sh +0 -0
  76. package/pennyfarthing-dist/scripts/hooks/cyclist-pretooluse-hook.sh +0 -0
  77. package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +0 -0
  78. package/pennyfarthing-dist/scripts/hooks/post-merge.sh +12 -5
  79. package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +4 -3
  80. package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +0 -0
  81. package/pennyfarthing-dist/scripts/hooks/pre-push.sh +11 -5
  82. package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +0 -0
  83. package/pennyfarthing-dist/scripts/hooks/question_reflector_check.py +0 -0
  84. package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +0 -0
  85. package/pennyfarthing-dist/scripts/hooks/session-start.sh +0 -0
  86. package/pennyfarthing-dist/scripts/hooks/session-stop.sh +0 -0
  87. package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +0 -0
  88. package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +0 -0
  89. package/pennyfarthing-dist/scripts/jira/create-jira-epic.sh +0 -0
  90. package/pennyfarthing-dist/scripts/jira/create-jira-story.sh +0 -0
  91. package/pennyfarthing-dist/scripts/jira/jira-claim-story.sh +0 -0
  92. package/pennyfarthing-dist/scripts/jira/jira-reconcile.sh +0 -0
  93. package/pennyfarthing-dist/scripts/jira/jira-sync-story.sh +0 -0
  94. package/pennyfarthing-dist/scripts/jira/sync-epic-jira.sh +0 -0
  95. package/pennyfarthing-dist/scripts/lib/background-tasks.sh +0 -0
  96. package/pennyfarthing-dist/scripts/lib/checkpoint.sh +0 -0
  97. package/pennyfarthing-dist/scripts/lib/common.sh +0 -0
  98. package/pennyfarthing-dist/scripts/lib/file-lock.sh +0 -0
  99. package/pennyfarthing-dist/scripts/lib/logging.sh +0 -0
  100. package/pennyfarthing-dist/scripts/lib/retry.sh +0 -0
  101. package/pennyfarthing-dist/scripts/maintenance/migrate-theme-schema.mjs +0 -0
  102. package/pennyfarthing-dist/scripts/maintenance/sidecar-health.sh +0 -0
  103. package/pennyfarthing-dist/scripts/misc/README.md +1 -1
  104. package/pennyfarthing-dist/scripts/misc/add-short-names.sh +0 -0
  105. package/pennyfarthing-dist/scripts/misc/add_short_names.py +0 -0
  106. package/pennyfarthing-dist/scripts/misc/backlog.sh +0 -0
  107. package/pennyfarthing-dist/scripts/misc/check-status.sh +0 -0
  108. package/pennyfarthing-dist/scripts/misc/find-related-work.sh +0 -0
  109. package/pennyfarthing-dist/scripts/misc/generate-skill-docs.sh +0 -0
  110. package/pennyfarthing-dist/scripts/misc/log-skill-usage.sh +0 -0
  111. package/pennyfarthing-dist/scripts/misc/migrate-bmad-workflow.sh +0 -0
  112. package/pennyfarthing-dist/scripts/misc/migrate_bmad_workflow.py +0 -0
  113. package/pennyfarthing-dist/scripts/misc/repo-scan.sh +0 -0
  114. package/pennyfarthing-dist/scripts/misc/repo-utils.sh +0 -0
  115. package/pennyfarthing-dist/scripts/misc/run-ci.sh +0 -0
  116. package/pennyfarthing-dist/scripts/misc/run-timestamp.sh +0 -0
  117. package/pennyfarthing-dist/scripts/misc/session-cleanup.sh +0 -0
  118. package/pennyfarthing-dist/scripts/misc/skill-usage-report.sh +0 -0
  119. package/pennyfarthing-dist/scripts/misc/statusline.sh +0 -0
  120. package/pennyfarthing-dist/scripts/misc/uninstall.sh +0 -0
  121. package/pennyfarthing-dist/scripts/misc/validate-subagent-frontmatter.sh +1 -2
  122. package/pennyfarthing-dist/scripts/portraits/generate-portraits.sh +0 -0
  123. package/pennyfarthing-dist/scripts/story/create-story.sh +0 -0
  124. package/pennyfarthing-dist/scripts/story/size-story.sh +0 -0
  125. package/pennyfarthing-dist/scripts/story/story-template.sh +0 -0
  126. package/pennyfarthing-dist/scripts/test/ensure-swebench-data.sh +0 -0
  127. package/pennyfarthing-dist/scripts/test/swebench-judge.py +0 -0
  128. package/pennyfarthing-dist/scripts/test/test-cache.sh +0 -0
  129. package/pennyfarthing-dist/scripts/test/test-setup.sh +0 -0
  130. package/pennyfarthing-dist/scripts/tests/check.test.sh +0 -0
  131. package/pennyfarthing-dist/scripts/tests/dev-story-workflow-import.test.sh +0 -0
  132. package/pennyfarthing-dist/scripts/tests/epics-and-stories-workflow-import.test.sh +0 -0
  133. package/pennyfarthing-dist/scripts/tests/handoff-phase-update.test.sh +5 -5
  134. package/pennyfarthing-dist/scripts/tests/implementation-readiness-workflow-import.test.sh +0 -0
  135. package/pennyfarthing-dist/scripts/tests/migrate-bmad-workflow.test.sh +0 -0
  136. package/pennyfarthing-dist/scripts/tests/prd-workflow-import.test.sh +0 -0
  137. package/pennyfarthing-dist/scripts/tests/project-context-workflow-import.test.sh +0 -0
  138. package/pennyfarthing-dist/scripts/tests/test-character-voice.sh +0 -0
  139. package/pennyfarthing-dist/scripts/tests/test-drift-detection.sh +3 -79
  140. package/pennyfarthing-dist/scripts/tests/test-post-merge-hook.sh +0 -0
  141. package/pennyfarthing-dist/scripts/tests/test-session-checkpoint.sh +0 -0
  142. package/pennyfarthing-dist/scripts/tests/test-solo-command.sh +0 -0
  143. package/pennyfarthing-dist/scripts/tests/ux-design-workflow-import.test.sh +0 -0
  144. package/pennyfarthing-dist/scripts/theme/README.md +1 -1
  145. package/pennyfarthing-dist/scripts/theme/compute-theme-tiers.sh +0 -0
  146. package/pennyfarthing-dist/scripts/theme/compute_theme_tiers.py +0 -0
  147. package/pennyfarthing-dist/scripts/theme/list-themes.sh +0 -0
  148. package/pennyfarthing-dist/scripts/theme/update-theme-tiers.sh +0 -0
  149. package/pennyfarthing-dist/scripts/validation/validate-agent-schema.sh +0 -1
  150. package/pennyfarthing-dist/scripts/workflow/check.py +0 -0
  151. package/pennyfarthing-dist/scripts/workflow/check.sh +0 -0
  152. package/pennyfarthing-dist/scripts/workflow/complete-step.py +0 -0
  153. package/pennyfarthing-dist/scripts/workflow/finish-story.sh +62 -17
  154. package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +0 -0
  155. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.py +0 -0
  156. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.sh +0 -0
  157. package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +0 -0
  158. package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +0 -0
  159. package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +0 -0
  160. package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +0 -0
  161. package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +0 -0
  162. package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +0 -0
  163. package/pennyfarthing-dist/skills/dev-patterns/SKILL.md +2 -2
  164. package/pennyfarthing-dist/skills/skill-registry.yaml +20 -16
  165. package/pennyfarthing-dist/skills/story/scripts/create-story.sh +0 -0
  166. package/pennyfarthing-dist/skills/story/scripts/size-story.sh +0 -0
  167. package/pennyfarthing-dist/skills/story/scripts/story-template.sh +0 -0
  168. package/pennyfarthing-dist/skills/theme/skill.md +290 -75
  169. package/pennyfarthing-dist/skills/theme-creation/SKILL.md +23 -166
  170. package/pennyfarthing-dist/skills/workflow/scripts/list-workflows.sh +0 -0
  171. package/pennyfarthing-dist/skills/workflow/scripts/resume-workflow.sh +0 -0
  172. package/pennyfarthing-dist/skills/workflow/scripts/show-workflow.sh +0 -0
  173. package/pennyfarthing-dist/skills/workflow/scripts/start-workflow.sh +0 -0
  174. package/pennyfarthing-dist/skills/workflow/scripts/workflow-status.sh +0 -0
  175. package/pennyfarthing-dist/skills/workflow/skill.md +4 -4
  176. package/pennyfarthing-dist/templates/agent-scopes.yaml.template +0 -11
  177. package/pennyfarthing-dist/templates/auto-load-sm.sh.template +14 -0
  178. package/pennyfarthing-dist/templates/settings.local.json.template +9 -0
  179. package/pennyfarthing-dist/workflows/2party-tdd.yaml +399 -0
  180. package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-05-import-to-future.md +41 -24
  181. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  182. package/pennyfarthing_scripts/__pycache__/hooks.cpython-314.pyc +0 -0
  183. package/pennyfarthing_scripts/__pycache__/schema_validation_hook.cpython-314.pyc +0 -0
  184. package/pennyfarthing_scripts/__pycache__/workflow.cpython-314.pyc +0 -0
  185. package/pennyfarthing_scripts/cli.py +15 -0
  186. package/pennyfarthing_scripts/codemarkers/__init__.py +19 -0
  187. package/pennyfarthing_scripts/codemarkers/__main__.py +6 -0
  188. package/pennyfarthing_scripts/codemarkers/__pycache__/__init__.cpython-314.pyc +0 -0
  189. package/pennyfarthing_scripts/codemarkers/__pycache__/__main__.cpython-314.pyc +0 -0
  190. package/pennyfarthing_scripts/codemarkers/__pycache__/analyze.cpython-314.pyc +0 -0
  191. package/pennyfarthing_scripts/codemarkers/__pycache__/cli.cpython-314.pyc +0 -0
  192. package/pennyfarthing_scripts/codemarkers/__pycache__/formatters.cpython-314.pyc +0 -0
  193. package/pennyfarthing_scripts/codemarkers/__pycache__/models.cpython-314.pyc +0 -0
  194. package/pennyfarthing_scripts/codemarkers/analyze.py +326 -0
  195. package/pennyfarthing_scripts/codemarkers/cli.py +129 -0
  196. package/pennyfarthing_scripts/codemarkers/formatters.py +89 -0
  197. package/pennyfarthing_scripts/codemarkers/models.py +45 -0
  198. package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
  199. package/pennyfarthing_scripts/common/__pycache__/themes.cpython-314.pyc +0 -0
  200. package/pennyfarthing_scripts/complexity/__init__.py +15 -0
  201. package/pennyfarthing_scripts/complexity/__main__.py +6 -0
  202. package/pennyfarthing_scripts/complexity/__pycache__/__init__.cpython-314.pyc +0 -0
  203. package/pennyfarthing_scripts/complexity/__pycache__/__main__.cpython-314.pyc +0 -0
  204. package/pennyfarthing_scripts/complexity/__pycache__/analyze.cpython-314.pyc +0 -0
  205. package/pennyfarthing_scripts/complexity/__pycache__/cli.cpython-314.pyc +0 -0
  206. package/pennyfarthing_scripts/complexity/__pycache__/formatters.cpython-314.pyc +0 -0
  207. package/pennyfarthing_scripts/complexity/__pycache__/models.cpython-314.pyc +0 -0
  208. package/pennyfarthing_scripts/complexity/analyze.py +207 -0
  209. package/pennyfarthing_scripts/complexity/cli.py +78 -0
  210. package/pennyfarthing_scripts/complexity/formatters.py +64 -0
  211. package/pennyfarthing_scripts/complexity/models.py +32 -0
  212. package/pennyfarthing_scripts/deadcode/__init__.py +6 -0
  213. package/pennyfarthing_scripts/deadcode/__main__.py +6 -0
  214. package/pennyfarthing_scripts/deadcode/__pycache__/__init__.cpython-314.pyc +0 -0
  215. package/pennyfarthing_scripts/deadcode/__pycache__/__main__.cpython-314.pyc +0 -0
  216. package/pennyfarthing_scripts/deadcode/__pycache__/analyze.cpython-314.pyc +0 -0
  217. package/pennyfarthing_scripts/deadcode/__pycache__/cli.cpython-314.pyc +0 -0
  218. package/pennyfarthing_scripts/deadcode/__pycache__/formatters.cpython-314.pyc +0 -0
  219. package/pennyfarthing_scripts/deadcode/__pycache__/models.cpython-314.pyc +0 -0
  220. package/pennyfarthing_scripts/deadcode/analyze.py +323 -0
  221. package/pennyfarthing_scripts/deadcode/cli.py +163 -0
  222. package/pennyfarthing_scripts/deadcode/formatters.py +106 -0
  223. package/pennyfarthing_scripts/deadcode/models.py +54 -0
  224. package/pennyfarthing_scripts/dependencies/__init__.py +20 -0
  225. package/pennyfarthing_scripts/dependencies/__main__.py +5 -0
  226. package/pennyfarthing_scripts/dependencies/__pycache__/__init__.cpython-314.pyc +0 -0
  227. package/pennyfarthing_scripts/dependencies/__pycache__/__main__.cpython-314.pyc +0 -0
  228. package/pennyfarthing_scripts/dependencies/__pycache__/analyze.cpython-314.pyc +0 -0
  229. package/pennyfarthing_scripts/dependencies/__pycache__/cli.cpython-314.pyc +0 -0
  230. package/pennyfarthing_scripts/dependencies/__pycache__/formatters.cpython-314.pyc +0 -0
  231. package/pennyfarthing_scripts/dependencies/__pycache__/models.cpython-314.pyc +0 -0
  232. package/pennyfarthing_scripts/dependencies/analyze.py +155 -0
  233. package/pennyfarthing_scripts/dependencies/cli.py +72 -0
  234. package/pennyfarthing_scripts/dependencies/formatters.py +63 -0
  235. package/pennyfarthing_scripts/dependencies/models.py +39 -0
  236. package/pennyfarthing_scripts/healthscore/__init__.py +21 -0
  237. package/pennyfarthing_scripts/healthscore/__main__.py +6 -0
  238. package/pennyfarthing_scripts/healthscore/__pycache__/__init__.cpython-314.pyc +0 -0
  239. package/pennyfarthing_scripts/healthscore/__pycache__/__main__.cpython-314.pyc +0 -0
  240. package/pennyfarthing_scripts/healthscore/__pycache__/analyze.cpython-314.pyc +0 -0
  241. package/pennyfarthing_scripts/healthscore/__pycache__/cli.cpython-314.pyc +0 -0
  242. package/pennyfarthing_scripts/healthscore/__pycache__/formatters.cpython-314.pyc +0 -0
  243. package/pennyfarthing_scripts/healthscore/__pycache__/models.cpython-314.pyc +0 -0
  244. package/pennyfarthing_scripts/healthscore/analyze.py +161 -0
  245. package/pennyfarthing_scripts/healthscore/cli.py +76 -0
  246. package/pennyfarthing_scripts/healthscore/formatters.py +46 -0
  247. package/pennyfarthing_scripts/healthscore/models.py +44 -0
  248. package/pennyfarthing_scripts/hooks/cyclist-pretooluse-hook.sh +0 -0
  249. package/pennyfarthing_scripts/hotspots/__pycache__/__init__.cpython-314.pyc +0 -0
  250. package/pennyfarthing_scripts/hotspots/__pycache__/__main__.cpython-314.pyc +0 -0
  251. package/pennyfarthing_scripts/hotspots/__pycache__/analyze.cpython-314.pyc +0 -0
  252. package/pennyfarthing_scripts/hotspots/__pycache__/cli.cpython-314.pyc +0 -0
  253. package/pennyfarthing_scripts/hotspots/__pycache__/formatters.cpython-314.pyc +0 -0
  254. package/pennyfarthing_scripts/hotspots/__pycache__/models.cpython-314.pyc +0 -0
  255. package/pennyfarthing_scripts/hotspots/analyze.py +27 -0
  256. package/pennyfarthing_scripts/hotspots/cli.py +10 -8
  257. package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-314.pyc +0 -0
  258. package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
  259. package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
  260. package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
  261. package/pennyfarthing_scripts/jira/__pycache__/create.cpython-314.pyc +0 -0
  262. package/pennyfarthing_scripts/jira/__pycache__/operations.cpython-314.pyc +0 -0
  263. package/pennyfarthing_scripts/jira/__pycache__/reconcile.cpython-314.pyc +0 -0
  264. package/pennyfarthing_scripts/jira/bidirectional.py +42 -15
  265. package/pennyfarthing_scripts/jira/cli.py +4 -1
  266. package/pennyfarthing_scripts/jira/client.py +28 -0
  267. package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
  268. package/pennyfarthing_scripts/prime/__pycache__/models.cpython-314.pyc +0 -0
  269. package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
  270. package/pennyfarthing_scripts/prime/__pycache__/tiers.cpython-314.pyc +0 -0
  271. package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  272. package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
  273. package/pennyfarthing_scripts/sprint/__pycache__/archive_epic.cpython-314.pyc +0 -0
  274. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  275. package/pennyfarthing_scripts/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
  276. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  277. package/pennyfarthing_scripts/sprint/__pycache__/story_add.cpython-314.pyc +0 -0
  278. package/pennyfarthing_scripts/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
  279. package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
  280. package/pennyfarthing_scripts/sprint/__pycache__/validate_cmd.cpython-314.pyc +0 -0
  281. package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-314.pyc +0 -0
  282. package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-314.pyc +0 -0
  283. package/pennyfarthing_scripts/sprint/__pycache__/yaml_io.cpython-314.pyc +0 -0
  284. package/pennyfarthing_scripts/sprint/archive_epic.py +198 -94
  285. package/pennyfarthing_scripts/sprint/cli.py +29 -19
  286. package/pennyfarthing_scripts/sprint/story_add.py +202 -27
  287. package/pennyfarthing_scripts/sprint/story_finish.py +211 -0
  288. package/pennyfarthing_scripts/sprint/work.py +27 -3
  289. package/pennyfarthing_scripts/tests/__pycache__/test_codemarkers.cpython-314-pytest-9.0.2.pyc +0 -0
  290. package/pennyfarthing_scripts/tests/__pycache__/test_healthscore.cpython-314-pytest-9.0.2.pyc +0 -0
  291. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_package.cpython-314-pytest-9.0.2.pyc +0 -0
  292. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
  293. package/pennyfarthing_scripts/tests/__pycache__/test_validate_cmd.cpython-314-pytest-9.0.2.pyc +0 -0
  294. package/pennyfarthing_scripts/tests/__pycache__/test_yaml_io.cpython-314-pytest-9.0.2.pyc +0 -0
  295. package/pennyfarthing_scripts/tests/test_codemarkers.py +682 -0
  296. package/pennyfarthing_scripts/tests/test_healthscore.py +524 -0
  297. package/pennyfarthing_scripts/theme/__init__.py +5 -0
  298. package/pennyfarthing_scripts/theme/__main__.py +6 -0
  299. package/pennyfarthing_scripts/theme/__pycache__/__init__.cpython-314.pyc +0 -0
  300. package/pennyfarthing_scripts/theme/__pycache__/cli.cpython-314.pyc +0 -0
  301. package/pennyfarthing_scripts/theme/cli.py +286 -0
  302. package/scripts/README.md +53 -0
  303. package/pennyfarthing-dist/agents/workflow-status-check.md +0 -96
@@ -0,0 +1,44 @@
1
+ """
2
+ Data models for health score analysis results.
3
+
4
+ Follows ADR-0008 result pattern — structured dataclasses with success/error fields.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass, field
10
+
11
+
12
+ # Default weights for each dimension (must sum to 1.0)
13
+ DEFAULT_WEIGHTS: dict[str, float] = {
14
+ "churn": 0.15,
15
+ "todo_density": 0.15,
16
+ "complexity": 0.15,
17
+ "test_gaps": 0.15,
18
+ "dead_code": 0.10,
19
+ "deprecation_debt": 0.10,
20
+ "dependency_freshness": 0.10,
21
+ "agent_context_efficiency": 0.10,
22
+ }
23
+
24
+
25
+ @dataclass
26
+ class DimensionScore:
27
+ """Score for a single health dimension."""
28
+
29
+ name: str
30
+ score: float | None = None # 0-100, None if unavailable
31
+ weight: float = 0.0
32
+ error: str | None = None
33
+
34
+
35
+ @dataclass
36
+ class HealthscoreResult:
37
+ """Composite health score following ADR-0008 pattern."""
38
+
39
+ success: bool
40
+ composite_score: float = 0.0 # 0-100 weighted average
41
+ target_path: str = ""
42
+ dimensions: list[DimensionScore] = field(default_factory=list)
43
+ cached: bool = False
44
+ error: str | None = None
@@ -39,6 +39,26 @@ DEFAULT_EXCLUDES = [
39
39
  "*.map",
40
40
  "package-lock.json",
41
41
  "pnpm-lock.yaml",
42
+ # Dotfiles
43
+ ".*",
44
+ # Images
45
+ "*.png",
46
+ "*.jpg",
47
+ "*.jpeg",
48
+ "*.gif",
49
+ "*.svg",
50
+ "*.ico",
51
+ # Fonts
52
+ "*.woff",
53
+ "*.woff2",
54
+ "*.ttf",
55
+ "*.eot",
56
+ # Generated files
57
+ "*.d.ts",
58
+ "*.snap",
59
+ "*.d.ts.map",
60
+ # CI config
61
+ ".github/*",
42
62
  ]
43
63
 
44
64
  # Regex for identifying bug-fix commits
@@ -422,6 +442,7 @@ async def analyze_all_repos(
422
442
  days: int = 90,
423
443
  excludes: list[str] | None = None,
424
444
  branch: str = "--all",
445
+ skip_types: list[str] | None = None,
425
446
  ) -> MultiRepoHotspotResult:
426
447
  """Analyze all repos found under project root in parallel.
427
448
 
@@ -432,6 +453,7 @@ async def analyze_all_repos(
432
453
  days: Time window in days
433
454
  excludes: Additional file patterns to exclude
434
455
  branch: Branch spec
456
+ skip_types: Repo types to exclude (e.g. ["orchestrator"])
435
457
 
436
458
  Returns:
437
459
  MultiRepoHotspotResult with per-repo results
@@ -446,6 +468,11 @@ async def analyze_all_repos(
446
468
  # Extract repos from repos.yaml
447
469
  for repo_name, repo_config in repos_yaml.items():
448
470
  if isinstance(repo_config, dict):
471
+ # Filter by type if skip_types is provided
472
+ if skip_types:
473
+ repo_type = repo_config.get("type")
474
+ if repo_type and repo_type in skip_types:
475
+ continue
449
476
  repo_path = repo_config.get("path", repo_name)
450
477
  else:
451
478
  repo_path = str(repo_config)
@@ -38,15 +38,17 @@ def _common_options(fn):
38
38
  fn = click.option("--output", "output_file", type=click.Path(), help="Write output to file")(fn)
39
39
  fn = click.option("--exclude", multiple=True, help="Additional exclude patterns (repeatable)")(fn)
40
40
  fn = click.option("--branch", default="--all", show_default=True, help="Branch spec for git log")(fn)
41
+ fn = click.option("--skip-type", "skip_type", multiple=True, help="Skip repos by type (repeatable, e.g. --skip-type orchestrator)")(fn)
41
42
  return fn
42
43
 
43
44
 
44
- def _run_analysis(repo: str | None, repo_path: str | None, days: int, exclude: tuple, branch: str):
45
+ def _run_analysis(repo: str | None, repo_path: str | None, days: int, exclude: tuple, branch: str, skip_type: tuple = ()):
45
46
  """Run analysis and return result."""
46
47
  from pennyfarthing_scripts.hotspots.analyze import analyze_all_repos, analyze_repo
47
48
  from pennyfarthing_scripts.common.config import get_project_root
48
49
 
49
50
  excludes = list(exclude) if exclude else None
51
+ skip_types = list(skip_type) if skip_type else None
50
52
 
51
53
  if repo_path:
52
54
  # Standalone analysis of a specific path
@@ -75,7 +77,7 @@ def _run_analysis(repo: str | None, repo_path: str | None, days: int, exclude: t
75
77
  # All repos
76
78
  project_root = get_project_root()
77
79
  return asyncio.run(
78
- analyze_all_repos(project_root, days, excludes, branch)
80
+ analyze_all_repos(project_root, days, excludes, branch, skip_types)
79
81
  )
80
82
 
81
83
 
@@ -130,23 +132,23 @@ def _output_result(result, fmt: str, output_file: str | None, top: int, mode: st
130
132
 
131
133
  @hotspots.command()
132
134
  @_common_options
133
- def analyze(repo, repo_path, days, top, fmt, output_file, exclude, branch):
135
+ def analyze(repo, repo_path, days, top, fmt, output_file, exclude, branch, skip_type):
134
136
  """Full hotspot analysis — files and directories."""
135
- result = _run_analysis(repo, repo_path, days, exclude, branch)
137
+ result = _run_analysis(repo, repo_path, days, exclude, branch, skip_type)
136
138
  _output_result(result, fmt, output_file, top, "analyze")
137
139
 
138
140
 
139
141
  @hotspots.command()
140
142
  @_common_options
141
- def files(repo, repo_path, days, top, fmt, output_file, exclude, branch):
143
+ def files(repo, repo_path, days, top, fmt, output_file, exclude, branch, skip_type):
142
144
  """File-level hotspot report."""
143
- result = _run_analysis(repo, repo_path, days, exclude, branch)
145
+ result = _run_analysis(repo, repo_path, days, exclude, branch, skip_type)
144
146
  _output_result(result, fmt, output_file, top, "files")
145
147
 
146
148
 
147
149
  @hotspots.command()
148
150
  @_common_options
149
- def dirs(repo, repo_path, days, top, fmt, output_file, exclude, branch):
151
+ def dirs(repo, repo_path, days, top, fmt, output_file, exclude, branch, skip_type):
150
152
  """Directory-level hotspot report."""
151
- result = _run_analysis(repo, repo_path, days, exclude, branch)
153
+ result = _run_analysis(repo, repo_path, days, exclude, branch, skip_type)
152
154
  _output_result(result, fmt, output_file, top, "dirs")
@@ -43,7 +43,7 @@ class SyncChange:
43
43
  """Represents a single sync change to apply."""
44
44
 
45
45
  key: str
46
- field: Literal["status", "points"]
46
+ field: Literal["status", "points", "assigned_to"]
47
47
  action: Literal["update-yaml", "update-jira"]
48
48
  yaml_value: Any
49
49
  jira_value: Any
@@ -123,10 +123,15 @@ Examples:
123
123
  action="store_true",
124
124
  help="Sync story points",
125
125
  )
126
+ parser.add_argument(
127
+ "--assignee",
128
+ action="store_true",
129
+ help="Sync assignee field (Jira -> YAML only)",
130
+ )
126
131
  parser.add_argument(
127
132
  "--all",
128
133
  action="store_true",
129
- help="Sync all fields (status + points)",
134
+ help="Sync all fields (status + points + assignee)",
130
135
  )
131
136
  parser.add_argument(
132
137
  "--sprint",
@@ -137,10 +142,11 @@ Examples:
137
142
 
138
143
  args = parser.parse_args(argv)
139
144
 
140
- # --all implies both --status and --points
145
+ # --all implies --status, --points, and --assignee
141
146
  if args.all:
142
147
  args.status = True
143
148
  args.points = True
149
+ args.assignee = True
144
150
 
145
151
  return args
146
152
 
@@ -156,6 +162,7 @@ def generate_sync_plan(
156
162
  *,
157
163
  sync_status: bool = False,
158
164
  sync_points: bool = False,
165
+ sync_assignee: bool = False,
159
166
  yaml_wins: bool = False,
160
167
  ) -> SyncPlan:
161
168
  """Generate a sync plan comparing YAML and Jira stories.
@@ -165,6 +172,7 @@ def generate_sync_plan(
165
172
  jira_stories: Stories from Jira [{key, fields: {status: {name}, customfield_10031, ...}}]
166
173
  sync_status: Whether to sync status field
167
174
  sync_points: Whether to sync points field
175
+ sync_assignee: Whether to sync assignee field (always Jira -> YAML)
168
176
  yaml_wins: If True, YAML wins conflicts (default: Jira wins)
169
177
 
170
178
  Returns:
@@ -248,6 +256,26 @@ def generate_sync_plan(
248
256
  )
249
257
  )
250
258
 
259
+ # Check assignee differences (always Jira -> YAML, ignores yaml_wins)
260
+ if sync_assignee:
261
+ yaml_assignee = yaml_story.get("assigned_to")
262
+ jira_assignee_obj = jira_story.get("fields", {}).get("assignee")
263
+ jira_assignee_email = (
264
+ jira_assignee_obj.get("emailAddress") if jira_assignee_obj else None
265
+ )
266
+
267
+ if yaml_assignee != jira_assignee_email:
268
+ plan.changes.append(
269
+ SyncChange(
270
+ key=key,
271
+ field="assigned_to",
272
+ action="update-yaml",
273
+ yaml_value=yaml_assignee,
274
+ jira_value=jira_assignee_email,
275
+ target_value=jira_assignee_email,
276
+ )
277
+ )
278
+
251
279
  return plan
252
280
 
253
281
 
@@ -326,21 +354,17 @@ async def execute_sync_plan(
326
354
 
327
355
  # Execute YAML updates (sequential, file-based)
328
356
  if yaml_updates:
329
- # Load current sprint data
330
357
  from pennyfarthing_scripts.common.config import get_project_root
358
+ from pennyfarthing_scripts.sprint.yaml_io import read_sprint, write_sprint
331
359
 
332
360
  root = get_project_root()
333
361
  sprint_file = sprint_path or (root / "sprint" / "current-sprint.yaml")
334
362
 
335
363
  if sprint_file.exists():
336
- import yaml
337
-
338
- with open(sprint_file) as f:
339
- sprint_data = yaml.safe_load(f)
364
+ sprint_data = read_sprint(sprint_file)
340
365
 
341
366
  # Apply YAML updates
342
367
  for change in yaml_updates:
343
- # Find and update the story in sprint data
344
368
  updated = _update_story_in_sprint(
345
369
  sprint_data, change.key, change.field, change.target_value
346
370
  )
@@ -348,10 +372,9 @@ async def execute_sync_plan(
348
372
  result.changes_applied += 1
349
373
  result.yaml_modified = True
350
374
 
351
- # Write back if modified
375
+ # Write back if modified (handles shards automatically)
352
376
  if result.yaml_modified:
353
- with open(sprint_file, "w") as f:
354
- yaml.dump(sprint_data, f, default_flow_style=False, sort_keys=False)
377
+ write_sprint(sprint_file, sprint_data)
355
378
 
356
379
  return result
357
380
 
@@ -379,7 +402,10 @@ def _update_story_in_sprint(
379
402
  for epic in sprint_data.get("epics", []):
380
403
  for story in epic.get("stories", []):
381
404
  if story.get("jira") == jira_key:
382
- story[field] = value
405
+ if value is None and field in story:
406
+ del story[field]
407
+ elif value is not None:
408
+ story[field] = value
383
409
  return True
384
410
 
385
411
  return False
@@ -465,8 +491,8 @@ async def async_main(args: argparse.Namespace) -> int:
465
491
  Exit code (0 for success, 1 for error)
466
492
  """
467
493
  # Validate at least one field is selected
468
- if not args.status and not args.points:
469
- error("Must specify at least one field to sync: --status, --points, or --all")
494
+ if not args.status and not args.points and not args.assignee:
495
+ error("Must specify at least one field to sync: --status, --points, --assignee, or --all")
470
496
  return 1
471
497
 
472
498
  # Load sprint data
@@ -510,6 +536,7 @@ async def async_main(args: argparse.Namespace) -> int:
510
536
  jira_stories,
511
537
  sync_status=args.status,
512
538
  sync_points=args.points,
539
+ sync_assignee=args.assignee,
513
540
  yaml_wins=args.yaml_wins,
514
541
  )
515
542
 
@@ -274,9 +274,10 @@ def sync(epic, dry_run, transition, points, sync_all):
274
274
  @click.option("--yaml-wins", is_flag=True, help="Prefer YAML values on conflict")
275
275
  @click.option("--status", is_flag=True, help="Sync status field")
276
276
  @click.option("--points", is_flag=True, help="Sync story points")
277
+ @click.option("--assignee", is_flag=True, help="Sync assignee field (Jira -> YAML only)")
277
278
  @click.option("--all", "sync_all", is_flag=True, help="Sync all fields")
278
279
  @click.option("--sprint", "sprint_id", help="Target specific sprint")
279
- def bidirectional(dry_run, yaml_wins, status, points, sync_all, sprint_id):
280
+ def bidirectional(dry_run, yaml_wins, status, points, assignee, sync_all, sprint_id):
280
281
  """Bidirectional sync between YAML and Jira."""
281
282
  from pennyfarthing_scripts.jira.bidirectional import main as bidirectional_main
282
283
 
@@ -289,6 +290,8 @@ def bidirectional(dry_run, yaml_wins, status, points, sync_all, sprint_id):
289
290
  args.append("--status")
290
291
  if points or sync_all:
291
292
  args.append("--points")
293
+ if assignee or sync_all:
294
+ args.append("--assignee")
292
295
  if sync_all:
293
296
  args.append("--all")
294
297
  if sprint_id:
@@ -301,6 +301,34 @@ def map_github_to_jira(github_user: str | None) -> str | None:
301
301
  return GITHUB_TO_JIRA_MAP.get(github_user, f"{github_user}@1898andco.io")
302
302
 
303
303
 
304
+ def get_current_user_email() -> str:
305
+ """Get the current user's Jira email address.
306
+
307
+ Resolution order:
308
+ 1. JIRA_USER environment variable
309
+ 2. git config user.email
310
+ 3. Default fallback
311
+
312
+ Returns:
313
+ Email address string
314
+ """
315
+ jira_user = os.environ.get("JIRA_USER")
316
+ if jira_user:
317
+ return jira_user
318
+
319
+ try:
320
+ result = subprocess.run(
321
+ ["git", "config", "user.email"],
322
+ capture_output=True, text=True, timeout=5,
323
+ )
324
+ if result.returncode == 0 and result.stdout.strip():
325
+ return result.stdout.strip()
326
+ except Exception:
327
+ pass
328
+
329
+ return "keith.avery@1898andco.io"
330
+
331
+
304
332
  # =============================================================================
305
333
  # JiraClient - Unified REST API client
306
334
  # =============================================================================