@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.
- package/README.md +18 -9
- package/package.json +3 -3
- package/packages/core/dist/cli/commands/doctor.d.ts +5 -2
- package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/doctor.js +225 -17
- package/packages/core/dist/cli/commands/doctor.js.map +1 -1
- package/packages/core/dist/cli/commands/init.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/init.js +3 -246
- package/packages/core/dist/cli/commands/init.js.map +1 -1
- package/packages/core/dist/cli/commands/update.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/update.js +4 -140
- package/packages/core/dist/cli/commands/update.js.map +1 -1
- package/packages/core/dist/cli/utils/constants.d.ts +7 -1
- package/packages/core/dist/cli/utils/constants.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/constants.js +2 -0
- package/packages/core/dist/cli/utils/constants.js.map +1 -1
- package/packages/core/dist/cli/utils/settings.d.ts +22 -0
- package/packages/core/dist/cli/utils/settings.d.ts.map +1 -0
- package/packages/core/dist/cli/utils/settings.js +300 -0
- package/packages/core/dist/cli/utils/settings.js.map +1 -0
- package/pennyfarthing-dist/agents/README.md +1 -1
- package/pennyfarthing-dist/agents/dev.md +1 -1
- package/pennyfarthing-dist/agents/handoff.md +1 -1
- package/pennyfarthing-dist/agents/reviewer-preflight.md +1 -1
- package/pennyfarthing-dist/agents/sm-setup.md +3 -3
- package/pennyfarthing-dist/agents/sm.md +1 -1
- package/pennyfarthing-dist/agents/tea.md +1 -1
- package/pennyfarthing-dist/agents/testing-runner.md +3 -3
- package/pennyfarthing-dist/commands/architect.md +2 -0
- package/pennyfarthing-dist/commands/chore.md +18 -17
- package/pennyfarthing-dist/commands/continue-session.md +43 -9
- package/pennyfarthing-dist/commands/dev.md +2 -0
- package/pennyfarthing-dist/commands/devops.md +2 -0
- package/pennyfarthing-dist/commands/fix-blocker.md +22 -0
- package/pennyfarthing-dist/commands/git-cleanup.md +25 -19
- package/pennyfarthing-dist/commands/health-check.md +2 -0
- package/pennyfarthing-dist/commands/new-work.md +23 -0
- package/pennyfarthing-dist/commands/orchestrator.md +2 -0
- package/pennyfarthing-dist/commands/parallel-work.md +4 -2
- package/pennyfarthing-dist/commands/patch.md +210 -0
- package/pennyfarthing-dist/commands/pm.md +2 -0
- package/pennyfarthing-dist/commands/reviewer.md +2 -0
- package/pennyfarthing-dist/commands/sm.md +2 -0
- package/pennyfarthing-dist/commands/tea.md +2 -0
- package/pennyfarthing-dist/commands/tech-writer.md +2 -0
- package/pennyfarthing-dist/commands/ux-designer.md +2 -0
- package/pennyfarthing-dist/commands/work.md +2 -0
- package/pennyfarthing-dist/guides/agent-behavior.md +29 -264
- package/pennyfarthing-dist/guides/session-schema.md +346 -0
- package/pennyfarthing-dist/guides/skill-schema.md +412 -0
- package/pennyfarthing-dist/guides/workflow-step-schema.md +512 -0
- package/pennyfarthing-dist/guides/xml-tags.md +292 -0
- package/pennyfarthing-dist/scripts/core/agent-session.sh +7 -0
- package/pennyfarthing-dist/scripts/core/check-context.sh +140 -226
- package/pennyfarthing-dist/scripts/core/handoff-marker.sh +13 -2
- package/pennyfarthing-dist/scripts/git/worktree-manager.sh +4 -1
- package/pennyfarthing-dist/scripts/health/drift-detection.sh +1 -7
- package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +43 -8
- package/pennyfarthing-dist/scripts/hooks/post-merge.sh +4 -11
- package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +3 -8
- package/pennyfarthing-dist/scripts/hooks/pre-push.sh +3 -3
- package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +30 -0
- package/pennyfarthing-dist/scripts/jira/create-jira-epic.sh +1 -7
- package/pennyfarthing-dist/scripts/jira/create-jira-story.sh +2 -8
- package/pennyfarthing-dist/scripts/jira/jira-reconcile.sh +2 -8
- package/pennyfarthing-dist/scripts/lib/find-root.sh +41 -44
- package/pennyfarthing-dist/scripts/maintenance/sidecar-health.sh +1 -7
- package/pennyfarthing-dist/scripts/sprint/archive-story.sh +2 -8
- package/pennyfarthing-dist/scripts/sprint/available-stories.sh +2 -8
- package/pennyfarthing-dist/scripts/sprint/check-story.sh +2 -8
- package/pennyfarthing-dist/scripts/sprint/get-epic-field.sh +2 -8
- package/pennyfarthing-dist/scripts/sprint/get-story-field.sh +2 -8
- package/pennyfarthing-dist/scripts/sprint/list-future.sh +2 -8
- package/pennyfarthing-dist/scripts/sprint/new-sprint.sh +2 -8
- package/pennyfarthing-dist/scripts/sprint/promote-epic.sh +2 -8
- package/pennyfarthing-dist/scripts/sprint/sprint-info.sh +2 -8
- package/pennyfarthing-dist/scripts/tests/test-character-voice.sh +2 -1
- package/pennyfarthing-dist/scripts/workflow/finish-story.sh +4 -9
- package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +2 -8
- package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +2 -8
- package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +1 -7
- package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +2 -8
- package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +2 -8
- package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +2 -8
- package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +2 -8
- package/pennyfarthing-dist/skills/agentic-patterns/SKILL.md +4 -0
- package/pennyfarthing-dist/skills/changelog/SKILL.md +18 -0
- package/pennyfarthing-dist/skills/code-review/SKILL.md +5 -1
- package/pennyfarthing-dist/skills/context-engineering/SKILL.md +3 -0
- package/pennyfarthing-dist/skills/cyclist/SKILL.md +2 -2
- package/pennyfarthing-dist/skills/dev-patterns/SKILL.md +25 -1
- package/pennyfarthing-dist/skills/finalize-run/SKILL.md +3 -0
- package/pennyfarthing-dist/skills/jira/SKILL.md +48 -24
- package/pennyfarthing-dist/skills/judge/SKILL.md +8 -0
- package/pennyfarthing-dist/skills/just/SKILL.md +11 -0
- package/pennyfarthing-dist/skills/mermaid/SKILL.md +16 -0
- package/pennyfarthing-dist/skills/otel/skill.md +4 -0
- package/pennyfarthing-dist/skills/permissions/skill.md +3 -0
- package/pennyfarthing-dist/skills/persona-benchmark/SKILL.md +9 -0
- package/pennyfarthing-dist/skills/sprint/scripts/sync-epic-jira.sh +7 -0
- package/pennyfarthing-dist/skills/sprint/skill.md +30 -30
- package/pennyfarthing-dist/skills/story/skill.md +16 -16
- package/pennyfarthing-dist/skills/systematic-debugging/SKILL.md +56 -0
- package/pennyfarthing-dist/skills/testing/SKILL.md +22 -0
- package/pennyfarthing-dist/skills/theme/skill.md +12 -0
- package/pennyfarthing-dist/skills/theme-creation/SKILL.md +4 -0
- package/pennyfarthing-dist/skills/workflow/skill.md +22 -14
- package/pennyfarthing-dist/skills/yq/SKILL.md +8 -0
- package/pennyfarthing-dist/templates/settings.local.json.template +9 -0
- package/pennyfarthing-dist/workflows/architecture/steps/step-01-initialize.md +12 -0
- package/pennyfarthing-dist/workflows/architecture/steps/step-01b-continue.md +12 -0
- package/pennyfarthing-dist/workflows/architecture/steps/step-02-context.md +12 -0
- package/pennyfarthing-dist/workflows/architecture/steps/step-03-patterns.md +12 -0
- package/pennyfarthing-dist/workflows/architecture/steps/step-04-components.md +12 -0
- package/pennyfarthing-dist/workflows/architecture/steps/step-05-interfaces.md +12 -0
- package/pennyfarthing-dist/workflows/architecture/steps/step-06-risks.md +12 -0
- package/pennyfarthing-dist/workflows/architecture/steps/step-07-document.md +12 -0
- package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-01-validate-prerequisites.md +25 -0
- package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-02-design-epics.md +23 -0
- package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-03-create-stories.md +26 -0
- package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-04-final-validation.md +24 -0
- package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-05-import-to-future.md +23 -0
- package/pennyfarthing-dist/workflows/git-cleanup/steps/step-01-analyze.md +43 -41
- package/pennyfarthing-dist/workflows/git-cleanup/steps/step-02-categorize.md +50 -19
- package/pennyfarthing-dist/workflows/git-cleanup/steps/step-03-execute.md +102 -111
- package/pennyfarthing-dist/workflows/git-cleanup/steps/step-04-verify.md +48 -39
- package/pennyfarthing-dist/workflows/git-cleanup/steps/step-05-complete.md +30 -31
- package/pennyfarthing-dist/workflows/implementation-readiness/steps/step-01-document-discovery.md +21 -0
- package/pennyfarthing-dist/workflows/implementation-readiness/steps/step-02-prd-analysis.md +21 -0
- package/pennyfarthing-dist/workflows/implementation-readiness/steps/step-03-epic-coverage-validation.md +23 -0
- package/pennyfarthing-dist/workflows/implementation-readiness/steps/step-04-ux-alignment.md +23 -0
- package/pennyfarthing-dist/workflows/implementation-readiness/steps/step-05-epic-quality-review.md +28 -0
- package/pennyfarthing-dist/workflows/implementation-readiness/steps/step-06-final-assessment.md +25 -0
- package/pennyfarthing-dist/workflows/interactive-debug/steps/step-01-connect.md +257 -0
- package/pennyfarthing-dist/workflows/interactive-debug/steps/step-02-explore.md +107 -0
- package/pennyfarthing-dist/workflows/interactive-debug/steps/step-03-fix.md +127 -0
- package/pennyfarthing-dist/workflows/interactive-debug/steps/step-04-commit.md +122 -0
- package/pennyfarthing-dist/workflows/interactive-debug/workflow.yaml +51 -0
- package/pennyfarthing-dist/workflows/patch.yaml +68 -0
- package/pennyfarthing-dist/workflows/prd/steps-c/step-01-init.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-c/step-01b-continue.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-c/step-02-discovery.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-c/step-03-success.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-c/step-04-journeys.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-c/step-05-domain.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-c/step-06-innovation.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-c/step-07-project-type.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-c/step-08-scoping.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-c/step-09-functional.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-c/step-10-nonfunctional.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-c/step-11-polish.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-c/step-12-complete.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-e/step-e-01-discovery.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-e/step-e-01b-legacy-conversion.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-e/step-e-02-review.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-e/step-e-03-edit.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-e/step-e-04-complete.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-v/step-v-01-discovery.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-v/step-v-02-format-detection.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-v/step-v-02b-parity-check.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-v/step-v-03-density-validation.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-v/step-v-04-brief-coverage-validation.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-v/step-v-05-measurability-validation.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-v/step-v-06-traceability-validation.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-v/step-v-07-implementation-leakage-validation.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-v/step-v-08-domain-compliance-validation.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-v/step-v-09-project-type-validation.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-v/step-v-10-smart-validation.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-v/step-v-11-holistic-quality-validation.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-v/step-v-12-completeness-validation.md +6 -0
- package/pennyfarthing-dist/workflows/prd/steps-v/step-v-13-report-complete.md +6 -0
- package/pennyfarthing-dist/workflows/product-brief/steps/step-01-init.md +18 -0
- package/pennyfarthing-dist/workflows/product-brief/steps/step-01b-continue.md +19 -0
- package/pennyfarthing-dist/workflows/product-brief/steps/step-02-vision.md +22 -0
- package/pennyfarthing-dist/workflows/product-brief/steps/step-03-users.md +22 -0
- package/pennyfarthing-dist/workflows/product-brief/steps/step-04-metrics.md +23 -0
- package/pennyfarthing-dist/workflows/product-brief/steps/step-05-scope.md +24 -0
- package/pennyfarthing-dist/workflows/product-brief/steps/step-06-complete.md +22 -0
- package/pennyfarthing-dist/workflows/project-context/steps/step-01-discover.md +22 -0
- package/pennyfarthing-dist/workflows/project-context/steps/step-02-generate.md +31 -0
- package/pennyfarthing-dist/workflows/project-context/steps/step-03-complete.md +28 -0
- package/pennyfarthing-dist/workflows/quick-dev/steps/step-01-mode-detection.md +21 -0
- package/pennyfarthing-dist/workflows/quick-dev/steps/step-02-context-gathering.md +23 -0
- package/pennyfarthing-dist/workflows/quick-dev/steps/step-03-execute.md +25 -0
- package/pennyfarthing-dist/workflows/quick-dev/steps/step-04-self-check.md +22 -0
- package/pennyfarthing-dist/workflows/quick-dev/steps/step-05-adversarial-review.md +23 -0
- package/pennyfarthing-dist/workflows/quick-dev/steps/step-06-resolve-findings.md +23 -0
- package/pennyfarthing-dist/workflows/quick-spec/steps/step-01-understand.md +12 -0
- package/pennyfarthing-dist/workflows/quick-spec/steps/step-02-investigate.md +12 -0
- package/pennyfarthing-dist/workflows/quick-spec/steps/step-03-generate.md +12 -0
- package/pennyfarthing-dist/workflows/quick-spec/steps/step-04-review.md +12 -0
- package/pennyfarthing-dist/workflows/research/steps-domain/step-01-init.md +22 -0
- package/pennyfarthing-dist/workflows/research/steps-domain/step-02-domain-analysis.md +24 -0
- package/pennyfarthing-dist/workflows/research/steps-domain/step-03-competitive-landscape.md +25 -0
- package/pennyfarthing-dist/workflows/research/steps-domain/step-04-regulatory-focus.md +26 -0
- package/pennyfarthing-dist/workflows/research/steps-domain/step-05-technical-trends.md +26 -0
- package/pennyfarthing-dist/workflows/research/steps-domain/step-06-research-synthesis.md +34 -0
- package/pennyfarthing-dist/workflows/research/steps-market/step-01-init.md +23 -0
- package/pennyfarthing-dist/workflows/research/steps-market/step-02-customer-behavior.md +25 -0
- package/pennyfarthing-dist/workflows/research/steps-market/step-02-customer-insights.md +27 -0
- package/pennyfarthing-dist/workflows/research/steps-market/step-03-customer-pain-points.md +26 -0
- package/pennyfarthing-dist/workflows/research/steps-market/step-04-customer-decisions.md +27 -0
- package/pennyfarthing-dist/workflows/research/steps-market/step-05-competitive-analysis.md +26 -0
- package/pennyfarthing-dist/workflows/research/steps-market/step-06-research-completion.md +35 -0
- package/pennyfarthing-dist/workflows/research/steps-technical/step-01-init.md +22 -0
- package/pennyfarthing-dist/workflows/research/steps-technical/step-02-technical-overview.md +25 -0
- package/pennyfarthing-dist/workflows/research/steps-technical/step-03-integration-patterns.md +26 -0
- package/pennyfarthing-dist/workflows/research/steps-technical/step-04-architectural-patterns.md +26 -0
- package/pennyfarthing-dist/workflows/research/steps-technical/step-05-implementation-research.md +29 -1
- package/pennyfarthing-dist/workflows/research/steps-technical/step-06-research-synthesis.md +37 -1
- package/pennyfarthing-dist/workflows/sprint-planning/steps/step-01-parse-epic-files.md +15 -0
- package/pennyfarthing-dist/workflows/sprint-planning/steps/step-02-build-sprint-status.md +17 -0
- package/pennyfarthing-dist/workflows/sprint-planning/steps/step-03-status-detection.md +16 -0
- package/pennyfarthing-dist/workflows/sprint-planning/steps/step-04-generate-status-file.md +17 -0
- package/pennyfarthing-dist/workflows/sprint-planning/steps/step-05-validate-and-report.md +22 -0
- package/pennyfarthing-dist/workflows/ux-design/steps/step-01-init.md +6 -0
- package/pennyfarthing-dist/workflows/ux-design/steps/step-01b-continue.md +6 -0
- package/pennyfarthing-dist/workflows/ux-design/steps/step-02-discovery.md +6 -0
- package/pennyfarthing-dist/workflows/ux-design/steps/step-03-core-experience.md +6 -0
- package/pennyfarthing-dist/workflows/ux-design/steps/step-04-emotional-response.md +6 -0
- package/pennyfarthing-dist/workflows/ux-design/steps/step-05-inspiration.md +6 -0
- package/pennyfarthing-dist/workflows/ux-design/steps/step-06-design-system.md +6 -0
- package/pennyfarthing-dist/workflows/ux-design/steps/step-07-defining-experience.md +6 -0
- package/pennyfarthing-dist/workflows/ux-design/steps/step-08-visual-foundation.md +6 -0
- package/pennyfarthing-dist/workflows/ux-design/steps/step-09-design-directions.md +6 -0
- package/pennyfarthing-dist/workflows/ux-design/steps/step-10-user-journeys.md +6 -0
- package/pennyfarthing-dist/workflows/ux-design/steps/step-11-component-strategy.md +6 -0
- package/pennyfarthing-dist/workflows/ux-design/steps/step-12-ux-patterns.md +6 -0
- package/pennyfarthing-dist/workflows/ux-design/steps/step-13-responsive-accessibility.md +6 -0
- package/pennyfarthing-dist/workflows/ux-design/steps/step-14-complete.md +6 -0
- package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/output.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/context.py +414 -0
- package/pennyfarthing_scripts/migration/__init__.py +39 -0
- package/pennyfarthing_scripts/migration/__main__.py +10 -0
- package/pennyfarthing_scripts/migration/cli.py +304 -0
- package/pennyfarthing_scripts/migration/session.py +384 -0
- package/pennyfarthing_scripts/migration/skill.py +188 -0
- package/pennyfarthing_scripts/migration/step.py +229 -0
- package/pennyfarthing_scripts/migration/validate.py +282 -0
- package/pennyfarthing_scripts/patch_mode.py +449 -0
- package/pennyfarthing_scripts/prime/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/loader.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/session.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/tiers.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/cli.py +201 -0
- package/pennyfarthing_scripts/prime/models.py +9 -0
- package/pennyfarthing_scripts/prime/persona.py +41 -0
- package/pennyfarthing_scripts/prime/tiers.py +201 -0
- package/pennyfarthing_scripts/schema_validation_hook.py +306 -0
- package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/status.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/archive_epic.py +399 -0
- package/pennyfarthing_scripts/sprint/cli.py +100 -0
- package/pennyfarthing_scripts/sprint/import_epic.py +431 -0
- package/pennyfarthing_scripts/story/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/create.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/size.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/template.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/tests/test_patch_mode.py +830 -0
- package/pennyfarthing_scripts/tests/test_tiers.py +1090 -0
- package/pennyfarthing_scripts/tests/test_token_counting.py +559 -0
- package/pennyfarthing-dist/scripts/hooks/__pycache__/question_reflector_check.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/scripts/sprint/import-epic-to-future.sh +0 -10
- package/pennyfarthing-dist/scripts/sprint/import_epic_to_future.py +0 -270
- package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/config.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_bidirectional_sync.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_epic_creation.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_sync.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_sync_story.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/output.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/sprint.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/workflow.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/discover.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/compat.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/mappings.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/story.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/finish.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_brownfield.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_cli_modules.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_common.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_git_utils.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_jira_package.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_package_structure.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_prime.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_sprint_package.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_story_package.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
- 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
|