@pennyfarthing/core 8.1.0 → 9.0.3

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 (329) hide show
  1. package/README.md +18 -9
  2. package/package.json +3 -3
  3. package/packages/core/dist/cli/commands/doctor.d.ts +5 -2
  4. package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
  5. package/packages/core/dist/cli/commands/doctor.js +225 -17
  6. package/packages/core/dist/cli/commands/doctor.js.map +1 -1
  7. package/packages/core/dist/cli/commands/init.d.ts.map +1 -1
  8. package/packages/core/dist/cli/commands/init.js +3 -246
  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 +4 -140
  12. package/packages/core/dist/cli/commands/update.js.map +1 -1
  13. package/packages/core/dist/cli/utils/constants.d.ts +7 -1
  14. package/packages/core/dist/cli/utils/constants.d.ts.map +1 -1
  15. package/packages/core/dist/cli/utils/constants.js +2 -0
  16. package/packages/core/dist/cli/utils/constants.js.map +1 -1
  17. package/packages/core/dist/cli/utils/settings.d.ts +22 -0
  18. package/packages/core/dist/cli/utils/settings.d.ts.map +1 -0
  19. package/packages/core/dist/cli/utils/settings.js +300 -0
  20. package/packages/core/dist/cli/utils/settings.js.map +1 -0
  21. package/pennyfarthing-dist/agents/README.md +1 -1
  22. package/pennyfarthing-dist/agents/dev.md +1 -1
  23. package/pennyfarthing-dist/agents/handoff.md +1 -1
  24. package/pennyfarthing-dist/agents/reviewer-preflight.md +1 -1
  25. package/pennyfarthing-dist/agents/sm-setup.md +3 -3
  26. package/pennyfarthing-dist/agents/sm.md +1 -1
  27. package/pennyfarthing-dist/agents/tea.md +1 -1
  28. package/pennyfarthing-dist/agents/testing-runner.md +3 -3
  29. package/pennyfarthing-dist/commands/architect.md +2 -0
  30. package/pennyfarthing-dist/commands/chore.md +18 -17
  31. package/pennyfarthing-dist/commands/continue-session.md +43 -9
  32. package/pennyfarthing-dist/commands/dev.md +2 -0
  33. package/pennyfarthing-dist/commands/devops.md +2 -0
  34. package/pennyfarthing-dist/commands/fix-blocker.md +22 -0
  35. package/pennyfarthing-dist/commands/git-cleanup.md +25 -19
  36. package/pennyfarthing-dist/commands/health-check.md +2 -0
  37. package/pennyfarthing-dist/commands/new-work.md +23 -0
  38. package/pennyfarthing-dist/commands/orchestrator.md +2 -0
  39. package/pennyfarthing-dist/commands/parallel-work.md +4 -2
  40. package/pennyfarthing-dist/commands/patch.md +210 -0
  41. package/pennyfarthing-dist/commands/pm.md +2 -0
  42. package/pennyfarthing-dist/commands/reviewer.md +2 -0
  43. package/pennyfarthing-dist/commands/sm.md +2 -0
  44. package/pennyfarthing-dist/commands/tea.md +2 -0
  45. package/pennyfarthing-dist/commands/tech-writer.md +2 -0
  46. package/pennyfarthing-dist/commands/ux-designer.md +2 -0
  47. package/pennyfarthing-dist/commands/work.md +2 -0
  48. package/pennyfarthing-dist/guides/agent-behavior.md +29 -264
  49. package/pennyfarthing-dist/guides/session-schema.md +346 -0
  50. package/pennyfarthing-dist/guides/skill-schema.md +412 -0
  51. package/pennyfarthing-dist/guides/workflow-step-schema.md +512 -0
  52. package/pennyfarthing-dist/guides/xml-tags.md +292 -0
  53. package/pennyfarthing-dist/scripts/core/agent-session.sh +7 -0
  54. package/pennyfarthing-dist/scripts/core/check-context.sh +140 -226
  55. package/pennyfarthing-dist/scripts/core/handoff-marker.sh +13 -2
  56. package/pennyfarthing-dist/scripts/git/worktree-manager.sh +4 -1
  57. package/pennyfarthing-dist/scripts/health/drift-detection.sh +1 -7
  58. package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +43 -8
  59. package/pennyfarthing-dist/scripts/hooks/post-merge.sh +4 -11
  60. package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +3 -8
  61. package/pennyfarthing-dist/scripts/hooks/pre-push.sh +3 -3
  62. package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +30 -0
  63. package/pennyfarthing-dist/scripts/jira/create-jira-epic.sh +1 -7
  64. package/pennyfarthing-dist/scripts/jira/create-jira-story.sh +2 -8
  65. package/pennyfarthing-dist/scripts/jira/jira-reconcile.sh +2 -8
  66. package/pennyfarthing-dist/scripts/lib/find-root.sh +41 -44
  67. package/pennyfarthing-dist/scripts/maintenance/sidecar-health.sh +1 -7
  68. package/pennyfarthing-dist/scripts/sprint/archive-story.sh +2 -8
  69. package/pennyfarthing-dist/scripts/sprint/available-stories.sh +2 -8
  70. package/pennyfarthing-dist/scripts/sprint/check-story.sh +2 -8
  71. package/pennyfarthing-dist/scripts/sprint/get-epic-field.sh +2 -8
  72. package/pennyfarthing-dist/scripts/sprint/get-story-field.sh +2 -8
  73. package/pennyfarthing-dist/scripts/sprint/list-future.sh +2 -8
  74. package/pennyfarthing-dist/scripts/sprint/new-sprint.sh +2 -8
  75. package/pennyfarthing-dist/scripts/sprint/promote-epic.sh +2 -8
  76. package/pennyfarthing-dist/scripts/sprint/sprint-info.sh +2 -8
  77. package/pennyfarthing-dist/scripts/tests/test-character-voice.sh +2 -1
  78. package/pennyfarthing-dist/scripts/workflow/finish-story.sh +4 -9
  79. package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +2 -8
  80. package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +2 -8
  81. package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +1 -7
  82. package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +2 -8
  83. package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +2 -8
  84. package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +2 -8
  85. package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +2 -8
  86. package/pennyfarthing-dist/skills/agentic-patterns/SKILL.md +4 -0
  87. package/pennyfarthing-dist/skills/changelog/SKILL.md +18 -0
  88. package/pennyfarthing-dist/skills/code-review/SKILL.md +5 -1
  89. package/pennyfarthing-dist/skills/context-engineering/SKILL.md +3 -0
  90. package/pennyfarthing-dist/skills/cyclist/SKILL.md +2 -2
  91. package/pennyfarthing-dist/skills/dev-patterns/SKILL.md +25 -1
  92. package/pennyfarthing-dist/skills/finalize-run/SKILL.md +3 -0
  93. package/pennyfarthing-dist/skills/jira/SKILL.md +48 -24
  94. package/pennyfarthing-dist/skills/judge/SKILL.md +8 -0
  95. package/pennyfarthing-dist/skills/just/SKILL.md +11 -0
  96. package/pennyfarthing-dist/skills/mermaid/SKILL.md +16 -0
  97. package/pennyfarthing-dist/skills/otel/skill.md +4 -0
  98. package/pennyfarthing-dist/skills/permissions/skill.md +3 -0
  99. package/pennyfarthing-dist/skills/persona-benchmark/SKILL.md +9 -0
  100. package/pennyfarthing-dist/skills/sprint/scripts/sync-epic-jira.sh +7 -0
  101. package/pennyfarthing-dist/skills/sprint/skill.md +30 -30
  102. package/pennyfarthing-dist/skills/story/skill.md +16 -16
  103. package/pennyfarthing-dist/skills/systematic-debugging/SKILL.md +56 -0
  104. package/pennyfarthing-dist/skills/testing/SKILL.md +22 -0
  105. package/pennyfarthing-dist/skills/theme/skill.md +12 -0
  106. package/pennyfarthing-dist/skills/theme-creation/SKILL.md +4 -0
  107. package/pennyfarthing-dist/skills/workflow/skill.md +22 -14
  108. package/pennyfarthing-dist/skills/yq/SKILL.md +8 -0
  109. package/pennyfarthing-dist/templates/settings.local.json.template +9 -0
  110. package/pennyfarthing-dist/workflows/architecture/steps/step-01-initialize.md +12 -0
  111. package/pennyfarthing-dist/workflows/architecture/steps/step-01b-continue.md +12 -0
  112. package/pennyfarthing-dist/workflows/architecture/steps/step-02-context.md +12 -0
  113. package/pennyfarthing-dist/workflows/architecture/steps/step-03-patterns.md +12 -0
  114. package/pennyfarthing-dist/workflows/architecture/steps/step-04-components.md +12 -0
  115. package/pennyfarthing-dist/workflows/architecture/steps/step-05-interfaces.md +12 -0
  116. package/pennyfarthing-dist/workflows/architecture/steps/step-06-risks.md +12 -0
  117. package/pennyfarthing-dist/workflows/architecture/steps/step-07-document.md +12 -0
  118. package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-01-validate-prerequisites.md +25 -0
  119. package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-02-design-epics.md +23 -0
  120. package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-03-create-stories.md +26 -0
  121. package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-04-final-validation.md +24 -0
  122. package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-05-import-to-future.md +23 -0
  123. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-01-analyze.md +43 -41
  124. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-02-categorize.md +50 -19
  125. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-03-execute.md +102 -111
  126. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-04-verify.md +48 -39
  127. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-05-complete.md +30 -31
  128. package/pennyfarthing-dist/workflows/implementation-readiness/steps/step-01-document-discovery.md +21 -0
  129. package/pennyfarthing-dist/workflows/implementation-readiness/steps/step-02-prd-analysis.md +21 -0
  130. package/pennyfarthing-dist/workflows/implementation-readiness/steps/step-03-epic-coverage-validation.md +23 -0
  131. package/pennyfarthing-dist/workflows/implementation-readiness/steps/step-04-ux-alignment.md +23 -0
  132. package/pennyfarthing-dist/workflows/implementation-readiness/steps/step-05-epic-quality-review.md +28 -0
  133. package/pennyfarthing-dist/workflows/implementation-readiness/steps/step-06-final-assessment.md +25 -0
  134. package/pennyfarthing-dist/workflows/interactive-debug/steps/step-01-connect.md +257 -0
  135. package/pennyfarthing-dist/workflows/interactive-debug/steps/step-02-explore.md +107 -0
  136. package/pennyfarthing-dist/workflows/interactive-debug/steps/step-03-fix.md +127 -0
  137. package/pennyfarthing-dist/workflows/interactive-debug/steps/step-04-commit.md +122 -0
  138. package/pennyfarthing-dist/workflows/interactive-debug/workflow.yaml +51 -0
  139. package/pennyfarthing-dist/workflows/patch.yaml +68 -0
  140. package/pennyfarthing-dist/workflows/prd/steps-c/step-01-init.md +6 -0
  141. package/pennyfarthing-dist/workflows/prd/steps-c/step-01b-continue.md +6 -0
  142. package/pennyfarthing-dist/workflows/prd/steps-c/step-02-discovery.md +6 -0
  143. package/pennyfarthing-dist/workflows/prd/steps-c/step-03-success.md +6 -0
  144. package/pennyfarthing-dist/workflows/prd/steps-c/step-04-journeys.md +6 -0
  145. package/pennyfarthing-dist/workflows/prd/steps-c/step-05-domain.md +6 -0
  146. package/pennyfarthing-dist/workflows/prd/steps-c/step-06-innovation.md +6 -0
  147. package/pennyfarthing-dist/workflows/prd/steps-c/step-07-project-type.md +6 -0
  148. package/pennyfarthing-dist/workflows/prd/steps-c/step-08-scoping.md +6 -0
  149. package/pennyfarthing-dist/workflows/prd/steps-c/step-09-functional.md +6 -0
  150. package/pennyfarthing-dist/workflows/prd/steps-c/step-10-nonfunctional.md +6 -0
  151. package/pennyfarthing-dist/workflows/prd/steps-c/step-11-polish.md +6 -0
  152. package/pennyfarthing-dist/workflows/prd/steps-c/step-12-complete.md +6 -0
  153. package/pennyfarthing-dist/workflows/prd/steps-e/step-e-01-discovery.md +6 -0
  154. package/pennyfarthing-dist/workflows/prd/steps-e/step-e-01b-legacy-conversion.md +6 -0
  155. package/pennyfarthing-dist/workflows/prd/steps-e/step-e-02-review.md +6 -0
  156. package/pennyfarthing-dist/workflows/prd/steps-e/step-e-03-edit.md +6 -0
  157. package/pennyfarthing-dist/workflows/prd/steps-e/step-e-04-complete.md +6 -0
  158. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-01-discovery.md +6 -0
  159. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-02-format-detection.md +6 -0
  160. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-02b-parity-check.md +6 -0
  161. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-03-density-validation.md +6 -0
  162. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-04-brief-coverage-validation.md +6 -0
  163. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-05-measurability-validation.md +6 -0
  164. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-06-traceability-validation.md +6 -0
  165. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-07-implementation-leakage-validation.md +6 -0
  166. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-08-domain-compliance-validation.md +6 -0
  167. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-09-project-type-validation.md +6 -0
  168. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-10-smart-validation.md +6 -0
  169. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-11-holistic-quality-validation.md +6 -0
  170. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-12-completeness-validation.md +6 -0
  171. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-13-report-complete.md +6 -0
  172. package/pennyfarthing-dist/workflows/product-brief/steps/step-01-init.md +18 -0
  173. package/pennyfarthing-dist/workflows/product-brief/steps/step-01b-continue.md +19 -0
  174. package/pennyfarthing-dist/workflows/product-brief/steps/step-02-vision.md +22 -0
  175. package/pennyfarthing-dist/workflows/product-brief/steps/step-03-users.md +22 -0
  176. package/pennyfarthing-dist/workflows/product-brief/steps/step-04-metrics.md +23 -0
  177. package/pennyfarthing-dist/workflows/product-brief/steps/step-05-scope.md +24 -0
  178. package/pennyfarthing-dist/workflows/product-brief/steps/step-06-complete.md +22 -0
  179. package/pennyfarthing-dist/workflows/project-context/steps/step-01-discover.md +22 -0
  180. package/pennyfarthing-dist/workflows/project-context/steps/step-02-generate.md +31 -0
  181. package/pennyfarthing-dist/workflows/project-context/steps/step-03-complete.md +28 -0
  182. package/pennyfarthing-dist/workflows/quick-dev/steps/step-01-mode-detection.md +21 -0
  183. package/pennyfarthing-dist/workflows/quick-dev/steps/step-02-context-gathering.md +23 -0
  184. package/pennyfarthing-dist/workflows/quick-dev/steps/step-03-execute.md +25 -0
  185. package/pennyfarthing-dist/workflows/quick-dev/steps/step-04-self-check.md +22 -0
  186. package/pennyfarthing-dist/workflows/quick-dev/steps/step-05-adversarial-review.md +23 -0
  187. package/pennyfarthing-dist/workflows/quick-dev/steps/step-06-resolve-findings.md +23 -0
  188. package/pennyfarthing-dist/workflows/quick-spec/steps/step-01-understand.md +12 -0
  189. package/pennyfarthing-dist/workflows/quick-spec/steps/step-02-investigate.md +12 -0
  190. package/pennyfarthing-dist/workflows/quick-spec/steps/step-03-generate.md +12 -0
  191. package/pennyfarthing-dist/workflows/quick-spec/steps/step-04-review.md +12 -0
  192. package/pennyfarthing-dist/workflows/research/steps-domain/step-01-init.md +22 -0
  193. package/pennyfarthing-dist/workflows/research/steps-domain/step-02-domain-analysis.md +24 -0
  194. package/pennyfarthing-dist/workflows/research/steps-domain/step-03-competitive-landscape.md +25 -0
  195. package/pennyfarthing-dist/workflows/research/steps-domain/step-04-regulatory-focus.md +26 -0
  196. package/pennyfarthing-dist/workflows/research/steps-domain/step-05-technical-trends.md +26 -0
  197. package/pennyfarthing-dist/workflows/research/steps-domain/step-06-research-synthesis.md +34 -0
  198. package/pennyfarthing-dist/workflows/research/steps-market/step-01-init.md +23 -0
  199. package/pennyfarthing-dist/workflows/research/steps-market/step-02-customer-behavior.md +25 -0
  200. package/pennyfarthing-dist/workflows/research/steps-market/step-02-customer-insights.md +27 -0
  201. package/pennyfarthing-dist/workflows/research/steps-market/step-03-customer-pain-points.md +26 -0
  202. package/pennyfarthing-dist/workflows/research/steps-market/step-04-customer-decisions.md +27 -0
  203. package/pennyfarthing-dist/workflows/research/steps-market/step-05-competitive-analysis.md +26 -0
  204. package/pennyfarthing-dist/workflows/research/steps-market/step-06-research-completion.md +35 -0
  205. package/pennyfarthing-dist/workflows/research/steps-technical/step-01-init.md +22 -0
  206. package/pennyfarthing-dist/workflows/research/steps-technical/step-02-technical-overview.md +25 -0
  207. package/pennyfarthing-dist/workflows/research/steps-technical/step-03-integration-patterns.md +26 -0
  208. package/pennyfarthing-dist/workflows/research/steps-technical/step-04-architectural-patterns.md +26 -0
  209. package/pennyfarthing-dist/workflows/research/steps-technical/step-05-implementation-research.md +29 -1
  210. package/pennyfarthing-dist/workflows/research/steps-technical/step-06-research-synthesis.md +37 -1
  211. package/pennyfarthing-dist/workflows/sprint-planning/steps/step-01-parse-epic-files.md +15 -0
  212. package/pennyfarthing-dist/workflows/sprint-planning/steps/step-02-build-sprint-status.md +17 -0
  213. package/pennyfarthing-dist/workflows/sprint-planning/steps/step-03-status-detection.md +16 -0
  214. package/pennyfarthing-dist/workflows/sprint-planning/steps/step-04-generate-status-file.md +17 -0
  215. package/pennyfarthing-dist/workflows/sprint-planning/steps/step-05-validate-and-report.md +22 -0
  216. package/pennyfarthing-dist/workflows/ux-design/steps/step-01-init.md +6 -0
  217. package/pennyfarthing-dist/workflows/ux-design/steps/step-01b-continue.md +6 -0
  218. package/pennyfarthing-dist/workflows/ux-design/steps/step-02-discovery.md +6 -0
  219. package/pennyfarthing-dist/workflows/ux-design/steps/step-03-core-experience.md +6 -0
  220. package/pennyfarthing-dist/workflows/ux-design/steps/step-04-emotional-response.md +6 -0
  221. package/pennyfarthing-dist/workflows/ux-design/steps/step-05-inspiration.md +6 -0
  222. package/pennyfarthing-dist/workflows/ux-design/steps/step-06-design-system.md +6 -0
  223. package/pennyfarthing-dist/workflows/ux-design/steps/step-07-defining-experience.md +6 -0
  224. package/pennyfarthing-dist/workflows/ux-design/steps/step-08-visual-foundation.md +6 -0
  225. package/pennyfarthing-dist/workflows/ux-design/steps/step-09-design-directions.md +6 -0
  226. package/pennyfarthing-dist/workflows/ux-design/steps/step-10-user-journeys.md +6 -0
  227. package/pennyfarthing-dist/workflows/ux-design/steps/step-11-component-strategy.md +6 -0
  228. package/pennyfarthing-dist/workflows/ux-design/steps/step-12-ux-patterns.md +6 -0
  229. package/pennyfarthing-dist/workflows/ux-design/steps/step-13-responsive-accessibility.md +6 -0
  230. package/pennyfarthing-dist/workflows/ux-design/steps/step-14-complete.md +6 -0
  231. package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
  232. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  233. package/pennyfarthing_scripts/common/__pycache__/__init__.cpython-314.pyc +0 -0
  234. package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
  235. package/pennyfarthing_scripts/common/__pycache__/output.cpython-314.pyc +0 -0
  236. package/pennyfarthing_scripts/context.py +414 -0
  237. package/pennyfarthing_scripts/migration/__init__.py +39 -0
  238. package/pennyfarthing_scripts/migration/__main__.py +10 -0
  239. package/pennyfarthing_scripts/migration/cli.py +304 -0
  240. package/pennyfarthing_scripts/migration/session.py +384 -0
  241. package/pennyfarthing_scripts/migration/skill.py +188 -0
  242. package/pennyfarthing_scripts/migration/step.py +229 -0
  243. package/pennyfarthing_scripts/migration/validate.py +282 -0
  244. package/pennyfarthing_scripts/patch_mode.py +449 -0
  245. package/pennyfarthing_scripts/prime/__pycache__/__init__.cpython-314.pyc +0 -0
  246. package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
  247. package/pennyfarthing_scripts/prime/__pycache__/loader.cpython-314.pyc +0 -0
  248. package/pennyfarthing_scripts/prime/__pycache__/models.cpython-314.pyc +0 -0
  249. package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
  250. package/pennyfarthing_scripts/prime/__pycache__/session.cpython-314.pyc +0 -0
  251. package/pennyfarthing_scripts/prime/__pycache__/tiers.cpython-314.pyc +0 -0
  252. package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  253. package/pennyfarthing_scripts/prime/cli.py +201 -0
  254. package/pennyfarthing_scripts/prime/models.py +9 -0
  255. package/pennyfarthing_scripts/prime/persona.py +41 -0
  256. package/pennyfarthing_scripts/prime/tiers.py +201 -0
  257. package/pennyfarthing_scripts/schema_validation_hook.py +306 -0
  258. package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
  259. package/pennyfarthing_scripts/sprint/__pycache__/__main__.cpython-314.pyc +0 -0
  260. package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
  261. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  262. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  263. package/pennyfarthing_scripts/sprint/__pycache__/status.cpython-314.pyc +0 -0
  264. package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-314.pyc +0 -0
  265. package/pennyfarthing_scripts/sprint/archive_epic.py +399 -0
  266. package/pennyfarthing_scripts/sprint/cli.py +100 -0
  267. package/pennyfarthing_scripts/sprint/import_epic.py +431 -0
  268. package/pennyfarthing_scripts/story/__pycache__/__init__.cpython-314.pyc +0 -0
  269. package/pennyfarthing_scripts/story/__pycache__/__main__.cpython-314.pyc +0 -0
  270. package/pennyfarthing_scripts/story/__pycache__/cli.cpython-314.pyc +0 -0
  271. package/pennyfarthing_scripts/story/__pycache__/create.cpython-314.pyc +0 -0
  272. package/pennyfarthing_scripts/story/__pycache__/size.cpython-314.pyc +0 -0
  273. package/pennyfarthing_scripts/story/__pycache__/template.cpython-314.pyc +0 -0
  274. package/pennyfarthing_scripts/tests/test_patch_mode.py +830 -0
  275. package/pennyfarthing_scripts/tests/test_tiers.py +1090 -0
  276. package/pennyfarthing_scripts/tests/test_token_counting.py +559 -0
  277. package/pennyfarthing-dist/scripts/hooks/__pycache__/question_reflector_check.cpython-314.pyc +0 -0
  278. package/pennyfarthing-dist/scripts/sprint/import-epic-to-future.sh +0 -10
  279. package/pennyfarthing-dist/scripts/sprint/import_epic_to_future.py +0 -270
  280. package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  281. package/pennyfarthing_scripts/__pycache__/config.cpython-314.pyc +0 -0
  282. package/pennyfarthing_scripts/__pycache__/jira.cpython-314.pyc +0 -0
  283. package/pennyfarthing_scripts/__pycache__/jira_bidirectional_sync.cpython-314.pyc +0 -0
  284. package/pennyfarthing_scripts/__pycache__/jira_epic_creation.cpython-314.pyc +0 -0
  285. package/pennyfarthing_scripts/__pycache__/jira_sync.cpython-314.pyc +0 -0
  286. package/pennyfarthing_scripts/__pycache__/jira_sync_story.cpython-314.pyc +0 -0
  287. package/pennyfarthing_scripts/__pycache__/output.cpython-314.pyc +0 -0
  288. package/pennyfarthing_scripts/__pycache__/sprint.cpython-314.pyc +0 -0
  289. package/pennyfarthing_scripts/__pycache__/workflow.cpython-311.pyc +0 -0
  290. package/pennyfarthing_scripts/__pycache__/workflow.cpython-314.pyc +0 -0
  291. package/pennyfarthing_scripts/brownfield/__pycache__/__init__.cpython-314.pyc +0 -0
  292. package/pennyfarthing_scripts/brownfield/__pycache__/__main__.cpython-314.pyc +0 -0
  293. package/pennyfarthing_scripts/brownfield/__pycache__/cli.cpython-314.pyc +0 -0
  294. package/pennyfarthing_scripts/brownfield/__pycache__/discover.cpython-314.pyc +0 -0
  295. package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
  296. package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
  297. package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
  298. package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-314.pyc +0 -0
  299. package/pennyfarthing_scripts/jira/__pycache__/__main__.cpython-314.pyc +0 -0
  300. package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
  301. package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-314.pyc +0 -0
  302. package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
  303. package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
  304. package/pennyfarthing_scripts/jira/__pycache__/compat.cpython-314.pyc +0 -0
  305. package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-314.pyc +0 -0
  306. package/pennyfarthing_scripts/jira/__pycache__/mappings.cpython-314.pyc +0 -0
  307. package/pennyfarthing_scripts/jira/__pycache__/models.cpython-314.pyc +0 -0
  308. package/pennyfarthing_scripts/jira/__pycache__/story.cpython-314.pyc +0 -0
  309. package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-314.pyc +0 -0
  310. package/pennyfarthing_scripts/preflight/__pycache__/__init__.cpython-314.pyc +0 -0
  311. package/pennyfarthing_scripts/preflight/__pycache__/__main__.cpython-314.pyc +0 -0
  312. package/pennyfarthing_scripts/preflight/__pycache__/cli.cpython-314.pyc +0 -0
  313. package/pennyfarthing_scripts/preflight/__pycache__/finish.cpython-314.pyc +0 -0
  314. package/pennyfarthing_scripts/prime/__pycache__/__main__.cpython-314.pyc +0 -0
  315. package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-314.pyc +0 -0
  316. package/pennyfarthing_scripts/tests/__pycache__/__init__.cpython-314.pyc +0 -0
  317. package/pennyfarthing_scripts/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
  318. package/pennyfarthing_scripts/tests/__pycache__/test_brownfield.cpython-314-pytest-9.0.2.pyc +0 -0
  319. package/pennyfarthing_scripts/tests/__pycache__/test_cli_modules.cpython-314-pytest-9.0.2.pyc +0 -0
  320. package/pennyfarthing_scripts/tests/__pycache__/test_common.cpython-314-pytest-9.0.2.pyc +0 -0
  321. package/pennyfarthing_scripts/tests/__pycache__/test_git_utils.cpython-314-pytest-9.0.2.pyc +0 -0
  322. package/pennyfarthing_scripts/tests/__pycache__/test_jira_package.cpython-314-pytest-9.0.2.pyc +0 -0
  323. package/pennyfarthing_scripts/tests/__pycache__/test_package_structure.cpython-314-pytest-9.0.2.pyc +0 -0
  324. package/pennyfarthing_scripts/tests/__pycache__/test_prime.cpython-314-pytest-9.0.2.pyc +0 -0
  325. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_package.cpython-314-pytest-9.0.2.pyc +0 -0
  326. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
  327. package/pennyfarthing_scripts/tests/__pycache__/test_story_package.cpython-314-pytest-9.0.2.pyc +0 -0
  328. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
  329. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_cli.cpython-314-pytest-9.0.2.pyc +0 -0
@@ -34,6 +34,7 @@ from pennyfarthing_scripts.prime.loader import (
34
34
  )
35
35
  from pennyfarthing_scripts.prime.models import PrimeResult, WorkflowState
36
36
  from pennyfarthing_scripts.prime.persona import (
37
+ format_persona_compressed,
37
38
  format_persona_output,
38
39
  get_crew_manifest,
39
40
  get_user_title,
@@ -41,6 +42,7 @@ from pennyfarthing_scripts.prime.persona import (
41
42
  load_persona,
42
43
  )
43
44
  from pennyfarthing_scripts.prime.session import cleanup_old_sessions, register_session
45
+ from pennyfarthing_scripts.prime.tiers import ContextTier, tier_from_string, load_tier_components
44
46
  from pennyfarthing_scripts.prime.workflow import check_redirect, detect_workflow_state
45
47
 
46
48
 
@@ -87,6 +89,154 @@ def _format_workflow_state_text(result: PrimeResult) -> str:
87
89
  return "\n".join(lines)
88
90
 
89
91
 
92
+ def _prime_tiered(
93
+ agent_name: str | None,
94
+ tier: ContextTier,
95
+ quiet: bool,
96
+ json_output: bool,
97
+ no_workflow: bool,
98
+ no_register: bool,
99
+ session_id: str | None,
100
+ root: Path,
101
+ result: PrimeResult,
102
+ ) -> int:
103
+ """Handle reduced tier context loading (REFRESH, HANDOFF, MINIMAL).
104
+
105
+ This is a separate path from the FULL tier to ensure reduced output.
106
+
107
+ Args:
108
+ agent_name: Name of agent to load context for
109
+ tier: Context tier level (REFRESH, HANDOFF, or MINIMAL)
110
+ quiet: If True, suppress section headers
111
+ json_output: If True, output JSON instead of text
112
+ no_workflow: If True, skip workflow detection
113
+ no_register: If True, skip session registration
114
+ session_id: Explicit session ID
115
+ root: Project root path
116
+ result: PrimeResult to populate
117
+
118
+ Returns:
119
+ Exit code (0 for success)
120
+ """
121
+ # Session registration (if enabled)
122
+ if agent_name and not no_register:
123
+ cleanup_old_sessions(root)
124
+ session_info = register_session(agent_name, session_id, root)
125
+ result.session_id = session_info.session_id
126
+
127
+ # Workflow state (always included in all tiers)
128
+ if not no_workflow:
129
+ workflow_status = detect_workflow_state(root)
130
+ result.workflow_status = workflow_status
131
+
132
+ if agent_name and workflow_status.state == WorkflowState.IN_PROGRESS_STATE:
133
+ redirect = check_redirect(workflow_status, agent_name)
134
+ if redirect:
135
+ result.redirect_to, result.redirect_reason = redirect
136
+
137
+ if not json_output:
138
+ _print_header("Workflow State", quiet)
139
+ print(_format_workflow_state_text(result))
140
+
141
+ # MINIMAL tier: Just workflow state + note
142
+ if tier == ContextTier.MINIMAL:
143
+ if not json_output:
144
+ print()
145
+ print("<!-- Minimal context: see conversation history for full agent context -->")
146
+
147
+ if json_output:
148
+ # Get token counts from load_tier_components
149
+ components = load_tier_components(tier, agent_name or "", root)
150
+ result.tier = tier.value
151
+ result.token_counts = components.get("token_counts", {})
152
+ result.total_tokens = components.get("total_tokens", 0)
153
+ print(json.dumps(result.to_dict(), indent=2))
154
+
155
+ return 0
156
+
157
+ # REFRESH tier: Dynamic state only
158
+ if tier == ContextTier.REFRESH:
159
+ if not json_output:
160
+ # Sprint context
161
+ sprint_content = load_sprint_context(root)
162
+ if sprint_content:
163
+ _print_header("Sprint Context", quiet)
164
+ print(sprint_content)
165
+
166
+ # Session header only (not full assessment)
167
+ session_result = load_session_context(root)
168
+ if session_result:
169
+ filename, header, _ = session_result
170
+ _print_header(f"Active Session: {filename}", quiet)
171
+ if header:
172
+ print(header)
173
+
174
+ # Note about full context
175
+ print()
176
+ print("<!-- Full context already in conversation history -->")
177
+
178
+ if json_output:
179
+ # Get token counts from load_tier_components
180
+ components = load_tier_components(tier, agent_name or "", root)
181
+ result.tier = tier.value
182
+ result.token_counts = components.get("token_counts", {})
183
+ result.total_tokens = components.get("total_tokens", 0)
184
+ print(json.dumps(result.to_dict(), indent=2))
185
+
186
+ return 0
187
+
188
+ # HANDOFF tier: Agent essentials for new agent
189
+ if tier == ContextTier.HANDOFF:
190
+ if agent_name:
191
+ # Agent definition
192
+ agent_content = load_agent_definition(agent_name, root)
193
+ if agent_content is None:
194
+ if json_output:
195
+ print(json.dumps({"error": f"Agent '{agent_name}' not found"}))
196
+ else:
197
+ print(f"Error: Agent '{agent_name}' not found", file=sys.stderr)
198
+ return 1
199
+
200
+ if not json_output:
201
+ _print_header(f"Agent Definition: {agent_name}", quiet)
202
+ print(agent_content)
203
+
204
+ # Compressed persona
205
+ if is_character_voice_enabled(root):
206
+ persona, theme = load_persona(agent_name, root)
207
+ if persona and theme:
208
+ result.persona = persona
209
+ result.theme = theme
210
+ if not json_output:
211
+ _print_header(f"Persona: {persona.character} ({agent_name})", quiet)
212
+ print(format_persona_compressed(persona, theme, agent_name))
213
+
214
+ if not json_output:
215
+ # Note about behavior guides
216
+ print()
217
+ print("<!-- Behavior guides in conversation history -->")
218
+
219
+ # Redirect marker
220
+ if result.redirect_to and not json_output:
221
+ print()
222
+ print("=" * 60)
223
+ print(f"REDIRECT: You ({agent_name}) should hand off to {result.redirect_to}")
224
+ print(f"Reason: {result.redirect_reason}")
225
+ print("=" * 60)
226
+
227
+ if json_output:
228
+ # Get token counts from load_tier_components
229
+ components = load_tier_components(tier, agent_name or "", root)
230
+ result.tier = tier.value
231
+ result.token_counts = components.get("token_counts", {})
232
+ result.total_tokens = components.get("total_tokens", 0)
233
+ print(json.dumps(result.to_dict(), indent=2))
234
+
235
+ return 0
236
+
237
+ return 0
238
+
239
+
90
240
  def prime(
91
241
  agent_name: str | None = None,
92
242
  minimal: bool = False,
@@ -98,6 +248,7 @@ def prime(
98
248
  no_register: bool = False,
99
249
  session_id: str | None = None,
100
250
  project_root: Path | None = None,
251
+ tier: str | None = None,
101
252
  ) -> int:
102
253
  """Load and print context.
103
254
 
@@ -113,6 +264,12 @@ def prime(
113
264
  9. Domain docs (--full only)
114
265
  10. Redirect marker (if wrong agent)
115
266
 
267
+ Context tiers (--tier):
268
+ - FULL: All components (~4000 tokens) - default
269
+ - REFRESH: Dynamic state only (~600 tokens)
270
+ - HANDOFF: Agent essentials (~700 tokens)
271
+ - MINIMAL: Routing only (~200 tokens)
272
+
116
273
  Args:
117
274
  agent_name: Name of agent to load context for
118
275
  minimal: If True, skip all context (fastest)
@@ -124,6 +281,7 @@ def prime(
124
281
  no_register: If True, skip session registration
125
282
  session_id: Explicit session ID (generated if not provided)
126
283
  project_root: Project root path (auto-detected if not provided)
284
+ tier: Context tier level (FULL, REFRESH, HANDOFF, MINIMAL)
127
285
 
128
286
  Returns:
129
287
  Exit code (0 for success)
@@ -139,6 +297,31 @@ def prime(
139
297
  # Build result for JSON output
140
298
  result = PrimeResult(agent_name=agent_name or "")
141
299
 
300
+ # Parse tier if specified
301
+ context_tier: ContextTier | None = None
302
+ if tier:
303
+ try:
304
+ context_tier = tier_from_string(tier)
305
+ except ValueError as e:
306
+ print(f"Error: {e}", file=sys.stderr)
307
+ return 1
308
+
309
+ # ==========================================================================
310
+ # TIERED CONTEXT PATH (REFRESH, HANDOFF, MINIMAL)
311
+ # ==========================================================================
312
+ if context_tier and context_tier != ContextTier.FULL:
313
+ return _prime_tiered(
314
+ agent_name=agent_name,
315
+ tier=context_tier,
316
+ quiet=quiet,
317
+ json_output=json_output,
318
+ no_workflow=no_workflow,
319
+ no_register=no_register,
320
+ session_id=session_id,
321
+ root=root,
322
+ result=result,
323
+ )
324
+
142
325
  # ==========================================================================
143
326
  # Session registration (if enabled)
144
327
  # ==========================================================================
@@ -267,6 +450,16 @@ def prime(
267
450
  # JSON output
268
451
  # ==========================================================================
269
452
  if json_output:
453
+ # Get token counts for FULL tier
454
+ tier_value = context_tier.value if context_tier else "FULL"
455
+ components = load_tier_components(
456
+ context_tier or ContextTier.FULL,
457
+ agent_name or "",
458
+ root,
459
+ )
460
+ result.tier = tier_value
461
+ result.token_counts = components.get("token_counts", {})
462
+ result.total_tokens = components.get("total_tokens", 0)
270
463
  print(json.dumps(result.to_dict(), indent=2))
271
464
 
272
465
  return 0
@@ -355,6 +548,13 @@ Examples:
355
548
  metavar="ID",
356
549
  help="Use explicit session ID",
357
550
  )
551
+ parser.add_argument(
552
+ "--tier",
553
+ metavar="TIER",
554
+ type=lambda x: x.upper(),
555
+ choices=["FULL", "REFRESH", "HANDOFF", "MINIMAL"],
556
+ help="Context tier: FULL (~4000 tokens), REFRESH (~600), HANDOFF (~700), MINIMAL (~200)",
557
+ )
358
558
 
359
559
  parsed = parser.parse_args(args)
360
560
 
@@ -369,6 +569,7 @@ Examples:
369
569
  no_workflow=parsed.no_workflow,
370
570
  no_register=parsed.no_register,
371
571
  session_id=parsed.session_id,
572
+ tier=parsed.tier,
372
573
  )
373
574
  except FileNotFoundError as e:
374
575
  print(f"Error: {e}", file=sys.stderr)
@@ -144,6 +144,9 @@ class PrimeResult:
144
144
  redirect_reason: Reason for redirect
145
145
  session_id: Session ID (if registered)
146
146
  crew: List of crew members for handoff reference
147
+ tier: Context tier used (FULL, REFRESH, HANDOFF, MINIMAL)
148
+ token_counts: Per-component token estimates
149
+ total_tokens: Sum of all component token counts
147
150
  """
148
151
 
149
152
  agent_name: str
@@ -154,6 +157,9 @@ class PrimeResult:
154
157
  redirect_reason: str | None = None
155
158
  session_id: str | None = None
156
159
  crew: list[CrewMember] = field(default_factory=list)
160
+ tier: str | None = None
161
+ token_counts: dict[str, int] = field(default_factory=dict)
162
+ total_tokens: int = 0
157
163
 
158
164
  def to_dict(self) -> dict[str, Any]:
159
165
  """Convert to dictionary for JSON serialization."""
@@ -166,4 +172,7 @@ class PrimeResult:
166
172
  "redirect_reason": self.redirect_reason,
167
173
  "session_id": self.session_id,
168
174
  "crew": [{"role": c.role, "character": c.character} for c in self.crew],
175
+ "tier": self.tier,
176
+ "token_counts": self.token_counts,
177
+ "total_tokens": self.total_tokens,
169
178
  }
@@ -286,3 +286,44 @@ def is_character_voice_enabled(project_root: Path | None = None) -> bool:
286
286
 
287
287
  # Default to enabled
288
288
  return True
289
+
290
+
291
+ def format_persona_compressed(
292
+ persona: Persona,
293
+ theme: str,
294
+ agent_name: str,
295
+ ) -> str:
296
+ """Format persona as compressed XML for reduced token usage.
297
+
298
+ Compressed format (~100 tokens vs ~300 for full):
299
+ <persona agent="dev" character="Rosie the Riveter">
300
+ <voice>Can-do wartime spirit, practical, determined</voice>
301
+ <catchphrase>"We Can Do It!"</catchphrase>
302
+ <style>Direct, encouraging, efficiency-focused</style>
303
+ </persona>
304
+
305
+ Args:
306
+ persona: Persona to format
307
+ theme: Theme name
308
+ agent_name: Agent name
309
+
310
+ Returns:
311
+ Compressed persona XML string (~100 tokens)
312
+ """
313
+ lines = [f'<persona agent="{agent_name}" character="{persona.character}">']
314
+
315
+ # Voice from style (primary behavioral descriptor)
316
+ if persona.style:
317
+ lines.append(f" <voice>{persona.style}</voice>")
318
+
319
+ # Catchphrase from quote
320
+ if persona.quote:
321
+ lines.append(f" <catchphrase>{persona.quote}</catchphrase>")
322
+
323
+ # Style from role (short descriptor)
324
+ if persona.role:
325
+ lines.append(f" <style>{persona.role}</style>")
326
+
327
+ lines.append("</persona>")
328
+
329
+ return "\n".join(lines)
@@ -0,0 +1,201 @@
1
+ """Tiered context injection for Prime.
2
+
3
+ Implements four context tiers based on session state:
4
+ - FULL (~4000 tokens): First turn of new session
5
+ - REFRESH (~600 tokens): Resumed session, same agent
6
+ - HANDOFF (~700 tokens): Resumed session, different agent
7
+ - MINIMAL (~200 tokens): Deep conversation (turn 3+), same agent
8
+
9
+ Story: MSSCI-12797 - Python Prime Tier Support
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from enum import Enum
15
+ from pathlib import Path
16
+ from typing import Any
17
+
18
+
19
+ def estimate_tokens(text: str) -> int:
20
+ """Estimate token count for a text string.
21
+
22
+ Uses character-based approximation (~4 characters per token) which is
23
+ reasonably accurate for English text with mixed code/prose content.
24
+
25
+ Args:
26
+ text: Text to estimate tokens for
27
+
28
+ Returns:
29
+ Estimated token count (0 for empty string)
30
+ """
31
+ if not text:
32
+ return 0
33
+ # Approximate: ~4 characters per token for cl100k_base encoding
34
+ # This is within 10% for typical agent context content
35
+ return max(1, len(text) // 4)
36
+
37
+
38
+ from pennyfarthing_scripts.prime.loader import (
39
+ load_agent_definition,
40
+ load_behavior_guide,
41
+ load_session_context,
42
+ load_sidecars,
43
+ load_sprint_context,
44
+ )
45
+ from pennyfarthing_scripts.prime.persona import (
46
+ format_persona_compressed,
47
+ get_crew_manifest,
48
+ get_user_title,
49
+ is_character_voice_enabled,
50
+ load_persona,
51
+ )
52
+ from pennyfarthing_scripts.prime.workflow import detect_workflow_state
53
+
54
+
55
+ class ContextTier(Enum):
56
+ """Context tier levels for session-aware injection."""
57
+
58
+ FULL = "FULL"
59
+ REFRESH = "REFRESH"
60
+ HANDOFF = "HANDOFF"
61
+ MINIMAL = "MINIMAL"
62
+
63
+
64
+ def tier_from_string(value: str) -> ContextTier:
65
+ """Convert string to ContextTier enum.
66
+
67
+ Args:
68
+ value: Tier name (case-insensitive)
69
+
70
+ Returns:
71
+ ContextTier enum value
72
+
73
+ Raises:
74
+ ValueError: If value is not a valid tier name
75
+ """
76
+ normalized = value.upper()
77
+ try:
78
+ return ContextTier(normalized)
79
+ except ValueError:
80
+ valid = ", ".join(t.value for t in ContextTier)
81
+ raise ValueError(f"Invalid tier '{value}'. Must be one of: {valid}")
82
+
83
+
84
+ def load_tier_components(
85
+ tier: ContextTier,
86
+ agent_name: str,
87
+ project_root: Path,
88
+ ) -> dict[str, Any]:
89
+ """Load components for the specified tier.
90
+
91
+ Component sets by tier:
92
+ - FULL: All components (workflow, agent, persona, guide, sprint, session, sidecars)
93
+ - REFRESH: Dynamic state only (workflow, sprint, session_header)
94
+ - HANDOFF: Agent essentials (workflow, agent, persona_compressed)
95
+ - MINIMAL: Routing only (workflow)
96
+
97
+ Args:
98
+ tier: Context tier level
99
+ agent_name: Name of the agent to load context for
100
+ project_root: Project root path
101
+
102
+ Returns:
103
+ Dict with:
104
+ - Component name -> content mappings
105
+ - "token_counts": Dict mapping component name to estimated token count
106
+ - "total_tokens": Sum of all component token counts
107
+ """
108
+ components: dict[str, Any] = {}
109
+ token_counts: dict[str, int] = {}
110
+
111
+ def add_component(name: str, content: str | Any) -> None:
112
+ """Add a component and track its token count."""
113
+ components[name] = content
114
+ # Estimate tokens for string content, 0 for structured data
115
+ if isinstance(content, str):
116
+ token_counts[name] = estimate_tokens(content)
117
+ else:
118
+ # For structured data (like WorkflowStatus), estimate from string repr
119
+ token_counts[name] = estimate_tokens(str(content))
120
+
121
+ # All tiers include workflow state
122
+ workflow_status = detect_workflow_state(project_root)
123
+ add_component("workflow_state", workflow_status)
124
+
125
+ if tier == ContextTier.MINIMAL:
126
+ # MINIMAL: Just workflow state
127
+ components["token_counts"] = token_counts
128
+ components["total_tokens"] = sum(token_counts.values())
129
+ return components
130
+
131
+ if tier == ContextTier.REFRESH:
132
+ # REFRESH: Dynamic state only
133
+ sprint_content = load_sprint_context(project_root)
134
+ if sprint_content:
135
+ add_component("sprint_context", sprint_content)
136
+
137
+ session_result = load_session_context(project_root)
138
+ if session_result:
139
+ filename, header, _ = session_result
140
+ add_component("session_header", header)
141
+
142
+ components["token_counts"] = token_counts
143
+ components["total_tokens"] = sum(token_counts.values())
144
+ return components
145
+
146
+ if tier == ContextTier.HANDOFF:
147
+ # HANDOFF: Agent essentials for new agent
148
+ agent_content = load_agent_definition(agent_name, project_root)
149
+ if agent_content:
150
+ add_component("agent_definition", agent_content)
151
+
152
+ # Load compressed persona
153
+ if is_character_voice_enabled(project_root):
154
+ persona, theme = load_persona(agent_name, project_root)
155
+ if persona and theme:
156
+ compressed = format_persona_compressed(persona, theme, agent_name)
157
+ add_component("persona_compressed", compressed)
158
+
159
+ components["token_counts"] = token_counts
160
+ components["total_tokens"] = sum(token_counts.values())
161
+ return components
162
+
163
+ # FULL tier: Everything
164
+ agent_content = load_agent_definition(agent_name, project_root)
165
+ if agent_content:
166
+ add_component("agent_definition", agent_content)
167
+
168
+ if is_character_voice_enabled(project_root):
169
+ persona, theme = load_persona(agent_name, project_root)
170
+ if persona and theme:
171
+ from pennyfarthing_scripts.prime.persona import format_persona_output
172
+
173
+ crew = get_crew_manifest(project_root)
174
+ user_title = get_user_title(project_root)
175
+ persona_content = format_persona_output(
176
+ persona, theme, agent_name, crew, user_title
177
+ )
178
+ add_component("persona", persona_content)
179
+
180
+ guide_content = load_behavior_guide(project_root)
181
+ if guide_content:
182
+ add_component("behavior_guide", guide_content)
183
+
184
+ sprint_content = load_sprint_context(project_root)
185
+ if sprint_content:
186
+ add_component("sprint_context", sprint_content)
187
+
188
+ session_result = load_session_context(project_root)
189
+ if session_result:
190
+ filename, header, assessment = session_result
191
+ add_component("session_header", header)
192
+ if assessment:
193
+ add_component("session_assessment", assessment)
194
+
195
+ sidecars = load_sidecars(agent_name, project_root)
196
+ if sidecars:
197
+ add_component("sidecars", sidecars)
198
+
199
+ components["token_counts"] = token_counts
200
+ components["total_tokens"] = sum(token_counts.values())
201
+ return components