@pennyfarthing/core 11.4.0 → 11.5.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/packages/core/dist/cli/commands/cyclist.js +1 -1
- package/packages/core/dist/cli/commands/cyclist.js.map +1 -1
- package/packages/core/dist/cli/commands/cyclist.test.js +3 -3
- package/packages/core/dist/cli/commands/cyclist.test.js.map +1 -1
- package/packages/core/dist/cli/commands/doctor-persona-config-false-negative.test.d.ts +22 -0
- package/packages/core/dist/cli/commands/doctor-persona-config-false-negative.test.d.ts.map +1 -0
- package/packages/core/dist/cli/commands/doctor-persona-config-false-negative.test.js +161 -0
- package/packages/core/dist/cli/commands/doctor-persona-config-false-negative.test.js.map +1 -0
- package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/doctor.js +101 -33
- 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 +33 -13
- package/packages/core/dist/cli/commands/init.js.map +1 -1
- package/packages/core/dist/cli/commands/stale-artifacts-cleanup.test.d.ts +17 -0
- package/packages/core/dist/cli/commands/stale-artifacts-cleanup.test.d.ts.map +1 -0
- package/packages/core/dist/cli/commands/stale-artifacts-cleanup.test.js +470 -0
- package/packages/core/dist/cli/commands/stale-artifacts-cleanup.test.js.map +1 -0
- package/packages/core/dist/cli/commands/update.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/update.js +68 -17
- package/packages/core/dist/cli/commands/update.js.map +1 -1
- package/packages/core/dist/cli/cyclist-migration.test.d.ts +16 -0
- package/packages/core/dist/cli/cyclist-migration.test.d.ts.map +1 -0
- package/packages/core/dist/cli/cyclist-migration.test.js +229 -0
- package/packages/core/dist/cli/cyclist-migration.test.js.map +1 -0
- package/packages/core/dist/cli/index.js +2 -21
- package/packages/core/dist/cli/index.js.map +1 -1
- package/packages/core/dist/cli/utils/node-modules.d.ts +7 -0
- package/packages/core/dist/cli/utils/node-modules.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/node-modules.js +39 -0
- package/packages/core/dist/cli/utils/node-modules.js.map +1 -1
- package/packages/core/dist/cli/utils/settings.d.ts +1 -1
- package/packages/core/dist/cli/utils/settings.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/settings.js +24 -21
- package/packages/core/dist/cli/utils/settings.js.map +1 -1
- package/packages/core/dist/cli/utils/stale-artifacts.d.ts +59 -0
- package/packages/core/dist/cli/utils/stale-artifacts.d.ts.map +1 -0
- package/packages/core/dist/cli/utils/stale-artifacts.js +163 -0
- package/packages/core/dist/cli/utils/stale-artifacts.js.map +1 -0
- package/packages/core/dist/public/css/react.css +1 -1
- package/packages/core/dist/public/js/react/react.js +39 -39
- package/packages/core/dist/scripts/benchmark-integration.d.ts +182 -0
- package/packages/core/dist/scripts/benchmark-integration.d.ts.map +1 -0
- package/packages/core/dist/scripts/benchmark-integration.js +691 -0
- package/packages/core/dist/scripts/benchmark-integration.js.map +1 -0
- package/packages/core/dist/scripts/job-fair-aggregator.d.ts +150 -0
- package/packages/core/dist/scripts/job-fair-aggregator.d.ts.map +1 -0
- package/packages/core/dist/scripts/job-fair-aggregator.js +547 -0
- package/packages/core/dist/scripts/job-fair-aggregator.js.map +1 -0
- package/packages/core/dist/scripts/theme-detail.test.d.ts.map +1 -0
- package/packages/core/dist/scripts/theme-detail.test.js.map +1 -0
- package/packages/core/dist/server/api/context.d.ts.map +1 -1
- package/packages/core/dist/server/api/context.js +23 -5
- package/packages/core/dist/server/api/context.js.map +1 -1
- package/packages/core/dist/server/api/index.d.ts +0 -1
- package/packages/core/dist/server/api/index.d.ts.map +1 -1
- package/packages/core/dist/server/api/index.js +0 -2
- package/packages/core/dist/server/api/index.js.map +1 -1
- package/packages/core/dist/server/api/settings.d.ts.map +1 -1
- package/packages/core/dist/server/api/settings.js +6 -0
- package/packages/core/dist/server/api/settings.js.map +1 -1
- package/packages/core/dist/server/otlp-receiver.d.ts +0 -4
- package/packages/core/dist/server/otlp-receiver.d.ts.map +1 -1
- package/packages/core/dist/server/otlp-receiver.js +0 -18
- package/packages/core/dist/server/otlp-receiver.js.map +1 -1
- package/packages/core/dist/server/otlp-receiver.test.js +0 -27
- package/packages/core/dist/server/otlp-receiver.test.js.map +1 -1
- package/packages/core/dist/server/server.d.ts +1 -1
- package/packages/core/dist/server/server.d.ts.map +1 -1
- package/packages/core/dist/server/server.js +2 -4
- package/packages/core/dist/server/server.js.map +1 -1
- package/packages/core/dist/workflow/cross-entity-validation.d.ts.map +1 -1
- package/packages/core/dist/workflow/cross-entity-validation.js.map +1 -1
- package/packages/core/src/public/App.tsx +0 -2
- package/packages/core/src/public/components/BikeRackWorkspace.tsx +0 -2
- package/packages/core/src/public/components/DockviewWorkspace.tsx +0 -4
- package/packages/core/src/public/components/StandalonePanel.tsx +0 -2
- package/packages/core/src/public/components/panels/SettingsPanel.tsx +83 -283
- package/packages/core/src/public/components/panels/SprintPanel.tsx +9 -1
- package/packages/core/src/public/components/panels/index.ts +0 -1
- package/packages/core/src/public/hooks/index.ts +0 -3
- package/packages/core/src/public/hooks/useSprint.ts +11 -2
- package/packages/core/src/public/images/cyclist-tandem-source.png +0 -0
- package/packages/core/src/public/styles/tailwind.css +0 -101
- package/pennyfarthing-dist/guides/bikerack.md +94 -0
- package/pennyfarthing-dist/personas/themes/firefly.yaml +12 -12
- package/pennyfarthing-dist/pf/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/bellmode_hook.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/config.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/context.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/context.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/hooks.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/jira.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/jira_bidirectional_sync.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/jira_epic_creation.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/jira_sync.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/jira_sync_story.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/output.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/patch_mode.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/pretooluse_hook.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/schema_validation_hook.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/session_start_hook.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/sprint.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/workflow.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bc/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/bc/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bc/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/bc/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bc/__pycache__/focus.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/bc/__pycache__/focus.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bc/__pycache__/split.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bc/cli.py +0 -1
- package/pennyfarthing-dist/pf/bc/focus.py +0 -1
- package/pennyfarthing-dist/pf/bikerack/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/audit_log_panel.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/background_panel.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/changed_panel.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/context_meter_footer.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/debug_panel.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/diffs_panel.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/events.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/git_panel.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/launcher.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/portrait_resolver.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/progress_panel.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/story_detail_data.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/story_detail_screen.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/story_detail_widget.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/ws_client.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/context_meter_footer.py +115 -35
- package/pennyfarthing-dist/pf/bikerack/portrait_resolver.py +14 -2
- package/pennyfarthing-dist/pf/bikerack/progress_panel.py +54 -0
- package/pennyfarthing-dist/pf/bikerack/sprint_panel.py +58 -0
- package/pennyfarthing-dist/pf/bikerack/story_detail_screen.py +10 -105
- package/pennyfarthing-dist/pf/bikerack/story_detail_widget.py +167 -0
- package/pennyfarthing-dist/pf/bikerack/tui.py +140 -50
- package/pennyfarthing-dist/pf/bmad/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bmad/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bmad/cli.py +0 -1
- package/pennyfarthing-dist/pf/bmad/importer.py +0 -1
- package/pennyfarthing-dist/pf/bmad/parser.py +0 -1
- package/pennyfarthing-dist/pf/bmad/sync.py +1 -3
- package/pennyfarthing-dist/pf/bmad/test_parser.py +0 -4
- package/pennyfarthing-dist/pf/bmad/test_sync.py +0 -3
- package/pennyfarthing-dist/pf/brownfield/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/brownfield/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/brownfield/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/brownfield/__pycache__/discover.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/brownfield/cli.py +1 -1
- package/pennyfarthing-dist/pf/cli.py +145 -111
- package/pennyfarthing-dist/pf/codemarkers/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/codemarkers/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/codemarkers/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/codemarkers/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/codemarkers/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/codemarkers/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/common/__init__.py +2 -0
- package/pennyfarthing-dist/pf/common/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/common/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/common/__pycache__/config.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/common/__pycache__/config.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/common/__pycache__/output.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/common/__pycache__/output.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/common/__pycache__/pr_config.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/common/__pycache__/themes.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/common/config.py +43 -0
- package/pennyfarthing-dist/pf/common/themes.py +7 -5
- package/pennyfarthing-dist/pf/complexity/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/complexity/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/complexity/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/complexity/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/complexity/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/complexity/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/consultation/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/consultation/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/consultation/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/consultation/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/consultation/__pycache__/dialogue_manager.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/context.py +1 -1
- package/pennyfarthing-dist/pf/deadcode/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/deadcode/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/deadcode/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/deadcode/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/deadcode/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/deadcode/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/deadcode/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/deadcode/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/dependencies/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/dependencies/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/dependencies/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/dependencies/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/dependencies/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/dependencies/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/epic/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/epic/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/epic/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/epic/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/git/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/git/__pycache__/create_branches.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/git/__pycache__/status_all.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/git/hooks_installer.py +7 -6
- package/pennyfarthing-dist/pf/git_group/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/git_group/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/git_group/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/git_group/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/handoff/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/handoff/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/handoff/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/handoff/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/handoff/__pycache__/complete_phase.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/handoff/__pycache__/gate_file.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/handoff/__pycache__/gate_runner.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/handoff/__pycache__/marker.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/handoff/__pycache__/phase_check.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/handoff/gate_file.py +5 -1
- package/pennyfarthing-dist/pf/healthscore/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/healthscore/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/healthscore/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/healthscore/__pycache__/analyze.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/healthscore/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/healthscore/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/healthscore/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/healthscore/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/healthscore/__pycache__/models.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/healthscore/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/bell_mode.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/bell_mode.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/context_breaker.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/context_breaker.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/context_warning.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/context_warning.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/cyclist_pretooluse.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/cyclist_pretooluse.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/pre_edit_check.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/pre_edit_check.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/reflector_check.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/reflector_check.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/schema_validation.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/schema_validation.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/session_start.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/session_start.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/session_stop.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/sprint_yaml_validation.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/sprint_yaml_validation.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/statusline.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/statusline.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/statusline.py +11 -0
- package/pennyfarthing-dist/pf/hotspots/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/hotspots/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hotspots/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hotspots/__pycache__/analyze.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/hotspots/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hotspots/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/hotspots/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hotspots/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hotspots/__pycache__/models.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/hotspots/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__init__.py +0 -6
- package/pennyfarthing-dist/pf/jira/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/bidirectional.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/claim.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/claim.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/client.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/client.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/compat.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/create.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/create.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/epic.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/epic.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/mappings.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/operations.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/operations.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/reconcile.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/reconcile.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/story.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/story.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/sync.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/sync.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/claim.py +14 -44
- package/pennyfarthing-dist/pf/jira/cli.py +124 -14
- package/pennyfarthing-dist/pf/jira/client.py +51 -87
- package/pennyfarthing-dist/pf/jira/reconcile.py +1 -1
- package/pennyfarthing-dist/pf/jira/story.py +6 -4
- package/pennyfarthing-dist/pf/jira/sync.py +2 -2
- package/pennyfarthing-dist/pf/launch/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/launch/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/launch/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/launch/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/migration/__init__.py +1 -1
- package/pennyfarthing-dist/pf/migration/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/migration/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/migration/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/migration/__pycache__/session.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/migration/__pycache__/skill.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/migration/__pycache__/step.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/migration/__pycache__/validate.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/migration/cli.py +0 -1
- package/pennyfarthing-dist/pf/package/__init__.py +0 -0
- package/pennyfarthing-dist/pf/package/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/package/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/package/__pycache__/discovery.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/package/__pycache__/portraits.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/package/cli.py +186 -0
- package/pennyfarthing-dist/pf/package/discovery.py +130 -0
- package/pennyfarthing-dist/pf/package/portraits.py +243 -0
- package/pennyfarthing-dist/pf/preflight/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/preflight/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/preflight/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/preflight/__pycache__/finish.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/prime/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/prime/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/prime/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/prime/__pycache__/loader.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/prime/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/prime/__pycache__/persona.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/prime/__pycache__/session.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/prime/__pycache__/tiers.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/prime/__pycache__/version_sentinel.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/prime/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/prime/loader.py +21 -7
- package/pennyfarthing-dist/pf/prime/workflow.py +11 -3
- package/pennyfarthing-dist/pf/release/__init__.py +0 -0
- package/pennyfarthing-dist/pf/release/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/release/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/release/__pycache__/deprecate.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/release/__pycache__/dry_run.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/release/cli.py +112 -0
- package/pennyfarthing-dist/pf/release/deprecate.py +187 -0
- package/pennyfarthing-dist/pf/release/dry_run.py +187 -0
- package/pennyfarthing-dist/pf/session/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/session/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/session/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/session/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/settings/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/settings/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/settings/__pycache__/settings.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/archive.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/archive.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/archive_epic.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/epic_add.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/epic_update.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/epic_update.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/import_epic.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/loader.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/loader.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/shard_merge.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/status.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/status.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/story_add.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/story_add.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/story_update.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/validate_cmd.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/validate_cmd.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/validator.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/validator.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/work.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/work.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/yaml_io.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/yaml_io.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/cli.py +1 -1
- package/pennyfarthing-dist/pf/sprint/loader.py +6 -74
- package/pennyfarthing-dist/pf/sprint/shard_merge.py +126 -0
- package/pennyfarthing-dist/pf/sprint/story_finish.py +4 -2
- package/pennyfarthing-dist/pf/sprint/yaml_io.py +8 -53
- package/pennyfarthing-dist/pf/story/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/story/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/story/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/story/__pycache__/create.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/story/__pycache__/size.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/story/__pycache__/template.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_108_1_gate_migration.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_archive_epic.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_bc.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_bikerack.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_brownfield.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_cli_modules.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_cli_normalization.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_codemarkers.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_common.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_confidence_sm_evaluation.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_confidence_sm_gate.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_dialogue_manager.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_epic_shard_validation.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_git_utils.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_handoff_cli.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_handoff_e2e.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_healthscore.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_jira_package.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_package_structure.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_patch_mode.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_prime.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_sprint_package.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_sprint_panel.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_story_add.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_story_package.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_story_update.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_tiers.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_token_counting.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_topology_loader.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_tui_focus.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_tui_panel_persistence.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_validate_cmd.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_version_sentinel.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_workflow_cli.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_yaml_io.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/test_cli_modules.py +32 -137
- package/pennyfarthing-dist/pf/tests/test_codemarkers.py +0 -15
- package/pennyfarthing-dist/pf/tests/test_dist_root.py +720 -0
- package/pennyfarthing-dist/pf/tests/test_package_structure.py +24 -70
- package/pennyfarthing-dist/pf/theme/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/theme/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/theme/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/theme/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/validate/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/validate/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/validate/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/validate/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/validate/adapters/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/validate/adapters/__pycache__/agent.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/validate/adapters/__pycache__/schema.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/validate/adapters/__pycache__/skill_command.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/validate/adapters/__pycache__/sprint.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/validate/adapters/__pycache__/tandem_awareness.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/validate/adapters/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/validate/adapters/agent.py +11 -1
- package/pennyfarthing-dist/pf/validate/adapters/skill_command.py +15 -4
- package/pennyfarthing-dist/pf/validate/adapters/tandem_awareness.py +7 -1
- package/pennyfarthing-dist/pf/validate/adapters/team_mode.py +8 -2
- package/pennyfarthing-dist/pf/validate/adapters/workflow.py +12 -2
- package/pennyfarthing-dist/pf/workflow/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/workflow/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/workflow/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/workflow/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/workflow/__pycache__/helpers.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/workflow/__pycache__/scale.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/workflow/__pycache__/scale.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/workflow/__pycache__/state.cpython-311.pyc +0 -0
- package/pennyfarthing-dist/pf/workflow/__pycache__/state.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pyproject.toml +1 -1
- package/pennyfarthing-dist/scripts/core/check-context.sh +2 -2
- package/pennyfarthing-dist/scripts/git/changelog-links.sh +216 -0
- package/pennyfarthing-dist/scripts/hooks/__pycache__/question_reflector_check.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/scripts/lib/README.md +0 -1
- package/pennyfarthing-dist/scripts/portraits/generate-portraits.py +54 -0
- package/pennyfarthing-dist/scripts/portraits/generate-tandem-portraits.sh +7 -3
- package/pennyfarthing-dist/workflows/patch.yaml +5 -6
- package/pennyfarthing-dist/workflows/tdd-tandem.yaml +12 -0
- package/packages/core/dist/workflow/team-lifecycle.d.ts +0 -169
- package/packages/core/dist/workflow/team-lifecycle.d.ts.map +0 -1
- package/packages/core/dist/workflow/team-lifecycle.js +0 -217
- package/packages/core/dist/workflow/team-lifecycle.js.map +0 -1
- package/packages/core/dist/workflow/team-lifecycle.test.d.ts +0 -20
- package/packages/core/dist/workflow/team-lifecycle.test.d.ts.map +0 -1
- package/packages/core/dist/workflow/team-lifecycle.test.js +0 -966
- package/packages/core/dist/workflow/team-lifecycle.test.js.map +0 -1
- package/packages/core/src/public/components/FontPicker/FontPicker.css +0 -276
- package/packages/core/src/public/components/FontPicker/index.tsx +0 -430
- package/packages/core/src/public/components/ThemePalette/ThemePalette.css +0 -179
- package/packages/core/src/public/components/ThemePalette/index.tsx +0 -326
- package/packages/core/src/public/components/panels/BackgroundPanel.tsx +0 -115
- package/packages/core/src/public/components/ui/switch.tsx +0 -27
- package/packages/core/src/public/hooks/useBackgroundTasks.ts +0 -131
- package/pennyfarthing-dist/pf/bikerack/background_panel.py +0 -162
- package/pennyfarthing-dist/pf/bmad/__pycache__/parser.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/brownfield/__main__.py +0 -8
- package/pennyfarthing-dist/pf/config.py +0 -21
- package/pennyfarthing-dist/pf/hooks.py +0 -32
- package/pennyfarthing-dist/pf/jira_bidirectional_sync.py +0 -37
- package/pennyfarthing-dist/pf/jira_epic_creation.py +0 -30
- package/pennyfarthing-dist/pf/jira_sync.py +0 -36
- package/pennyfarthing-dist/pf/jira_sync_story.py +0 -30
- package/pennyfarthing-dist/pf/migration/__main__.py +0 -10
- package/pennyfarthing-dist/pf/output.py +0 -37
- package/pennyfarthing-dist/pf/theme/__main__.py +0 -6
- package/pennyfarthing-dist/scripts/lib/background-tasks.sh +0 -177
|
@@ -122,76 +122,6 @@ def is_jira_cli_available() -> bool:
|
|
|
122
122
|
return shutil.which("jira") is not None
|
|
123
123
|
|
|
124
124
|
|
|
125
|
-
def get_issue(issue_key: str) -> dict[str, Any] | None:
|
|
126
|
-
"""Fetch issue details from Jira.
|
|
127
|
-
|
|
128
|
-
Args:
|
|
129
|
-
issue_key: Jira issue key (e.g., "MSSCI-12398")
|
|
130
|
-
|
|
131
|
-
Returns:
|
|
132
|
-
Issue data as dict, or None if not found
|
|
133
|
-
"""
|
|
134
|
-
if not is_jira_cli_available():
|
|
135
|
-
return None
|
|
136
|
-
|
|
137
|
-
result = subprocess.run(
|
|
138
|
-
["jira", "issue", "view", issue_key, "--raw"],
|
|
139
|
-
capture_output=True,
|
|
140
|
-
text=True,
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
if result.returncode != 0:
|
|
144
|
-
return None
|
|
145
|
-
|
|
146
|
-
try:
|
|
147
|
-
return json.loads(result.stdout)
|
|
148
|
-
except json.JSONDecodeError:
|
|
149
|
-
return None
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
def update_issue_status(issue_key: str, status: str) -> bool:
|
|
153
|
-
"""Transition an issue to a new status.
|
|
154
|
-
|
|
155
|
-
Args:
|
|
156
|
-
issue_key: Jira issue key
|
|
157
|
-
status: Target status name
|
|
158
|
-
|
|
159
|
-
Returns:
|
|
160
|
-
True if successful, False otherwise
|
|
161
|
-
"""
|
|
162
|
-
if not is_jira_cli_available():
|
|
163
|
-
return False
|
|
164
|
-
|
|
165
|
-
result = subprocess.run(
|
|
166
|
-
["jira", "issue", "move", issue_key, status],
|
|
167
|
-
capture_output=True,
|
|
168
|
-
text=True,
|
|
169
|
-
)
|
|
170
|
-
|
|
171
|
-
return result.returncode == 0
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
def add_comment(issue_key: str, comment: str) -> bool:
|
|
175
|
-
"""Add a comment to an issue.
|
|
176
|
-
|
|
177
|
-
Args:
|
|
178
|
-
issue_key: Jira issue key
|
|
179
|
-
comment: Comment text
|
|
180
|
-
|
|
181
|
-
Returns:
|
|
182
|
-
True if successful, False otherwise
|
|
183
|
-
"""
|
|
184
|
-
if not is_jira_cli_available():
|
|
185
|
-
return False
|
|
186
|
-
|
|
187
|
-
result = subprocess.run(
|
|
188
|
-
["jira", "issue", "comment", "add", issue_key, "--body", comment],
|
|
189
|
-
capture_output=True,
|
|
190
|
-
text=True,
|
|
191
|
-
)
|
|
192
|
-
|
|
193
|
-
return result.returncode == 0
|
|
194
|
-
|
|
195
125
|
|
|
196
126
|
def get_jira_field(issue_json: dict[str, Any], field_path: str, default: Any = None) -> Any:
|
|
197
127
|
"""Extract field from Jira issue JSON using dot notation.
|
|
@@ -226,13 +156,13 @@ def get_story_points(issue_key: str, issue_json: dict[str, Any] | None = None) -
|
|
|
226
156
|
|
|
227
157
|
Args:
|
|
228
158
|
issue_key: Jira issue key
|
|
229
|
-
issue_json: Optional pre-fetched issue JSON
|
|
159
|
+
issue_json: Optional pre-fetched issue JSON (required if no default client)
|
|
230
160
|
|
|
231
161
|
Returns:
|
|
232
162
|
Story points as int, or None if not set
|
|
233
163
|
"""
|
|
234
164
|
if issue_json is None:
|
|
235
|
-
issue_json =
|
|
165
|
+
issue_json = get_client().get_issue_sync(issue_key)
|
|
236
166
|
if not issue_json:
|
|
237
167
|
return None
|
|
238
168
|
|
|
@@ -349,13 +279,11 @@ def get_current_user_email() -> str:
|
|
|
349
279
|
class JiraClient:
|
|
350
280
|
"""Unified Jira REST API client with sync and async support.
|
|
351
281
|
|
|
352
|
-
Consolidates REST API access
|
|
353
|
-
- jira_sync.py (httpx async)
|
|
354
|
-
- jira_epic_creation.py (curl subprocess)
|
|
282
|
+
Consolidates all Jira REST API access.
|
|
355
283
|
|
|
356
284
|
Usage (sync):
|
|
357
285
|
client = JiraClient()
|
|
358
|
-
issue = client.
|
|
286
|
+
issue = client.get_issue_sync("MSSCI-12345")
|
|
359
287
|
|
|
360
288
|
Usage (async):
|
|
361
289
|
client = JiraClient()
|
|
@@ -510,7 +438,7 @@ class JiraClient:
|
|
|
510
438
|
"GET", f"/rest/api/3/issue/{issue_key}/transitions"
|
|
511
439
|
)
|
|
512
440
|
if not transitions_data:
|
|
513
|
-
return {"success": False, "
|
|
441
|
+
return {"success": False, "error": "Could not get transitions"}
|
|
514
442
|
|
|
515
443
|
transitions = transitions_data.get("transitions", [])
|
|
516
444
|
transition_id = None
|
|
@@ -523,7 +451,7 @@ class JiraClient:
|
|
|
523
451
|
available = [t.get("name") for t in transitions]
|
|
524
452
|
return {
|
|
525
453
|
"success": False,
|
|
526
|
-
"
|
|
454
|
+
"error": f"No transition to '{target_status}' available. "
|
|
527
455
|
f"Available: {available}",
|
|
528
456
|
}
|
|
529
457
|
|
|
@@ -558,7 +486,7 @@ class JiraClient:
|
|
|
558
486
|
if not users or not isinstance(users, list) or len(users) == 0:
|
|
559
487
|
return {
|
|
560
488
|
"success": False,
|
|
561
|
-
"
|
|
489
|
+
"error": f"User not found: {assignee_email}",
|
|
562
490
|
}
|
|
563
491
|
account_id = users[0].get("accountId")
|
|
564
492
|
else:
|
|
@@ -571,6 +499,41 @@ class JiraClient:
|
|
|
571
499
|
# Assign PUT returns empty body on success (204)
|
|
572
500
|
return {"success": True}
|
|
573
501
|
|
|
502
|
+
def add_comment_sync(self, issue_key: str, comment: str) -> dict[str, Any]:
|
|
503
|
+
"""Add a comment to a Jira issue via REST API.
|
|
504
|
+
|
|
505
|
+
Args:
|
|
506
|
+
issue_key: Jira issue key
|
|
507
|
+
comment: Comment body text
|
|
508
|
+
|
|
509
|
+
Returns:
|
|
510
|
+
Result dict with success status
|
|
511
|
+
"""
|
|
512
|
+
payload = {
|
|
513
|
+
"body": {
|
|
514
|
+
"type": "doc",
|
|
515
|
+
"version": 1,
|
|
516
|
+
"content": [
|
|
517
|
+
{
|
|
518
|
+
"type": "paragraph",
|
|
519
|
+
"content": [{"type": "text", "text": comment}],
|
|
520
|
+
}
|
|
521
|
+
],
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
result = self._call_api_sync(
|
|
525
|
+
"POST",
|
|
526
|
+
f"/rest/api/3/issue/{issue_key}/comment",
|
|
527
|
+
payload,
|
|
528
|
+
)
|
|
529
|
+
if result is None:
|
|
530
|
+
# POST comment returns the created comment on success;
|
|
531
|
+
# _call_api_sync returns None on empty/failed response
|
|
532
|
+
# but also returns None on JSON decode failure for 201 with empty body.
|
|
533
|
+
# Treat None as success since the API may return 201 with no body.
|
|
534
|
+
pass
|
|
535
|
+
return {"success": True}
|
|
536
|
+
|
|
574
537
|
def add_to_sprint_sync(self, sprint_id: int | str, issue_key: str) -> dict[str, Any]:
|
|
575
538
|
"""Add issue to a sprint via Agile REST API.
|
|
576
539
|
|
|
@@ -609,9 +572,10 @@ class JiraClient:
|
|
|
609
572
|
|
|
610
573
|
encoded_jql = urllib.parse.quote(jql)
|
|
611
574
|
fields_param = ",".join(fields)
|
|
575
|
+
# Jira Cloud deprecated /rest/api/3/search — use /rest/api/3/search/jql
|
|
612
576
|
result = self._call_api_sync(
|
|
613
577
|
"GET",
|
|
614
|
-
f"/rest/api/3/search?jql={encoded_jql}&fields={fields_param}"
|
|
578
|
+
f"/rest/api/3/search/jql?jql={encoded_jql}&fields={fields_param}"
|
|
615
579
|
f"&maxResults={max_results}",
|
|
616
580
|
)
|
|
617
581
|
if not result:
|
|
@@ -680,7 +644,7 @@ class JiraClient:
|
|
|
680
644
|
target_status: Target status name (e.g., "In Progress", "Done")
|
|
681
645
|
|
|
682
646
|
Returns:
|
|
683
|
-
Result dict with success status and optional
|
|
647
|
+
Result dict with success status and optional error
|
|
684
648
|
"""
|
|
685
649
|
import httpx
|
|
686
650
|
|
|
@@ -694,7 +658,7 @@ class JiraClient:
|
|
|
694
658
|
if response.status_code != 200:
|
|
695
659
|
return {
|
|
696
660
|
"success": False,
|
|
697
|
-
"
|
|
661
|
+
"error": f"Could not get transitions: {response.status_code}",
|
|
698
662
|
}
|
|
699
663
|
|
|
700
664
|
transitions = response.json().get("transitions", [])
|
|
@@ -707,7 +671,7 @@ class JiraClient:
|
|
|
707
671
|
if not transition_id:
|
|
708
672
|
return {
|
|
709
673
|
"success": False,
|
|
710
|
-
"
|
|
674
|
+
"error": f"No transition to '{target_status}' available",
|
|
711
675
|
}
|
|
712
676
|
|
|
713
677
|
# Execute transition
|
|
@@ -722,11 +686,11 @@ class JiraClient:
|
|
|
722
686
|
return {"success": True}
|
|
723
687
|
return {
|
|
724
688
|
"success": False,
|
|
725
|
-
"
|
|
689
|
+
"error": f"Transition failed: {response.status_code}",
|
|
726
690
|
}
|
|
727
691
|
|
|
728
692
|
except httpx.HTTPError as e:
|
|
729
|
-
return {"success": False, "
|
|
693
|
+
return {"success": False, "error": str(e)}
|
|
730
694
|
|
|
731
695
|
async def update_fields_async(
|
|
732
696
|
self, issue_key: str, fields: dict[str, Any]
|
|
@@ -756,10 +720,10 @@ class JiraClient:
|
|
|
756
720
|
|
|
757
721
|
if response.status_code in (200, 204):
|
|
758
722
|
return {"success": True}
|
|
759
|
-
return {"success": False, "
|
|
723
|
+
return {"success": False, "error": f"HTTP {response.status_code}"}
|
|
760
724
|
|
|
761
725
|
except httpx.HTTPError as e:
|
|
762
|
-
return {"success": False, "
|
|
726
|
+
return {"success": False, "error": str(e)}
|
|
763
727
|
|
|
764
728
|
async def sync_story_points_async(
|
|
765
729
|
self,
|
|
@@ -88,7 +88,7 @@ def fetch_jira_issue(jira_key: str) -> dict[str, Any] | None:
|
|
|
88
88
|
Returns:
|
|
89
89
|
Issue JSON if found, None otherwise
|
|
90
90
|
"""
|
|
91
|
-
return jira_client.
|
|
91
|
+
return jira_client.get_client().get_issue_sync(jira_key)
|
|
92
92
|
|
|
93
93
|
|
|
94
94
|
def sync_story(
|
|
@@ -147,10 +147,11 @@ def sync_story(
|
|
|
147
147
|
if dry_run:
|
|
148
148
|
actions.append(f"[DRY-RUN] {action}")
|
|
149
149
|
else:
|
|
150
|
-
|
|
150
|
+
result = jira_client.get_client().transition_sync(jira_key, target_status)
|
|
151
|
+
if result.get("success"):
|
|
151
152
|
actions.append(action)
|
|
152
153
|
else:
|
|
153
|
-
errors.append(f"Failed to transition {jira_key}")
|
|
154
|
+
errors.append(f"Failed to transition {jira_key}: {result.get('error', 'unknown')}")
|
|
154
155
|
else:
|
|
155
156
|
actions.append(f"status already {current_jira_status}")
|
|
156
157
|
|
|
@@ -173,7 +174,8 @@ def sync_story(
|
|
|
173
174
|
if dry_run:
|
|
174
175
|
actions.append(f"[DRY-RUN] {action}")
|
|
175
176
|
else:
|
|
176
|
-
|
|
177
|
+
result = jira_client.get_client().add_comment_sync(jira_key, comment)
|
|
178
|
+
if result.get("success"):
|
|
177
179
|
actions.append(action)
|
|
178
180
|
else:
|
|
179
181
|
errors.append(f"Failed to add comment to {jira_key}")
|
|
@@ -150,7 +150,7 @@ async def sync_story(
|
|
|
150
150
|
if result.get("success"):
|
|
151
151
|
actions.append(f"transitioned: {current_status} -> {target_status}")
|
|
152
152
|
else:
|
|
153
|
-
actions.append(f"transition failed: {result.get('
|
|
153
|
+
actions.append(f"transition failed: {result.get('error')}")
|
|
154
154
|
|
|
155
155
|
# Sync points if requested
|
|
156
156
|
story_points = story.get("points")
|
|
@@ -167,7 +167,7 @@ async def sync_story(
|
|
|
167
167
|
else:
|
|
168
168
|
actions.append(f"synced points: {story_points}")
|
|
169
169
|
else:
|
|
170
|
-
actions.append(f"points sync failed: {result.get('
|
|
170
|
+
actions.append(f"points sync failed: {result.get('error')}")
|
|
171
171
|
|
|
172
172
|
return SyncResult(
|
|
173
173
|
story_id=story_id,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
Binary file
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""Package CLI — theme package installation and portrait downloads.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
pf package list # Show all theme packages with status
|
|
5
|
+
pf package install comedy # Install package + download portraits
|
|
6
|
+
pf package install-portraits comedy # Download portraits for installed package
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
import click
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@click.group()
|
|
18
|
+
def package():
|
|
19
|
+
"""Theme package management.
|
|
20
|
+
|
|
21
|
+
\b
|
|
22
|
+
Commands:
|
|
23
|
+
list - Show all theme packages with status
|
|
24
|
+
install - Install a theme package (npm + portraits)
|
|
25
|
+
install-portraits - Download portraits for installed packages
|
|
26
|
+
"""
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@package.command("list")
|
|
31
|
+
@click.option("--json", "output_json", is_flag=True, help="Output as JSON.")
|
|
32
|
+
def list_cmd(output_json: bool):
|
|
33
|
+
"""Show all theme packages with installation status.
|
|
34
|
+
|
|
35
|
+
Displays each of the 7 theme packages with their current state:
|
|
36
|
+
installed, not installed, and whether portraits are downloaded.
|
|
37
|
+
"""
|
|
38
|
+
from pf.package.discovery import get_package_status
|
|
39
|
+
|
|
40
|
+
statuses = get_package_status()
|
|
41
|
+
|
|
42
|
+
if output_json:
|
|
43
|
+
click.echo(json.dumps(statuses, indent=2))
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
click.echo("Theme Packages")
|
|
47
|
+
click.echo("=" * 50)
|
|
48
|
+
click.echo("")
|
|
49
|
+
|
|
50
|
+
for pkg in statuses:
|
|
51
|
+
name = pkg["name"]
|
|
52
|
+
if not pkg["installed"]:
|
|
53
|
+
status = "not installed"
|
|
54
|
+
elif pkg["portraits"]:
|
|
55
|
+
status = "installed + portraits"
|
|
56
|
+
else:
|
|
57
|
+
status = "installed (no portraits)"
|
|
58
|
+
click.echo(f" {name:<22} {status}")
|
|
59
|
+
|
|
60
|
+
click.echo("")
|
|
61
|
+
installed = sum(1 for p in statuses if p["installed"])
|
|
62
|
+
with_portraits = sum(1 for p in statuses if p["portraits"])
|
|
63
|
+
click.echo(f"{installed}/{len(statuses)} installed, {with_portraits} with portraits")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@package.command()
|
|
67
|
+
@click.argument("name")
|
|
68
|
+
@click.option("--skip-portraits", is_flag=True, help="Skip portrait download after install.")
|
|
69
|
+
@click.option("--all-sizes", is_flag=True, help="Download all portrait sizes (default: large only).")
|
|
70
|
+
@click.option("--dry-run", is_flag=True, help="Show what would be done without making changes.")
|
|
71
|
+
def install(name: str, skip_portraits: bool, all_sizes: bool, dry_run: bool):
|
|
72
|
+
"""Install a theme package and download its portraits.
|
|
73
|
+
|
|
74
|
+
Runs npm install for the package, then downloads portraits from
|
|
75
|
+
the GitHub monorepo via the gh CLI. Portraits are saved into the
|
|
76
|
+
package's node_modules directory where the portrait-resolver
|
|
77
|
+
already looks for them.
|
|
78
|
+
|
|
79
|
+
\b
|
|
80
|
+
Arguments:
|
|
81
|
+
NAME - Theme package name (comedy, literary, mythology-fantasy,
|
|
82
|
+
prestige-tv, realistic, scifi, superheroes)
|
|
83
|
+
"""
|
|
84
|
+
from pf.package.discovery import is_package_installed, npm_install
|
|
85
|
+
from pf.package.portraits import download_portraits
|
|
86
|
+
|
|
87
|
+
# Step 1: npm install
|
|
88
|
+
if is_package_installed(name) and not dry_run:
|
|
89
|
+
click.echo(f"@pennyfarthing/themes-{name} already installed, skipping npm install")
|
|
90
|
+
else:
|
|
91
|
+
if dry_run:
|
|
92
|
+
click.echo(f"[DRY-RUN] Would run: npm install @pennyfarthing/themes-{name}")
|
|
93
|
+
else:
|
|
94
|
+
click.echo(f"Installing @pennyfarthing/themes-{name}...")
|
|
95
|
+
|
|
96
|
+
result = npm_install(name, dry_run=dry_run)
|
|
97
|
+
if not result["success"]:
|
|
98
|
+
click.echo(f"Error: {result['error']}", err=True)
|
|
99
|
+
sys.exit(1)
|
|
100
|
+
|
|
101
|
+
if dry_run:
|
|
102
|
+
click.echo(f" Command: {result['data']['command']}")
|
|
103
|
+
else:
|
|
104
|
+
click.echo(" Installed successfully")
|
|
105
|
+
|
|
106
|
+
# Step 2: download portraits
|
|
107
|
+
if skip_portraits:
|
|
108
|
+
if not dry_run:
|
|
109
|
+
click.echo("Skipping portrait download (--skip-portraits)")
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
if dry_run:
|
|
113
|
+
click.echo(f"[DRY-RUN] Would download portraits (sizes: {'all' if all_sizes else 'large only'})")
|
|
114
|
+
else:
|
|
115
|
+
click.echo(f"Downloading portraits ({'all sizes' if all_sizes else 'large only'})...")
|
|
116
|
+
|
|
117
|
+
result = download_portraits(name, all_sizes=all_sizes, dry_run=dry_run)
|
|
118
|
+
if not result["success"]:
|
|
119
|
+
click.echo(f"Error: {result['error']}", err=True)
|
|
120
|
+
sys.exit(1)
|
|
121
|
+
|
|
122
|
+
data = result["data"]
|
|
123
|
+
if dry_run:
|
|
124
|
+
click.echo(f" {data['total_files']} files to download")
|
|
125
|
+
click.echo(f" Destination: {data['destination']}")
|
|
126
|
+
if data.get("files"):
|
|
127
|
+
for f in data["files"]:
|
|
128
|
+
click.echo(f" {f}")
|
|
129
|
+
if data.get("truncated"):
|
|
130
|
+
click.echo(" ...")
|
|
131
|
+
else:
|
|
132
|
+
click.echo(f" Downloaded: {data['downloaded']}, Skipped: {data['skipped']}, Total: {data['total']}")
|
|
133
|
+
if data.get("error_count"):
|
|
134
|
+
click.echo(f" Errors: {data['error_count']}")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@package.command("install-portraits")
|
|
138
|
+
@click.argument("name", required=False)
|
|
139
|
+
@click.option("--all", "install_all", is_flag=True, help="Download portraits for all installed packages.")
|
|
140
|
+
@click.option("--all-sizes", is_flag=True, help="Download all portrait sizes (default: large only).")
|
|
141
|
+
@click.option("--dry-run", is_flag=True, help="Show what would be done without making changes.")
|
|
142
|
+
def install_portraits(name: str | None, install_all: bool, all_sizes: bool, dry_run: bool):
|
|
143
|
+
"""Download portraits for already-installed theme packages.
|
|
144
|
+
|
|
145
|
+
Requires either a package NAME or --all. Downloads portrait images
|
|
146
|
+
from the GitHub monorepo and saves them into the package's
|
|
147
|
+
node_modules directory.
|
|
148
|
+
|
|
149
|
+
\b
|
|
150
|
+
Arguments:
|
|
151
|
+
NAME - Theme package name (optional if --all is used)
|
|
152
|
+
"""
|
|
153
|
+
from pf.package.discovery import THEME_PACKAGES, is_package_installed
|
|
154
|
+
from pf.package.portraits import download_portraits
|
|
155
|
+
|
|
156
|
+
if not name and not install_all:
|
|
157
|
+
click.echo("Error: provide a package name or use --all", err=True)
|
|
158
|
+
sys.exit(1)
|
|
159
|
+
|
|
160
|
+
packages = THEME_PACKAGES if install_all else [name]
|
|
161
|
+
|
|
162
|
+
for pkg_name in packages:
|
|
163
|
+
if not is_package_installed(pkg_name):
|
|
164
|
+
if install_all:
|
|
165
|
+
continue # silently skip uninstalled packages in --all mode
|
|
166
|
+
click.echo(
|
|
167
|
+
f"Error: @pennyfarthing/themes-{pkg_name} is not installed. "
|
|
168
|
+
f"Run: pf package install {pkg_name}",
|
|
169
|
+
err=True,
|
|
170
|
+
)
|
|
171
|
+
sys.exit(1)
|
|
172
|
+
|
|
173
|
+
click.echo(f"Downloading portraits for {pkg_name} ({'all sizes' if all_sizes else 'large only'})...")
|
|
174
|
+
|
|
175
|
+
result = download_portraits(pkg_name, all_sizes=all_sizes, dry_run=dry_run)
|
|
176
|
+
if not result["success"]:
|
|
177
|
+
click.echo(f" Error: {result['error']}", err=True)
|
|
178
|
+
if not install_all:
|
|
179
|
+
sys.exit(1)
|
|
180
|
+
continue
|
|
181
|
+
|
|
182
|
+
data = result["data"]
|
|
183
|
+
if dry_run:
|
|
184
|
+
click.echo(f" [DRY-RUN] {data['total_files']} files would be downloaded")
|
|
185
|
+
else:
|
|
186
|
+
click.echo(f" Downloaded: {data['downloaded']}, Skipped: {data['skipped']}, Total: {data['total']}")
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""Package discovery and npm install wrapper for theme packages.
|
|
2
|
+
|
|
3
|
+
Checks installation status of @pennyfarthing/themes-* packages and
|
|
4
|
+
provides npm install functionality.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import shutil
|
|
11
|
+
import subprocess
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from pf.common.config import get_project_root
|
|
16
|
+
|
|
17
|
+
THEME_PACKAGES = [
|
|
18
|
+
"comedy",
|
|
19
|
+
"literary",
|
|
20
|
+
"mythology-fantasy",
|
|
21
|
+
"prestige-tv",
|
|
22
|
+
"realistic",
|
|
23
|
+
"scifi",
|
|
24
|
+
"superheroes",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
PORTRAIT_SIZES = ["small", "medium", "large", "xlarge"]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _node_modules_dir(project_root: Path | None = None) -> Path:
|
|
31
|
+
"""Return the node_modules/@pennyfarthing directory."""
|
|
32
|
+
root = project_root or get_project_root()
|
|
33
|
+
return root / "node_modules" / "@pennyfarthing"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def get_package_dir(name: str, project_root: Path | None = None) -> Path:
|
|
37
|
+
"""Return the path to a theme package in node_modules."""
|
|
38
|
+
return _node_modules_dir(project_root) / f"themes-{name}"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def is_package_installed(name: str, project_root: Path | None = None) -> bool:
|
|
42
|
+
"""Check if a theme package is installed in node_modules."""
|
|
43
|
+
pkg_dir = get_package_dir(name, project_root)
|
|
44
|
+
pkg_json = pkg_dir / "package.json"
|
|
45
|
+
if not pkg_json.is_file():
|
|
46
|
+
return False
|
|
47
|
+
try:
|
|
48
|
+
data = json.loads(pkg_json.read_text())
|
|
49
|
+
return data.get("pennyfarthing-theme-pack") is True
|
|
50
|
+
except Exception:
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def has_portraits(name: str, project_root: Path | None = None) -> bool:
|
|
55
|
+
"""Check if a theme package has portraits downloaded."""
|
|
56
|
+
pkg_dir = get_package_dir(name, project_root)
|
|
57
|
+
portraits_dir = pkg_dir / "portraits"
|
|
58
|
+
if not portraits_dir.is_dir():
|
|
59
|
+
return False
|
|
60
|
+
# Check for at least one image file in any subdirectory
|
|
61
|
+
for child in portraits_dir.rglob("*"):
|
|
62
|
+
if child.is_file() and child.suffix.lower() in (".png", ".jpg", ".jpeg", ".webp"):
|
|
63
|
+
return True
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def get_package_status(
|
|
68
|
+
project_root: Path | None = None,
|
|
69
|
+
) -> list[dict[str, Any]]:
|
|
70
|
+
"""Get installation status for all theme packages.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
List of dicts with keys: name, installed, portraits
|
|
74
|
+
"""
|
|
75
|
+
results = []
|
|
76
|
+
for name in THEME_PACKAGES:
|
|
77
|
+
installed = is_package_installed(name, project_root)
|
|
78
|
+
portraits = has_portraits(name, project_root) if installed else False
|
|
79
|
+
results.append({
|
|
80
|
+
"name": name,
|
|
81
|
+
"installed": installed,
|
|
82
|
+
"portraits": portraits,
|
|
83
|
+
})
|
|
84
|
+
return results
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def npm_install(
|
|
88
|
+
name: str,
|
|
89
|
+
project_root: Path | None = None,
|
|
90
|
+
dry_run: bool = False,
|
|
91
|
+
) -> dict[str, Any]:
|
|
92
|
+
"""Install a theme package via npm.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
name: Theme package name (e.g. "comedy")
|
|
96
|
+
project_root: Project root (auto-detected if not provided)
|
|
97
|
+
dry_run: If True, show what would happen without executing
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Result dict with success, data?, error?
|
|
101
|
+
"""
|
|
102
|
+
if name not in THEME_PACKAGES:
|
|
103
|
+
return {"success": False, "error": f"Unknown theme package: {name}"}
|
|
104
|
+
|
|
105
|
+
if shutil.which("npm") is None:
|
|
106
|
+
return {"success": False, "error": "npm not found. Install Node.js first."}
|
|
107
|
+
|
|
108
|
+
root = project_root or get_project_root()
|
|
109
|
+
pkg_spec = f"@pennyfarthing/themes-{name}"
|
|
110
|
+
|
|
111
|
+
if dry_run:
|
|
112
|
+
return {
|
|
113
|
+
"success": True,
|
|
114
|
+
"data": {"action": "dry-run", "command": f"npm install {pkg_spec}", "cwd": str(root)},
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
result = subprocess.run(
|
|
118
|
+
["npm", "install", pkg_spec],
|
|
119
|
+
capture_output=True,
|
|
120
|
+
text=True,
|
|
121
|
+
cwd=str(root),
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
if result.returncode != 0:
|
|
125
|
+
return {
|
|
126
|
+
"success": False,
|
|
127
|
+
"error": f"npm install failed: {result.stderr.strip()}",
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return {"success": True, "data": {"package": pkg_spec, "output": result.stdout.strip()}}
|