@pennyfarthing/core 11.2.0 → 11.2.1
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 +1 -1
- package/package.json +2 -1
- package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/doctor.js +381 -66
- package/packages/core/dist/cli/commands/doctor.js.map +1 -1
- package/packages/core/dist/cli/commands/init.js +4 -4
- 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 -5
- package/packages/core/dist/cli/commands/update.js.map +1 -1
- package/packages/core/dist/cli/utils/constants.d.ts +3 -8
- package/packages/core/dist/cli/utils/constants.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/constants.js +3 -4
- package/packages/core/dist/cli/utils/constants.js.map +1 -1
- package/packages/core/dist/cli/utils/settings.d.ts +11 -0
- package/packages/core/dist/cli/utils/settings.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/settings.js +65 -29
- package/packages/core/dist/cli/utils/settings.js.map +1 -1
- package/packages/core/dist/cli/utils/symlinks.js +16 -16
- package/packages/core/dist/cli/utils/symlinks.js.map +1 -1
- package/packages/core/dist/consultation/tandem-metrics.d.ts +91 -0
- package/packages/core/dist/consultation/tandem-metrics.d.ts.map +1 -0
- package/packages/core/dist/consultation/tandem-metrics.js +131 -0
- package/packages/core/dist/consultation/tandem-metrics.js.map +1 -0
- package/packages/core/dist/consultation/tandem-metrics.test.d.ts +18 -0
- package/packages/core/dist/consultation/tandem-metrics.test.d.ts.map +1 -0
- package/packages/core/dist/consultation/tandem-metrics.test.js +457 -0
- package/packages/core/dist/consultation/tandem-metrics.test.js.map +1 -0
- package/packages/core/dist/public/js/react/react.js +14 -14
- 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/server/api/agent-load.js +1 -1
- package/packages/core/dist/server/api/agent-load.js.map +1 -1
- package/packages/core/dist/server/server.d.ts +0 -3
- package/packages/core/dist/server/server.d.ts.map +1 -1
- package/packages/core/dist/server/server.js +3 -37
- package/packages/core/dist/server/server.js.map +1 -1
- package/packages/core/dist/server/server.test.d.ts +1 -1
- package/packages/core/dist/server/server.test.js +12 -23
- package/packages/core/dist/server/server.test.js.map +1 -1
- package/packages/core/dist/shared/capabilities.d.ts +88 -0
- package/packages/core/dist/shared/capabilities.d.ts.map +1 -0
- package/packages/core/dist/shared/capabilities.js +133 -0
- package/packages/core/dist/shared/capabilities.js.map +1 -0
- package/packages/core/dist/shared/capabilities.test.d.ts +2 -0
- package/packages/core/dist/shared/capabilities.test.d.ts.map +1 -0
- package/packages/core/dist/shared/capabilities.test.js +217 -0
- package/packages/core/dist/shared/capabilities.test.js.map +1 -0
- package/packages/core/dist/shared/spawn-prompt.d.ts +47 -0
- package/packages/core/dist/shared/spawn-prompt.d.ts.map +1 -0
- package/packages/core/dist/shared/spawn-prompt.js +82 -0
- package/packages/core/dist/shared/spawn-prompt.js.map +1 -0
- package/packages/core/dist/shared/spawn-prompt.test.d.ts +2 -0
- package/packages/core/dist/shared/spawn-prompt.test.d.ts.map +1 -0
- package/packages/core/dist/shared/spawn-prompt.test.js +251 -0
- package/packages/core/dist/shared/spawn-prompt.test.js.map +1 -0
- package/packages/core/dist/workflow/tandem-workflow-templates.test.d.ts +18 -0
- package/packages/core/dist/workflow/tandem-workflow-templates.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/tandem-workflow-templates.test.js +434 -0
- package/packages/core/dist/workflow/tandem-workflow-templates.test.js.map +1 -0
- package/packages/core/dist/workflow/workflow-schema.d.ts +32 -0
- package/packages/core/dist/workflow/workflow-schema.d.ts.map +1 -1
- package/packages/core/dist/workflow/workflow-schema.js +120 -0
- package/packages/core/dist/workflow/workflow-schema.js.map +1 -1
- package/packages/core/dist/workflow/workflow-schema.test.d.ts.map +1 -1
- package/packages/core/dist/workflow/workflow-schema.test.js +570 -1
- package/packages/core/dist/workflow/workflow-schema.test.js.map +1 -1
- package/pennyfarthing-dist/agents/dev.md +6 -10
- package/pennyfarthing-dist/agents/reviewer.md +8 -2
- package/pennyfarthing-dist/agents/sm-finish.md +18 -1
- package/pennyfarthing-dist/commands/pf-git.md +4 -2
- package/pennyfarthing-dist/gates/approval.md +63 -0
- package/pennyfarthing-dist/gates/confidence-sm.md +71 -0
- package/pennyfarthing-dist/gates/context-ok.md +56 -0
- package/pennyfarthing-dist/gates/evaluations/confidence-sm.md +54 -0
- package/pennyfarthing-dist/gates/quality-pass.md +67 -0
- package/pennyfarthing-dist/gates/tests-fail.md +84 -0
- package/pennyfarthing-dist/gates/tests-pass.md +79 -0
- package/pennyfarthing-dist/guides/agent-behavior.md +23 -19
- package/pennyfarthing-dist/guides/bell-mode.md +1 -1
- package/pennyfarthing-dist/guides/hooks.md +28 -28
- package/pennyfarthing-dist/guides/reflector.md +1 -1
- package/pennyfarthing-dist/guides/tandem-protocol.md +3 -3
- package/pennyfarthing-dist/scripts/core/check-context.sh +2 -0
- package/pennyfarthing-dist/scripts/core/phase-check-start.sh +5 -87
- package/pennyfarthing-dist/scripts/hooks/README.md +5 -5
- package/pennyfarthing-dist/scripts/hooks/__pycache__/question_reflector_check.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +4 -183
- package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +4 -95
- package/pennyfarthing-dist/scripts/hooks/context-warning.sh +4 -65
- package/pennyfarthing-dist/scripts/hooks/cyclist-pretooluse-hook.sh +3 -31
- package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +27 -33
- package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +4 -71
- package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +3 -19
- package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +4 -30
- package/pennyfarthing-dist/scripts/hooks/session-start.sh +3 -32
- package/pennyfarthing-dist/scripts/hooks/session-stop.sh +4 -65
- package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +4 -78
- package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +4 -93
- package/pennyfarthing-dist/scripts/misc/README.md +1 -1
- package/pennyfarthing-dist/scripts/misc/statusline.sh +4 -301
- package/pennyfarthing-dist/templates/settings.local.json.template +19 -10
- package/pennyfarthing-dist/workflows/tdd.yaml +11 -2
- package/pennyfarthing_scripts/CLAUDE.md +19 -10
- package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/bellmode_hook.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/config.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/context.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/hooks.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__/patch_mode.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/pretooluse_hook.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/schema_validation_hook.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/session_start_hook.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/bc/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/__pycache__/focus.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bellmode_hook.py +12 -296
- package/pennyfarthing_scripts/bikerack/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/audit_log_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/background_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/changed_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/context_meter_footer.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/debug_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/diffs_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/events.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/git_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/portrait_resolver.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/progress_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/story_detail_data.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/story_detail_screen.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/ws_client.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/audit_log_panel.py +119 -0
- package/pennyfarthing_scripts/bikerack/base_panel.py +27 -4
- package/pennyfarthing_scripts/bikerack/changed_panel.py +96 -4
- package/pennyfarthing_scripts/bikerack/context_meter_footer.py +88 -0
- package/pennyfarthing_scripts/bikerack/debug_panel.py +1 -1
- package/pennyfarthing_scripts/bikerack/diffs_panel.py +30 -0
- package/pennyfarthing_scripts/bikerack/events.py +28 -0
- package/pennyfarthing_scripts/bikerack/portrait_resolver.py +139 -0
- package/pennyfarthing_scripts/bikerack/sprint_panel.py +373 -142
- package/pennyfarthing_scripts/bikerack/story_detail_data.py +244 -0
- package/pennyfarthing_scripts/bikerack/story_detail_screen.py +176 -0
- package/pennyfarthing_scripts/bikerack/tui.py +293 -61
- 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/cli.py +5 -0
- package/pennyfarthing_scripts/codemarkers/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/codemarkers/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/codemarkers/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/codemarkers/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/codemarkers/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/codemarkers/__pycache__/models.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/common/__pycache__/themes.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/pr_config.py +38 -0
- package/pennyfarthing_scripts/complexity/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/complexity/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/complexity/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/complexity/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/complexity/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/complexity/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/consultation/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/consultation/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/consultation/__pycache__/dialogue_manager.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/deadcode/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/deadcode/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/deadcode/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/deadcode/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/deadcode/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/deadcode/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/dependencies/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/dependencies/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/dependencies/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/dependencies/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/dependencies/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/dependencies/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/epic/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/epic/__pycache__/cli.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/git_group/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git_group/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/complete_phase.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/gate_file.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/gate_runner.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/marker.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/cli.py +33 -1
- package/pennyfarthing_scripts/handoff/complete_phase.py +28 -0
- package/pennyfarthing_scripts/handoff/marker.py +15 -15
- package/pennyfarthing_scripts/handoff/phase_check.py +96 -0
- package/pennyfarthing_scripts/handoff/resolve_gate.py +13 -1
- package/pennyfarthing_scripts/healthscore/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__init__.py +437 -0
- package/pennyfarthing_scripts/hooks/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/bell_mode.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/context_breaker.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/context_warning.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/cyclist_pretooluse.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/pre_edit_check.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/reflector_check.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/schema_validation.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/session_start.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/session_stop.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/sprint_yaml_validation.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/statusline.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/bell_mode.py +215 -0
- package/pennyfarthing_scripts/hooks/cli.py +96 -0
- package/pennyfarthing_scripts/hooks/context_breaker.py +104 -0
- package/pennyfarthing_scripts/hooks/context_warning.py +66 -0
- package/pennyfarthing_scripts/hooks/cyclist_pretooluse.py +129 -0
- package/pennyfarthing_scripts/hooks/pre_edit_check.py +78 -0
- package/pennyfarthing_scripts/hooks/reflector_check.py +271 -0
- package/pennyfarthing_scripts/hooks/schema_validation.py +203 -0
- package/pennyfarthing_scripts/hooks/session_start.py +296 -0
- package/pennyfarthing_scripts/hooks/session_stop.py +111 -0
- package/pennyfarthing_scripts/hooks/sprint_yaml_validation.py +97 -0
- package/pennyfarthing_scripts/hooks/statusline.py +420 -0
- package/pennyfarthing_scripts/hooks.py +27 -432
- package/pennyfarthing_scripts/hotspots/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hotspots/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hotspots/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hotspots/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hotspots/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hotspots/__pycache__/models.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__/create.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__/operations.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/reconcile.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/launch/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/launch/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/session.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/skill.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/step.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/validate.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/pretooluse_hook.py +3 -185
- package/pennyfarthing_scripts/prime/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/__main__.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__/version_sentinel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/workflow.py +2 -1
- package/pennyfarthing_scripts/schema_validation_hook.py +3 -298
- package/pennyfarthing_scripts/session/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/session/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/session_start_hook.py +4 -186
- 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__/archive_epic.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/epic_update.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/import_epic.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__/story_add.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/validate_cmd.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/yaml_io.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/story_update.py +19 -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/__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_108_1_gate_migration.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_archive_epic.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_bc.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_bikerack.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_cli_normalization.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_codemarkers.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_confidence_sm_evaluation.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_confidence_sm_gate.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_dialogue_manager.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_epic_shard_validation.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_handoff_cli.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_handoff_e2e.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_healthscore.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_patch_mode.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_panel.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_add.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_story_update.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_tiers.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_token_counting.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_topology_loader.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_tui_focus.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_tui_panel_persistence.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_validate_cmd.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_version_sentinel.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
- package/pennyfarthing_scripts/tests/__pycache__/test_yaml_io.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/test_sprint_panel.py +344 -265
- package/pennyfarthing_scripts/theme/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/theme/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/agent.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/schema.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/skill_command.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/sprint.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/tandem_awareness.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/workflow.py +19 -0
- package/pennyfarthing_scripts/welcome_hook.py +3 -149
- package/pennyfarthing_scripts/workflow/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/helpers.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/scale.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/state.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/cli.py +7 -6
- package/pennyfarthing_scripts/workflow/team_lifecycle.py +257 -0
- package/packages/core/dist/scripts/theme-detail.test.d.ts +0 -10
- package/packages/core/dist/scripts/theme-detail.test.js +0 -199
- package/pennyfarthing_scripts/bikerack/__pycache__/portrait.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/gate/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/gate/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/gate/__pycache__/validate.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/hooks_installer.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/repos.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/worktree.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/heatmap.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_108_2_remove_handoff_fallback.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_gate_file_resolution.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_gate_runner.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_resolve_gate_file_field.cpython-314-pytest-9.0.2.pyc +0 -0
|
@@ -17,16 +17,20 @@ from typing import Any
|
|
|
17
17
|
from textual.app import App, ComposeResult
|
|
18
18
|
from textual.binding import Binding
|
|
19
19
|
from textual.command import Hit, Hits, Provider
|
|
20
|
-
from textual.containers import VerticalScroll
|
|
20
|
+
from textual.containers import Horizontal, VerticalScroll
|
|
21
|
+
from textual.message import Message
|
|
21
22
|
from textual.reactive import reactive
|
|
22
|
-
from textual.widgets import Footer, Header, Static
|
|
23
|
+
from textual.widgets import Footer, Header, Static, Tab, Tabs
|
|
23
24
|
|
|
24
25
|
from pennyfarthing_scripts.bc.focus import get_last_panel, save_last_panel
|
|
26
|
+
from pennyfarthing_scripts.bikerack.audit_log_panel import AuditLogPanel
|
|
25
27
|
from pennyfarthing_scripts.bikerack.background_panel import BackgroundPanel
|
|
26
28
|
from pennyfarthing_scripts.bikerack.base_panel import get_panel_icon
|
|
27
29
|
from pennyfarthing_scripts.bikerack.changed_panel import ChangedPanel
|
|
30
|
+
from pennyfarthing_scripts.bikerack.context_meter_footer import ContextMeterFooter
|
|
28
31
|
from pennyfarthing_scripts.bikerack.debug_panel import DebugPanel
|
|
29
32
|
from pennyfarthing_scripts.bikerack.diffs_panel import DiffsPanel
|
|
33
|
+
from pennyfarthing_scripts.bikerack.events import NavigateToFile
|
|
30
34
|
from pennyfarthing_scripts.bikerack.git_panel import GitPanel
|
|
31
35
|
from pennyfarthing_scripts.bikerack.progress_panel import ProgressPanel
|
|
32
36
|
from pennyfarthing_scripts.bikerack.sprint_panel import SprintPanel
|
|
@@ -76,6 +80,7 @@ PANEL_REGISTRY: list[tuple[str, str]] = [
|
|
|
76
80
|
("diffs", "Diffs"),
|
|
77
81
|
("changed", "Changed"),
|
|
78
82
|
("background", "Background"),
|
|
83
|
+
("audit-log", "Audit Log"),
|
|
79
84
|
("debug", "Debug"),
|
|
80
85
|
("progress", "Progress"),
|
|
81
86
|
]
|
|
@@ -101,38 +106,69 @@ PANEL_DISPLAY_NAMES: dict[str, str] = {
|
|
|
101
106
|
_PANEL_KEYS = [key for key, _ in PANEL_REGISTRY]
|
|
102
107
|
|
|
103
108
|
|
|
104
|
-
class
|
|
105
|
-
"""
|
|
109
|
+
class BindingFooter(Footer):
|
|
110
|
+
"""Footer subclass that exposes active binding text via render().
|
|
106
111
|
|
|
107
|
-
|
|
112
|
+
Textual's Footer uses compose() for visual content, so render() returns
|
|
113
|
+
Blank. This override makes binding descriptions available through
|
|
114
|
+
str(footer.render()) for programmatic inspection.
|
|
115
|
+
"""
|
|
108
116
|
|
|
109
|
-
def
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
117
|
+
def render(self) -> Any:
|
|
118
|
+
try:
|
|
119
|
+
bindings = self.screen.active_bindings
|
|
120
|
+
parts: list[str] = []
|
|
121
|
+
for _, binding, _enabled, _tooltip in bindings.values():
|
|
122
|
+
if binding.show:
|
|
123
|
+
parts.append(f"{binding.key}:{binding.description}")
|
|
124
|
+
if parts:
|
|
125
|
+
return " ".join(parts)
|
|
126
|
+
except Exception:
|
|
127
|
+
pass
|
|
128
|
+
return super().render()
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _build_panel_tabs() -> list[Tab]:
|
|
132
|
+
"""Build Tab widgets for each panel in the registry."""
|
|
133
|
+
tabs: list[Tab] = []
|
|
134
|
+
for panel_key, display_name in PANEL_REGISTRY:
|
|
135
|
+
icon = get_panel_icon(panel_key)
|
|
136
|
+
label = f"{icon} {display_name}" if icon else display_name
|
|
137
|
+
tabs.append(Tab(label, id=f"tab-{panel_key}"))
|
|
138
|
+
return tabs
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
PORTRAIT_SKELETON = """\
|
|
142
|
+
[dim]┌────────┐
|
|
143
|
+
│░░░░░░░░│
|
|
144
|
+
│░░░▓▓░░░│
|
|
145
|
+
│░░░░░░░░│
|
|
146
|
+
└────────┘[/dim]"""
|
|
127
147
|
|
|
128
148
|
|
|
129
149
|
class AgentHeader(Static):
|
|
130
|
-
"""Displays current agent persona from WheelHub /ws/persona channel.
|
|
150
|
+
"""Displays current agent persona from WheelHub /ws/persona channel.
|
|
151
|
+
|
|
152
|
+
When a portrait image is available (resolved locally or provided via
|
|
153
|
+
portraitPath in persona data), mounts a Horizontal layout container.
|
|
154
|
+
Shows a skeleton placeholder while the image loads.
|
|
155
|
+
Falls back to text-only when no portrait is found.
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
class PortraitLayoutUpdate(Message):
|
|
159
|
+
"""Internal message to update portrait layout asynchronously."""
|
|
160
|
+
|
|
161
|
+
def __init__(self, portrait_path: Path | None) -> None:
|
|
162
|
+
super().__init__()
|
|
163
|
+
self.has_portrait = portrait_path is not None
|
|
164
|
+
self.portrait_path = portrait_path
|
|
131
165
|
|
|
132
166
|
def __init__(self, **kwargs: Any) -> None:
|
|
133
167
|
super().__init__(**kwargs)
|
|
134
168
|
self._is_streaming: bool = False
|
|
135
169
|
self._persona_data: dict[str, Any] = {}
|
|
170
|
+
self._header_text: str = ""
|
|
171
|
+
self._current_portrait: Path | None = None
|
|
136
172
|
|
|
137
173
|
def _apply_persona(self, data: dict[str, Any]) -> None:
|
|
138
174
|
"""Render persona data into the header."""
|
|
@@ -145,6 +181,21 @@ class AgentHeader(Static):
|
|
|
145
181
|
self._is_streaming = bool(data.get("isStreaming", False))
|
|
146
182
|
self._render_header()
|
|
147
183
|
|
|
184
|
+
def _resolve_portrait(self, data: dict[str, Any]) -> Path | None:
|
|
185
|
+
"""Get portrait path from persona data or resolve locally."""
|
|
186
|
+
portrait_path = data.get("portraitPath")
|
|
187
|
+
if portrait_path:
|
|
188
|
+
p = Path(portrait_path)
|
|
189
|
+
if p.exists():
|
|
190
|
+
return p
|
|
191
|
+
theme = data.get("theme", "")
|
|
192
|
+
role = data.get("role", "")
|
|
193
|
+
if theme and role:
|
|
194
|
+
from pennyfarthing_scripts.bikerack import portrait_resolver
|
|
195
|
+
|
|
196
|
+
return portrait_resolver.resolve_portrait_path(theme, role)
|
|
197
|
+
return None
|
|
198
|
+
|
|
148
199
|
def _render_header(self) -> None:
|
|
149
200
|
"""Re-render the header from stored state."""
|
|
150
201
|
data = self._persona_data
|
|
@@ -152,20 +203,20 @@ class AgentHeader(Static):
|
|
|
152
203
|
role = data.get("role", "")
|
|
153
204
|
role_desc = data.get("roleDescription", "")
|
|
154
205
|
quote = data.get("quote", "")
|
|
155
|
-
style = data.get("style", "")
|
|
156
206
|
theme = data.get("theme", "")
|
|
157
207
|
|
|
158
208
|
if not char:
|
|
159
209
|
self.update("[dim]Waiting for agent...[/dim]")
|
|
210
|
+
self.post_message(self.PortraitLayoutUpdate(portrait_path=None))
|
|
160
211
|
return
|
|
161
212
|
|
|
162
213
|
parts: list[str] = []
|
|
163
214
|
|
|
164
|
-
# Role badge
|
|
215
|
+
# Role badge — escape brackets so Rich doesn't eat them as tags
|
|
165
216
|
if role:
|
|
166
217
|
abbrev = AGENT_ABBREV.get(role, role.upper()[:3])
|
|
167
218
|
color = AGENT_ROLE_COLORS.get(role, "bright_magenta")
|
|
168
|
-
parts.append(f"[bold {color}][{abbrev}][/bold {color}]")
|
|
219
|
+
parts.append(f"[bold {color}]\\[{abbrev}][/bold {color}]")
|
|
169
220
|
|
|
170
221
|
# Character name
|
|
171
222
|
parts.append(f"[bold]{char}[/bold]")
|
|
@@ -173,6 +224,7 @@ class AgentHeader(Static):
|
|
|
173
224
|
# Theme name
|
|
174
225
|
if theme:
|
|
175
226
|
from pennyfarthing_scripts.bikerack.base_panel import humanize_theme
|
|
227
|
+
|
|
176
228
|
parts.append(f"[dim]{humanize_theme(theme)}[/dim]")
|
|
177
229
|
|
|
178
230
|
# Streaming indicator
|
|
@@ -181,17 +233,87 @@ class AgentHeader(Static):
|
|
|
181
233
|
|
|
182
234
|
line = " ".join(parts)
|
|
183
235
|
|
|
184
|
-
#
|
|
185
|
-
if role_desc:
|
|
186
|
-
line += f"\n[dim]{role_desc}[/dim]"
|
|
187
|
-
elif style:
|
|
188
|
-
line += f"\n[dim]{style}[/dim]"
|
|
189
|
-
|
|
190
|
-
# Quote
|
|
236
|
+
# Catchphrase subtitle (quote is a random catchphrase from the theme)
|
|
191
237
|
if quote:
|
|
192
238
|
line += f"\n[italic dim]\"{quote}\"[/italic dim]"
|
|
239
|
+
elif role_desc:
|
|
240
|
+
line += f"\n[dim]{role_desc}[/dim]"
|
|
241
|
+
|
|
242
|
+
self._header_text = line
|
|
243
|
+
|
|
244
|
+
# Check portrait and schedule layout update
|
|
245
|
+
portrait = self._resolve_portrait(data)
|
|
246
|
+
self.post_message(self.PortraitLayoutUpdate(portrait_path=portrait))
|
|
247
|
+
|
|
248
|
+
async def on_agent_header_portrait_layout_update(
|
|
249
|
+
self, event: PortraitLayoutUpdate
|
|
250
|
+
) -> None:
|
|
251
|
+
"""Mount or remove Horizontal portrait layout with text beside image.
|
|
193
252
|
|
|
194
|
-
|
|
253
|
+
Shows a skeleton placeholder immediately while the real image loads
|
|
254
|
+
to avoid a visible blank gap during image decode/render.
|
|
255
|
+
"""
|
|
256
|
+
if event.has_portrait and event.portrait_path:
|
|
257
|
+
if self._current_portrait == event.portrait_path:
|
|
258
|
+
# Same portrait — just update text label if it exists
|
|
259
|
+
try:
|
|
260
|
+
text_widget = self.query_one("#agent-text", Static)
|
|
261
|
+
text_widget.update(self._header_text)
|
|
262
|
+
except Exception:
|
|
263
|
+
pass
|
|
264
|
+
return
|
|
265
|
+
|
|
266
|
+
# New portrait or first time — full layout rebuild
|
|
267
|
+
for child in list(self.query("Horizontal")):
|
|
268
|
+
await child.remove()
|
|
269
|
+
|
|
270
|
+
# Mount skeleton + text immediately so there's no blank gap
|
|
271
|
+
self.update("")
|
|
272
|
+
self._current_portrait = event.portrait_path
|
|
273
|
+
skeleton = Static(PORTRAIT_SKELETON, id="portrait-skeleton")
|
|
274
|
+
text = Static(self._header_text, id="agent-text")
|
|
275
|
+
row = Horizontal(skeleton, text, id="portrait-row")
|
|
276
|
+
await self.mount(row)
|
|
277
|
+
|
|
278
|
+
# Now try to load the real image and swap it in
|
|
279
|
+
try:
|
|
280
|
+
from pennyfarthing_scripts.bikerack.portrait_resolver import (
|
|
281
|
+
detect_image_protocol,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
protocol = detect_image_protocol()
|
|
285
|
+
if protocol is None:
|
|
286
|
+
# No image protocol — remove skeleton, fall back to text-only
|
|
287
|
+
for child in list(self.query("Horizontal")):
|
|
288
|
+
await child.remove()
|
|
289
|
+
self.update(self._header_text)
|
|
290
|
+
return
|
|
291
|
+
|
|
292
|
+
if protocol == "kitty":
|
|
293
|
+
from textual_image.widget import TGPImage as ImageWidget
|
|
294
|
+
elif protocol == "sixel":
|
|
295
|
+
from textual_image.widget import SixelImage as ImageWidget
|
|
296
|
+
else:
|
|
297
|
+
from textual_image.widget import HalfcellImage as ImageWidget
|
|
298
|
+
|
|
299
|
+
img = ImageWidget(str(event.portrait_path), id="portrait-img")
|
|
300
|
+
try:
|
|
301
|
+
skel = self.query_one("#portrait-skeleton")
|
|
302
|
+
await skel.remove()
|
|
303
|
+
except Exception:
|
|
304
|
+
pass
|
|
305
|
+
await row.mount(img, before=0)
|
|
306
|
+
except (ImportError, Exception):
|
|
307
|
+
# textual-image not available — remove skeleton, text-only
|
|
308
|
+
for child in list(self.query("Horizontal")):
|
|
309
|
+
await child.remove()
|
|
310
|
+
self.update(self._header_text)
|
|
311
|
+
else:
|
|
312
|
+
# No portrait — text-only
|
|
313
|
+
for child in list(self.query("Horizontal")):
|
|
314
|
+
await child.remove()
|
|
315
|
+
self._current_portrait = None
|
|
316
|
+
self.update(self._header_text)
|
|
195
317
|
|
|
196
318
|
|
|
197
319
|
class ConnectionStatus(Static):
|
|
@@ -227,20 +349,64 @@ class PanelCommands(Provider):
|
|
|
227
349
|
class BikeRackApp(App):
|
|
228
350
|
"""BikeRack TUI application shell."""
|
|
229
351
|
|
|
352
|
+
class PersonaUpdate(Message, bubble=False):
|
|
353
|
+
"""Persona data from WS — routed through Textual message system."""
|
|
354
|
+
|
|
355
|
+
def __init__(self, data: dict[str, Any]) -> None:
|
|
356
|
+
super().__init__()
|
|
357
|
+
self.data = data
|
|
358
|
+
|
|
359
|
+
class FocusUpdate(Message, bubble=False):
|
|
360
|
+
"""Focus change from WS — routed through Textual message system."""
|
|
361
|
+
|
|
362
|
+
def __init__(self, focus: str | None) -> None:
|
|
363
|
+
super().__init__()
|
|
364
|
+
self.focus = focus
|
|
365
|
+
|
|
366
|
+
class WsStateUpdate(Message, bubble=False):
|
|
367
|
+
"""WS connection state change — routed through Textual message system."""
|
|
368
|
+
|
|
369
|
+
def __init__(self, state: ConnectionState) -> None:
|
|
370
|
+
super().__init__()
|
|
371
|
+
self.state = state
|
|
372
|
+
|
|
230
373
|
TITLE = "BikeRack"
|
|
231
374
|
|
|
232
375
|
CSS = """
|
|
233
376
|
#agent-header {
|
|
234
377
|
height: auto;
|
|
235
|
-
max-height:
|
|
378
|
+
max-height: 7;
|
|
236
379
|
padding: 0 1;
|
|
380
|
+
border-bottom: solid $accent;
|
|
237
381
|
}
|
|
238
|
-
#
|
|
239
|
-
height:
|
|
382
|
+
#portrait-row {
|
|
383
|
+
height: 5;
|
|
384
|
+
width: 100%;
|
|
385
|
+
}
|
|
386
|
+
#portrait-img {
|
|
387
|
+
width: 10;
|
|
388
|
+
height: 5;
|
|
389
|
+
margin: 0 1 0 0;
|
|
390
|
+
}
|
|
391
|
+
#agent-text {
|
|
392
|
+
height: auto;
|
|
393
|
+
width: 1fr;
|
|
394
|
+
}
|
|
395
|
+
Tabs {
|
|
396
|
+
dock: top;
|
|
397
|
+
}
|
|
398
|
+
Tab.-active {
|
|
399
|
+
color: $text;
|
|
400
|
+
}
|
|
401
|
+
Tab {
|
|
402
|
+
color: $text-muted;
|
|
240
403
|
}
|
|
241
404
|
#connection-status {
|
|
242
405
|
height: 1;
|
|
243
406
|
}
|
|
407
|
+
ContextMeterFooter {
|
|
408
|
+
height: 1;
|
|
409
|
+
}
|
|
244
410
|
"""
|
|
245
411
|
|
|
246
412
|
COMMANDS = App.COMMANDS | {PanelCommands}
|
|
@@ -252,8 +418,9 @@ class BikeRackApp(App):
|
|
|
252
418
|
Binding("3", "switch_panel('diffs')", "Diffs", show=False),
|
|
253
419
|
Binding("4", "switch_panel('changed')", "Changed", show=False),
|
|
254
420
|
Binding("5", "switch_panel('background')", "Background", show=False),
|
|
255
|
-
Binding("6", "switch_panel('
|
|
256
|
-
Binding("7", "switch_panel('
|
|
421
|
+
Binding("6", "switch_panel('audit-log')", "Audit Log", show=False),
|
|
422
|
+
Binding("7", "switch_panel('debug')", "Debug", show=False),
|
|
423
|
+
Binding("8", "switch_panel('progress')", "Progress", show=False),
|
|
257
424
|
Binding("bracketright", "next_panel", "]Next"),
|
|
258
425
|
Binding("bracketleft", "prev_panel", "[Prev"),
|
|
259
426
|
Binding("tab", "next_panel", show=False),
|
|
@@ -265,16 +432,21 @@ class BikeRackApp(App):
|
|
|
265
432
|
Binding("e", "toggle_epic", show=False),
|
|
266
433
|
]
|
|
267
434
|
|
|
435
|
+
def _get_dom_base(self):
|
|
436
|
+
"""Query the active screen so app.query() finds pushed screen widgets."""
|
|
437
|
+
return self.screen
|
|
438
|
+
|
|
268
439
|
def __init__(self, client=None, **kwargs):
|
|
269
440
|
super().__init__(**kwargs)
|
|
270
441
|
self._client = client
|
|
271
442
|
self._focused_panel: str = "sprint"
|
|
272
443
|
self._previous_panel: str | None = None
|
|
444
|
+
self._programmatic_tab_count: int = 0
|
|
273
445
|
|
|
274
446
|
def compose(self) -> ComposeResult:
|
|
275
447
|
yield Header()
|
|
276
448
|
yield AgentHeader(id="agent-header")
|
|
277
|
-
yield
|
|
449
|
+
yield Tabs(*_build_panel_tabs(), id="tab-bar")
|
|
278
450
|
yield ConnectionStatus(
|
|
279
451
|
STATE_DISPLAY[ConnectionState.DISCONNECTED],
|
|
280
452
|
id="connection-status",
|
|
@@ -285,9 +457,11 @@ class BikeRackApp(App):
|
|
|
285
457
|
yield DiffsPanel(client=self._client, id="panel-diffs")
|
|
286
458
|
yield ChangedPanel(client=self._client, id="panel-changed")
|
|
287
459
|
yield BackgroundPanel(client=self._client, id="panel-background")
|
|
460
|
+
yield AuditLogPanel(client=self._client, id="panel-audit-log")
|
|
288
461
|
yield DebugPanel(client=self._client, id="panel-debug")
|
|
289
462
|
yield ProgressPanel(client=self._client, id="panel-progress")
|
|
290
|
-
yield
|
|
463
|
+
yield ContextMeterFooter(client=self._client)
|
|
464
|
+
yield BindingFooter()
|
|
291
465
|
|
|
292
466
|
async def on_mount(self) -> None:
|
|
293
467
|
# Restore last panel or default to sprint
|
|
@@ -309,8 +483,13 @@ class BikeRackApp(App):
|
|
|
309
483
|
except Exception:
|
|
310
484
|
pass
|
|
311
485
|
|
|
312
|
-
# Set tab bar active state
|
|
486
|
+
# Set tab bar active state and focus initial panel
|
|
313
487
|
self._update_tab_bar(initial)
|
|
488
|
+
try:
|
|
489
|
+
initial_widget = self.query_one(f"#panel-{initial}")
|
|
490
|
+
initial_widget.focus()
|
|
491
|
+
except Exception:
|
|
492
|
+
pass
|
|
314
493
|
|
|
315
494
|
if self._client is not None:
|
|
316
495
|
self._client.on_state_change(self._on_ws_state_change)
|
|
@@ -318,6 +497,15 @@ class BikeRackApp(App):
|
|
|
318
497
|
self._client.subscribe("persona", self._handle_persona_message)
|
|
319
498
|
self.run_worker(self._client.connect(), exclusive=True, name="ws-client")
|
|
320
499
|
|
|
500
|
+
def on_navigate_to_file(self, event: NavigateToFile) -> None:
|
|
501
|
+
"""Handle NavigateToFile — switch to diffs and navigate to file."""
|
|
502
|
+
self.action_switch_panel("diffs")
|
|
503
|
+
try:
|
|
504
|
+
diffs = self.query_one("#panel-diffs", DiffsPanel)
|
|
505
|
+
diffs.navigate_to_file(event.path)
|
|
506
|
+
except Exception:
|
|
507
|
+
pass
|
|
508
|
+
|
|
321
509
|
def action_switch_panel(self, key: str) -> None:
|
|
322
510
|
"""Switch to a panel by key."""
|
|
323
511
|
if key not in _PANEL_KEYS:
|
|
@@ -332,10 +520,11 @@ class BikeRackApp(App):
|
|
|
332
520
|
except Exception:
|
|
333
521
|
pass
|
|
334
522
|
|
|
335
|
-
# Show target panel
|
|
523
|
+
# Show target panel and focus it
|
|
336
524
|
try:
|
|
337
525
|
target = self.query_one(f"#panel-{key}")
|
|
338
526
|
target.display = True
|
|
527
|
+
target.focus()
|
|
339
528
|
except Exception:
|
|
340
529
|
pass
|
|
341
530
|
|
|
@@ -408,18 +597,40 @@ class BikeRackApp(App):
|
|
|
408
597
|
pass
|
|
409
598
|
|
|
410
599
|
def _update_tab_bar(self, panel_key: str) -> None:
|
|
411
|
-
"""Update the tab bar widget with the given panel key.
|
|
600
|
+
"""Update the tab bar widget with the given panel key.
|
|
601
|
+
|
|
602
|
+
Increments _programmatic_tab_count so the async TabActivated
|
|
603
|
+
handler knows to ignore the event (prevents infinite ping-pong).
|
|
604
|
+
"""
|
|
412
605
|
try:
|
|
413
|
-
tab_bar = self.query_one("#tab-bar",
|
|
414
|
-
|
|
606
|
+
tab_bar = self.query_one("#tab-bar", Tabs)
|
|
607
|
+
tab_id = f"tab-{panel_key}"
|
|
608
|
+
if tab_bar.active != tab_id:
|
|
609
|
+
self._programmatic_tab_count += 1
|
|
610
|
+
tab_bar.active = tab_id
|
|
415
611
|
except Exception:
|
|
416
612
|
pass
|
|
417
613
|
|
|
614
|
+
def on_tabs_tab_activated(self, event: Tabs.TabActivated) -> None:
|
|
615
|
+
"""Handle tab activation from the Tabs widget.
|
|
616
|
+
|
|
617
|
+
Programmatic tabs.active changes fire TabActivated asynchronously.
|
|
618
|
+
We use a counter to skip those and only react to genuine user clicks.
|
|
619
|
+
"""
|
|
620
|
+
if self._programmatic_tab_count > 0:
|
|
621
|
+
self._programmatic_tab_count -= 1
|
|
622
|
+
return
|
|
623
|
+
tab_id = event.tab.id or ""
|
|
624
|
+
panel_key = tab_id.removeprefix("tab-")
|
|
625
|
+
if panel_key in _PANEL_KEYS and panel_key != self._focused_panel:
|
|
626
|
+
self.action_switch_panel(panel_key)
|
|
627
|
+
|
|
418
628
|
def _handle_focus_message(self, message: dict[str, Any] | None) -> None:
|
|
419
629
|
"""Handle incoming focus channel messages.
|
|
420
630
|
|
|
421
631
|
Expected format: {type: 'init'|'update', focus: '<panel>'|null}
|
|
422
632
|
Only 'update' messages trigger panel switches (matching React hook).
|
|
633
|
+
Routes through Textual message system via post_message for proper repaint.
|
|
423
634
|
"""
|
|
424
635
|
if message is None or not isinstance(message, dict):
|
|
425
636
|
return
|
|
@@ -427,31 +638,47 @@ class BikeRackApp(App):
|
|
|
427
638
|
return
|
|
428
639
|
if "focus" not in message:
|
|
429
640
|
return
|
|
430
|
-
|
|
431
|
-
focus = message["focus"]
|
|
432
|
-
if focus is not None and focus in _PANEL_KEYS:
|
|
433
|
-
self.action_switch_panel(focus)
|
|
434
|
-
elif focus is not None:
|
|
435
|
-
# Panel exists in display names but not implemented — just update state
|
|
436
|
-
self._previous_panel = self._focused_panel
|
|
437
|
-
self._focused_panel = focus
|
|
438
|
-
save_last_panel(focus, project_dir=None)
|
|
641
|
+
self.post_message(self.FocusUpdate(message["focus"]))
|
|
439
642
|
|
|
440
643
|
def _handle_persona_message(self, message: dict[str, Any] | None) -> None:
|
|
441
|
-
"""Handle incoming persona channel messages.
|
|
644
|
+
"""Handle incoming persona channel messages.
|
|
645
|
+
|
|
646
|
+
Routes through Textual message system via post_message for proper repaint.
|
|
647
|
+
"""
|
|
442
648
|
if message is None or not isinstance(message, dict):
|
|
443
649
|
return
|
|
650
|
+
self.post_message(self.PersonaUpdate(message))
|
|
651
|
+
|
|
652
|
+
def _on_ws_state_change(self, state: ConnectionState) -> None:
|
|
653
|
+
"""Handle WheelHub connection state changes.
|
|
654
|
+
|
|
655
|
+
Routes through Textual message system via post_message for proper repaint.
|
|
656
|
+
"""
|
|
657
|
+
self.post_message(self.WsStateUpdate(state))
|
|
658
|
+
|
|
659
|
+
def on_bike_rack_app_persona_update(self, event: PersonaUpdate) -> None:
|
|
660
|
+
"""Apply persona data in Textual message context."""
|
|
444
661
|
try:
|
|
445
662
|
header = self.query_one("#agent-header", AgentHeader)
|
|
446
|
-
header._apply_persona(
|
|
663
|
+
header._apply_persona(event.data)
|
|
447
664
|
except Exception:
|
|
448
665
|
pass
|
|
449
666
|
|
|
450
|
-
def
|
|
451
|
-
"""
|
|
667
|
+
def on_bike_rack_app_focus_update(self, event: FocusUpdate) -> None:
|
|
668
|
+
"""Apply focus change in Textual message context."""
|
|
669
|
+
focus = event.focus
|
|
670
|
+
if focus is not None and focus in _PANEL_KEYS:
|
|
671
|
+
self.action_switch_panel(focus)
|
|
672
|
+
elif focus is not None:
|
|
673
|
+
self._previous_panel = self._focused_panel
|
|
674
|
+
self._focused_panel = focus
|
|
675
|
+
save_last_panel(focus, project_dir=None)
|
|
676
|
+
|
|
677
|
+
def on_bike_rack_app_ws_state_update(self, event: WsStateUpdate) -> None:
|
|
678
|
+
"""Apply connection state in Textual message context."""
|
|
452
679
|
try:
|
|
453
680
|
widget = self.query_one("#connection-status", ConnectionStatus)
|
|
454
|
-
widget.connection_state = state
|
|
681
|
+
widget.connection_state = event.state
|
|
455
682
|
except Exception:
|
|
456
683
|
pass
|
|
457
684
|
|
|
@@ -469,6 +696,11 @@ def main(
|
|
|
469
696
|
port: Explicit WheelHub port. If None, reads from .wheelhub-port file.
|
|
470
697
|
project_dir: Project directory for port file discovery. Defaults to cwd.
|
|
471
698
|
"""
|
|
699
|
+
# Detect terminal image protocol BEFORE App.run() claims the terminal
|
|
700
|
+
from pennyfarthing_scripts.bikerack import portrait_resolver
|
|
701
|
+
|
|
702
|
+
portrait_resolver.detect_image_protocol()
|
|
703
|
+
|
|
472
704
|
if port is None:
|
|
473
705
|
if project_dir is not None:
|
|
474
706
|
port_file = project_dir / ".wheelhub-port"
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -142,6 +142,11 @@ from pennyfarthing_scripts.consultation.cli import consultation # noqa: E402
|
|
|
142
142
|
|
|
143
143
|
cli.add_command(consultation)
|
|
144
144
|
|
|
145
|
+
# Import and register hooks group
|
|
146
|
+
from pennyfarthing_scripts.hooks.cli import hooks # noqa: E402
|
|
147
|
+
|
|
148
|
+
cli.add_command(hooks)
|
|
149
|
+
|
|
145
150
|
|
|
146
151
|
@cli.group()
|
|
147
152
|
def agent():
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""PR mode configuration reader.
|
|
2
|
+
|
|
3
|
+
Reads the pr_mode preference from .pennyfarthing/config.local.yaml.
|
|
4
|
+
|
|
5
|
+
Values: draft | ready | none (default: draft)
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from pennyfarthing_scripts.common.config import load_pennyfarthing_config
|
|
11
|
+
|
|
12
|
+
VALID_PR_MODES = {"draft", "ready", "none"}
|
|
13
|
+
DEFAULT_PR_MODE = "draft"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_pr_mode() -> str:
|
|
17
|
+
"""Read pr_mode from pennyfarthing config.
|
|
18
|
+
|
|
19
|
+
Looks for workflow.pr_mode in .pennyfarthing/config.local.yaml.
|
|
20
|
+
Falls back to 'draft' if not set or invalid.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
One of: 'draft', 'ready', 'none'
|
|
24
|
+
"""
|
|
25
|
+
config = load_pennyfarthing_config()
|
|
26
|
+
workflow = config.get("workflow", {})
|
|
27
|
+
if not isinstance(workflow, dict):
|
|
28
|
+
return DEFAULT_PR_MODE
|
|
29
|
+
|
|
30
|
+
mode = workflow.get("pr_mode", DEFAULT_PR_MODE)
|
|
31
|
+
if mode not in VALID_PR_MODES:
|
|
32
|
+
return DEFAULT_PR_MODE
|
|
33
|
+
|
|
34
|
+
return mode
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if __name__ == "__main__":
|
|
38
|
+
print(get_pr_mode())
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|