@pennyfarthing/core 11.2.1 → 11.3.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 +102 -42
- package/package.json +1 -1
- package/packages/core/dist/cli/commands/doctor-legacy.test.js +2 -2
- package/packages/core/dist/cli/commands/doctor-legacy.test.js.map +1 -1
- package/packages/core/dist/cli/commands/doctor.d.ts +55 -0
- package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/doctor.js +324 -50
- package/packages/core/dist/cli/commands/doctor.js.map +1 -1
- package/packages/core/dist/cli/commands/init.d.ts +12 -0
- package/packages/core/dist/cli/commands/init.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/init.js +45 -0
- package/packages/core/dist/cli/commands/init.js.map +1 -1
- package/packages/core/dist/cli/commands/pyproject-install.test.d.ts +19 -0
- package/packages/core/dist/cli/commands/pyproject-install.test.d.ts.map +1 -0
- package/packages/core/dist/cli/commands/pyproject-install.test.js +261 -0
- package/packages/core/dist/cli/commands/pyproject-install.test.js.map +1 -0
- 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-consolidation.test.js +14 -6
- package/packages/core/dist/cli/commands/update-consolidation.test.js.map +1 -1
- package/packages/core/dist/cli/commands/update.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/update.js +31 -2
- package/packages/core/dist/cli/commands/update.js.map +1 -1
- package/packages/core/dist/cli/index.js +2 -0
- package/packages/core/dist/cli/index.js.map +1 -1
- package/packages/core/dist/cli/utils/python.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/python.js +11 -0
- package/packages/core/dist/cli/utils/python.js.map +1 -1
- package/packages/core/dist/cli/utils/settings-hook-migration.test.d.ts +17 -0
- package/packages/core/dist/cli/utils/settings-hook-migration.test.d.ts.map +1 -0
- package/packages/core/dist/cli/utils/settings-hook-migration.test.js +382 -0
- package/packages/core/dist/cli/utils/settings-hook-migration.test.js.map +1 -0
- package/packages/core/dist/cli/utils/settings.d.ts +0 -4
- package/packages/core/dist/cli/utils/settings.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/settings.js +45 -27
- 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/consultation/dialogue-manager.d.ts +1 -1
- package/packages/core/dist/consultation/dialogue-manager.d.ts.map +1 -1
- package/packages/core/dist/consultation/dialogue-manager.js +1 -1
- package/packages/core/dist/consultation/dialogue-manager.js.map +1 -1
- package/packages/core/dist/consultation/dialogue-manager.test.js.map +1 -1
- package/packages/core/dist/consultation/tandem-metrics.test.js.map +1 -1
- package/packages/core/dist/public/css/react.css +1 -1
- package/packages/core/dist/public/js/react/react.js +9 -9
- package/packages/core/dist/server/api/git.d.ts.map +1 -1
- package/packages/core/dist/server/api/git.js +0 -1
- package/packages/core/dist/server/api/git.js.map +1 -1
- package/packages/core/dist/server/api/index.d.ts +2 -0
- package/packages/core/dist/server/api/index.d.ts.map +1 -1
- package/packages/core/dist/server/api/index.js +2 -0
- package/packages/core/dist/server/api/index.js.map +1 -1
- package/packages/core/dist/server/api/project-info.d.ts +11 -0
- package/packages/core/dist/server/api/project-info.d.ts.map +1 -0
- package/packages/core/dist/server/api/project-info.js +18 -0
- package/packages/core/dist/server/api/project-info.js.map +1 -0
- package/packages/core/dist/server/otlp-receiver.d.ts.map +1 -1
- package/packages/core/dist/server/otlp-receiver.js +18 -1
- package/packages/core/dist/server/otlp-receiver.js.map +1 -1
- package/packages/core/dist/server/otlp-receiver.test.js +1 -1
- package/packages/core/dist/server/otlp-receiver.test.js.map +1 -1
- package/packages/core/dist/server/server.d.ts.map +1 -1
- package/packages/core/dist/server/server.js +3 -2
- 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 +8 -8
- package/packages/core/dist/server/settings.d.ts +1 -0
- package/packages/core/dist/server/settings.d.ts.map +1 -1
- package/packages/core/dist/server/settings.js +18 -0
- package/packages/core/dist/server/settings.js.map +1 -1
- package/packages/core/dist/workflow/tandem-workflow-templates.test.js +7 -5
- package/packages/core/dist/workflow/tandem-workflow-templates.test.js.map +1 -1
- package/packages/core/dist/workflow/workflow-migration.test.js +6 -5
- package/packages/core/dist/workflow/workflow-migration.test.js.map +1 -1
- package/packages/core/dist/workflow/workflow-team-templates.test.d.ts +17 -0
- package/packages/core/dist/workflow/workflow-team-templates.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/workflow-team-templates.test.js +275 -0
- package/packages/core/dist/workflow/workflow-team-templates.test.js.map +1 -0
- package/pennyfarthing-dist/agents/dev.md +19 -4
- package/pennyfarthing-dist/agents/devops.md +2 -10
- package/pennyfarthing-dist/agents/reviewer-preflight.md +4 -5
- package/pennyfarthing-dist/agents/reviewer.md +17 -4
- package/pennyfarthing-dist/agents/sm-finish.md +1 -1
- package/pennyfarthing-dist/agents/sm-setup.md +7 -7
- package/pennyfarthing-dist/agents/sm.md +16 -29
- package/pennyfarthing-dist/agents/tea.md +2 -2
- package/pennyfarthing-dist/agents/testing-runner.md +1 -1
- package/pennyfarthing-dist/commands/pf-architect.md +1 -1
- package/pennyfarthing-dist/commands/pf-ba.md +1 -1
- package/pennyfarthing-dist/commands/pf-chore.md +2 -2
- package/pennyfarthing-dist/commands/pf-dev.md +1 -1
- package/pennyfarthing-dist/commands/pf-devops.md +1 -1
- package/pennyfarthing-dist/commands/pf-epic.md +6 -6
- package/pennyfarthing-dist/commands/pf-git.md +10 -10
- package/pennyfarthing-dist/commands/pf-health-check.md +31 -12
- package/pennyfarthing-dist/commands/pf-help.md +12 -12
- package/pennyfarthing-dist/commands/pf-orchestrator.md +1 -1
- package/pennyfarthing-dist/commands/pf-pm.md +1 -1
- package/pennyfarthing-dist/commands/pf-prime.md +8 -8
- package/pennyfarthing-dist/commands/pf-reviewer.md +1 -1
- package/pennyfarthing-dist/commands/pf-session.md +7 -7
- package/pennyfarthing-dist/commands/pf-sm.md +1 -1
- package/pennyfarthing-dist/commands/pf-sprint.md +7 -7
- package/pennyfarthing-dist/commands/pf-tea.md +1 -1
- package/pennyfarthing-dist/commands/pf-tech-writer.md +1 -1
- package/pennyfarthing-dist/commands/pf-theme.md +9 -9
- package/pennyfarthing-dist/commands/pf-ux-designer.md +1 -1
- package/pennyfarthing-dist/commands/pf-work.md +1 -1
- package/pennyfarthing-dist/gates/{confidence-sm.md → confidence.md} +16 -17
- package/pennyfarthing-dist/gates/dev-exit.md +75 -0
- package/pennyfarthing-dist/gates/merge-ready.md +49 -0
- package/pennyfarthing-dist/gates/release-ready.md +95 -0
- package/pennyfarthing-dist/gates/reviewer-preflight-check.md +90 -0
- package/pennyfarthing-dist/gates/sm-setup-exit.md +82 -0
- package/pennyfarthing-dist/guides/agent-behavior.md +129 -20
- package/pennyfarthing-dist/guides/agent-coordination.md +10 -10
- package/pennyfarthing-dist/guides/agent-tag-taxonomy.md +6 -6
- package/pennyfarthing-dist/guides/agent-template-tactical.md +1 -1
- package/pennyfarthing-dist/guides/bell-mode.md +1 -1
- package/pennyfarthing-dist/guides/bikerack.md +10 -10
- package/pennyfarthing-dist/guides/brownfield-tools.md +24 -24
- package/pennyfarthing-dist/guides/command-tag-taxonomy.md +1 -1
- package/pennyfarthing-dist/guides/gate-schema.md +2 -2
- package/pennyfarthing-dist/guides/gates.md +10 -5
- package/pennyfarthing-dist/guides/handoff-cli.md +8 -8
- package/pennyfarthing-dist/guides/hooks.md +27 -27
- package/pennyfarthing-dist/guides/prime.md +2 -2
- package/pennyfarthing-dist/guides/reflector.md +1 -1
- package/pennyfarthing-dist/guides/skill-schema.md +6 -6
- package/pennyfarthing-dist/guides/tandem-protocol.md +3 -3
- package/pennyfarthing-dist/guides/workflow-schema.md +1 -1
- package/pennyfarthing-dist/guides/worktree-mode.md +3 -3
- package/pennyfarthing-dist/guides/xml-tags.md +8 -8
- package/pennyfarthing-dist/scripts/README.md +4 -4
- package/pennyfarthing-dist/scripts/core/agent-session.sh +2 -5
- package/pennyfarthing-dist/scripts/core/check-context.sh +1 -1
- package/pennyfarthing-dist/scripts/core/pf.sh +5 -0
- package/pennyfarthing-dist/scripts/core/phase-check-start.sh +2 -5
- package/pennyfarthing-dist/scripts/core/prime.sh +2 -25
- package/pennyfarthing-dist/scripts/git/README.md +14 -14
- package/pennyfarthing-dist/scripts/git/create-feature-branches.sh +2 -3
- package/pennyfarthing-dist/scripts/git/git-status-all.sh +2 -3
- package/pennyfarthing-dist/scripts/git/install-git-hooks.sh +2 -3
- package/pennyfarthing-dist/scripts/git/worktree-manager.sh +2 -4
- package/pennyfarthing-dist/scripts/hooks/README.md +6 -6
- package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +3 -3
- package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +3 -3
- package/pennyfarthing-dist/scripts/hooks/context-warning.sh +3 -3
- package/pennyfarthing-dist/scripts/hooks/cyclist-pretooluse-hook.sh +3 -3
- package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +5 -4
- package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +2 -1
- package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +3 -3
- package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +3 -3
- package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +3 -3
- package/pennyfarthing-dist/scripts/hooks/session-start.sh +3 -3
- package/pennyfarthing-dist/scripts/hooks/session-stop.sh +3 -3
- package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +3 -3
- package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +3 -4
- package/pennyfarthing-dist/scripts/lib/env.sh +34 -0
- package/pennyfarthing-dist/scripts/lib/find-root.sh +5 -0
- package/pennyfarthing-dist/scripts/lib/run-pf.sh +43 -0
- package/pennyfarthing-dist/scripts/misc/README.md +1 -1
- package/pennyfarthing-dist/scripts/misc/statusline.sh +3 -3
- package/pennyfarthing-dist/scripts/sprint/README.md +21 -21
- package/pennyfarthing-dist/scripts/workflow/README.md +2 -2
- package/pennyfarthing-dist/scripts/workflow/finish-story.sh +2 -16
- package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +3 -3
- package/pennyfarthing-dist/scripts/workflow/get-workflow-type.sh +3 -3
- package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +3 -3
- package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +3 -3
- package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +3 -3
- package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +3 -3
- package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +3 -3
- package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +3 -3
- package/pennyfarthing-dist/skills/pf-bc/examples.md +23 -23
- package/pennyfarthing-dist/skills/pf-bc/skill.md +17 -17
- package/pennyfarthing-dist/skills/pf-bc/usage.md +8 -8
- package/pennyfarthing-dist/skills/pf-jira/SKILL.md +15 -15
- package/pennyfarthing-dist/skills/pf-jira/examples.md +48 -48
- package/pennyfarthing-dist/skills/pf-jira/usage.md +15 -15
- package/pennyfarthing-dist/skills/pf-settings/skill.md +42 -0
- package/pennyfarthing-dist/skills/pf-sprint/examples.md +80 -80
- package/pennyfarthing-dist/skills/pf-sprint/skill.md +35 -35
- package/pennyfarthing-dist/skills/pf-sprint/usage.md +30 -30
- package/pennyfarthing-dist/skills/pf-theme/examples.md +15 -15
- package/pennyfarthing-dist/skills/pf-theme/skill.md +6 -6
- package/pennyfarthing-dist/skills/pf-theme/usage.md +5 -5
- package/pennyfarthing-dist/skills/pf-workflow/examples.md +27 -27
- package/pennyfarthing-dist/skills/pf-workflow/skill.md +11 -11
- package/pennyfarthing-dist/skills/pf-workflow/usage.md +11 -11
- package/pennyfarthing-dist/skills/skill-registry.yaml +34 -19
- package/pennyfarthing-dist/templates/pyproject.toml +27 -0
- package/pennyfarthing-dist/templates/settings.local.json.template +11 -11
- package/pennyfarthing-dist/workflows/bdd-tandem.yaml +7 -3
- package/pennyfarthing-dist/workflows/bdd-team.yaml +89 -0
- package/pennyfarthing-dist/workflows/bdd.yaml +7 -3
- package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-05-import-to-future.md +1 -1
- package/pennyfarthing-dist/workflows/git-cleanup/steps/step-01-analyze.md +1 -1
- package/pennyfarthing-dist/workflows/git-cleanup/steps/step-04-verify.md +1 -1
- package/pennyfarthing-dist/workflows/git-cleanup/steps/step-05-complete.md +1 -1
- package/pennyfarthing-dist/workflows/installation-check/steps/step-01-foundation.md +77 -0
- package/pennyfarthing-dist/workflows/installation-check/steps/step-02-commands.md +82 -0
- package/pennyfarthing-dist/workflows/installation-check/steps/step-03-hooks.md +121 -0
- package/pennyfarthing-dist/workflows/installation-check/steps/step-04-scripts.md +83 -0
- package/pennyfarthing-dist/workflows/installation-check/steps/step-05-layout.md +81 -0
- package/pennyfarthing-dist/workflows/installation-check/steps/step-06-legacy.md +94 -0
- package/pennyfarthing-dist/workflows/installation-check/steps/step-07-tools.md +80 -0
- package/pennyfarthing-dist/workflows/installation-check/steps/step-08-summary.md +99 -0
- package/pennyfarthing-dist/workflows/installation-check/workflow.yaml +47 -0
- package/pennyfarthing-dist/workflows/project-setup/steps/step-01-discover.md +47 -0
- package/pennyfarthing-dist/workflows/tdd-tandem.yaml +7 -3
- package/pennyfarthing-dist/workflows/tdd-team.yaml +80 -0
- package/pennyfarthing-dist/workflows/tdd.yaml +7 -3
- package/pennyfarthing-dist/workflows/trivial.yaml +7 -3
- package/pennyfarthing_scripts/__init__.py +1 -1
- package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/context.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/context.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/__pycache__/focus.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/bc/__pycache__/focus.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/__pycache__/split.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/cli.py +23 -2
- package/pennyfarthing_scripts/bc/focus.py +1 -0
- package/pennyfarthing_scripts/bc/split.py +52 -0
- package/pennyfarthing_scripts/bellmode_hook.py +2 -5
- package/pennyfarthing_scripts/bikerack/__pycache__/__init__.cpython-311.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-311.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-311.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 +48 -6
- package/pennyfarthing_scripts/bikerack/context_meter_footer.py +53 -3
- package/pennyfarthing_scripts/bikerack/launcher.py +6 -6
- package/pennyfarthing_scripts/bikerack/progress_panel.py +0 -1
- package/pennyfarthing_scripts/bikerack/sprint_panel.py +1 -1
- package/pennyfarthing_scripts/bikerack/story_detail_data.py +4 -1
- package/pennyfarthing_scripts/bikerack/story_detail_screen.py +2 -1
- package/pennyfarthing_scripts/bikerack/tui.py +214 -10
- package/pennyfarthing_scripts/bikerack/ws_client.py +2 -2
- package/pennyfarthing_scripts/cli.py +5 -0
- package/pennyfarthing_scripts/common/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/config.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/output.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/pr_config.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/config.py +29 -2
- package/pennyfarthing_scripts/consultation/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/consultation/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/consultation/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/consultation/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/consultation/cli.py +3 -3
- package/pennyfarthing_scripts/context.py +3 -3
- package/pennyfarthing_scripts/deadcode/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/deadcode/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/epic/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/epic/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/git/hooks_installer.py +2 -3
- package/pennyfarthing_scripts/git/status_all.py +1 -1
- package/pennyfarthing_scripts/git/worktree.py +2 -2
- package/pennyfarthing_scripts/git_group/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/git_group/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/git_group/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/cli.cpython-311.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__/marker.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/phase_check.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/analyze.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/models.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__init__.py +8 -3
- package/pennyfarthing_scripts/hooks/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/bell_mode.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/bell_mode.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/context_breaker.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/context_breaker.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/context_warning.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/context_warning.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/cyclist_pretooluse.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/cyclist_pretooluse.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/pre_edit_check.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/pre_edit_check.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/reflector_check.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/reflector_check.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/schema_validation.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/schema_validation.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/session_start.cpython-311.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-311.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/sprint_yaml_validation.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/statusline.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/statusline.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/bell_mode.py +0 -1
- package/pennyfarthing_scripts/hooks/pre_edit_check.py +0 -1
- package/pennyfarthing_scripts/hooks/reflector_check.py +1 -2
- package/pennyfarthing_scripts/hooks/schema_validation.py +0 -1
- package/pennyfarthing_scripts/hooks/session_start.py +6 -8
- package/pennyfarthing_scripts/hooks/statusline.py +10 -1
- package/pennyfarthing_scripts/hotspots/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/hotspots/__pycache__/analyze.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/hotspots/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/hotspots/__pycache__/models.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/client.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/create.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/operations.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/reconcile.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/story.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/launch/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/launch/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/heatmap.py +3 -15
- package/pennyfarthing_scripts/session/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/session/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/settings/__init__.py +0 -0
- package/pennyfarthing_scripts/settings/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/settings/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/settings/__pycache__/settings.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/settings/cli.py +55 -0
- package/pennyfarthing_scripts/settings/settings.py +98 -0
- package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/epic_add.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/epic_update.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/status.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/story_add.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/validate_cmd.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/yaml_io.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/sprint/cli.py +121 -0
- package/pennyfarthing_scripts/sprint/loader.py +154 -3
- package/pennyfarthing_scripts/sprint/story_update.py +7 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_bikerack.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/test_bikerack.py +26 -26
- package/pennyfarthing_scripts/tests/test_confidence_sm_gate.py +17 -16
- package/pennyfarthing_scripts/tests/test_dialogue_manager.py +0 -1
- package/pennyfarthing_scripts/tests/test_resolve_gate_file_field.py +45 -47
- package/pennyfarthing_scripts/tests/test_workflow_list_team.py +143 -0
- package/pennyfarthing_scripts/theme/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/theme/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/validate/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/team_mode.py +323 -0
- package/pennyfarthing_scripts/workflow/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/cli.cpython-311.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-311.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/scale.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/state.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/state.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/cli.py +15 -14
- package/pennyfarthing_scripts/workflow/state.py +0 -1
- package/pennyfarthing_scripts/workflow/team_lifecycle.py +3 -4
|
@@ -10,6 +10,7 @@ Panel navigation: Mount all panels, tab bar, keyboard switching, command palette
|
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
|
+
import os
|
|
13
14
|
from functools import partial
|
|
14
15
|
from pathlib import Path
|
|
15
16
|
from typing import Any
|
|
@@ -105,6 +106,14 @@ PANEL_DISPLAY_NAMES: dict[str, str] = {
|
|
|
105
106
|
# Keys from PANEL_REGISTRY for fast lookup
|
|
106
107
|
_PANEL_KEYS = [key for key, _ in PANEL_REGISTRY]
|
|
107
108
|
|
|
109
|
+
# Split-pane layout presets: name → (left_panel, right_panel)
|
|
110
|
+
# Story 110-4: Named presets for common side-by-side views.
|
|
111
|
+
SPLIT_PRESETS: dict[str, tuple[str, str]] = {
|
|
112
|
+
"sprint+diffs": ("sprint", "diffs"),
|
|
113
|
+
"changed+diffs": ("changed", "diffs"),
|
|
114
|
+
"progress+debug": ("progress", "debug"),
|
|
115
|
+
}
|
|
116
|
+
|
|
108
117
|
|
|
109
118
|
class BindingFooter(Footer):
|
|
110
119
|
"""Footer subclass that exposes active binding text via render().
|
|
@@ -359,9 +368,10 @@ class BikeRackApp(App):
|
|
|
359
368
|
class FocusUpdate(Message, bubble=False):
|
|
360
369
|
"""Focus change from WS — routed through Textual message system."""
|
|
361
370
|
|
|
362
|
-
def __init__(self, focus: str | None) -> None:
|
|
371
|
+
def __init__(self, focus: str | None, split_config: dict | None = None) -> None:
|
|
363
372
|
super().__init__()
|
|
364
373
|
self.focus = focus
|
|
374
|
+
self.split_config = split_config
|
|
365
375
|
|
|
366
376
|
class WsStateUpdate(Message, bubble=False):
|
|
367
377
|
"""WS connection state change — routed through Textual message system."""
|
|
@@ -392,6 +402,11 @@ class BikeRackApp(App):
|
|
|
392
402
|
height: auto;
|
|
393
403
|
width: 1fr;
|
|
394
404
|
}
|
|
405
|
+
#project-dir {
|
|
406
|
+
height: 1;
|
|
407
|
+
padding: 0 1;
|
|
408
|
+
color: $text-muted;
|
|
409
|
+
}
|
|
395
410
|
Tabs {
|
|
396
411
|
dock: top;
|
|
397
412
|
}
|
|
@@ -407,12 +422,23 @@ class BikeRackApp(App):
|
|
|
407
422
|
ContextMeterFooter {
|
|
408
423
|
height: 1;
|
|
409
424
|
}
|
|
425
|
+
#split-container {
|
|
426
|
+
display: none;
|
|
427
|
+
height: 1fr;
|
|
428
|
+
}
|
|
429
|
+
#split-left {
|
|
430
|
+
width: 1fr;
|
|
431
|
+
}
|
|
432
|
+
#split-right {
|
|
433
|
+
width: 1fr;
|
|
434
|
+
}
|
|
410
435
|
"""
|
|
411
436
|
|
|
412
437
|
COMMANDS = App.COMMANDS | {PanelCommands}
|
|
413
438
|
|
|
414
439
|
BINDINGS = [
|
|
415
440
|
Binding("q", "quit", "Quit"),
|
|
441
|
+
Binding("shift+s", "toggle_split", "Split"),
|
|
416
442
|
Binding("1", "switch_panel('sprint')", "Sprint", show=False),
|
|
417
443
|
Binding("2", "switch_panel('git')", "Git", show=False),
|
|
418
444
|
Binding("3", "switch_panel('diffs')", "Diffs", show=False),
|
|
@@ -423,8 +449,8 @@ class BikeRackApp(App):
|
|
|
423
449
|
Binding("8", "switch_panel('progress')", "Progress", show=False),
|
|
424
450
|
Binding("bracketright", "next_panel", "]Next"),
|
|
425
451
|
Binding("bracketleft", "prev_panel", "[Prev"),
|
|
426
|
-
Binding("tab", "next_panel", show=False),
|
|
427
|
-
Binding("shift+tab", "prev_panel", show=False),
|
|
452
|
+
Binding("tab", "next_panel", show=False, priority=True),
|
|
453
|
+
Binding("shift+tab", "prev_panel", show=False, priority=True),
|
|
428
454
|
Binding("n", "next_diff_file", "Next file", show=False),
|
|
429
455
|
Binding("p", "prev_diff_file", "Prev file", show=False),
|
|
430
456
|
Binding("j", "next_epic", show=False),
|
|
@@ -442,10 +468,20 @@ class BikeRackApp(App):
|
|
|
442
468
|
self._focused_panel: str = "sprint"
|
|
443
469
|
self._previous_panel: str | None = None
|
|
444
470
|
self._programmatic_tab_count: int = 0
|
|
471
|
+
self._context_meter: ContextMeterFooter | None = None
|
|
472
|
+
# Split-pane state (Story 110-4)
|
|
473
|
+
self._split_mode: bool = False
|
|
474
|
+
self._active_split_pane: str = "left"
|
|
475
|
+
self._split_left_key: str = "sprint"
|
|
476
|
+
self._split_right_key: str = "diffs"
|
|
445
477
|
|
|
446
478
|
def compose(self) -> ComposeResult:
|
|
479
|
+
project_dir_name = Path(
|
|
480
|
+
os.environ.get("CYCLIST_PROJECT_DIR", os.getcwd())
|
|
481
|
+
).name
|
|
447
482
|
yield Header()
|
|
448
483
|
yield AgentHeader(id="agent-header")
|
|
484
|
+
yield Static(f"[dim]{project_dir_name}[/dim]", id="project-dir")
|
|
449
485
|
yield Tabs(*_build_panel_tabs(), id="tab-bar")
|
|
450
486
|
yield ConnectionStatus(
|
|
451
487
|
STATE_DISPLAY[ConnectionState.DISCONNECTED],
|
|
@@ -460,7 +496,11 @@ class BikeRackApp(App):
|
|
|
460
496
|
yield AuditLogPanel(client=self._client, id="panel-audit-log")
|
|
461
497
|
yield DebugPanel(client=self._client, id="panel-debug")
|
|
462
498
|
yield ProgressPanel(client=self._client, id="panel-progress")
|
|
463
|
-
|
|
499
|
+
with Horizontal(id="split-container"):
|
|
500
|
+
yield VerticalScroll(id="split-left")
|
|
501
|
+
yield VerticalScroll(id="split-right")
|
|
502
|
+
self._context_meter = ContextMeterFooter(client=self._client)
|
|
503
|
+
yield self._context_meter
|
|
464
504
|
yield BindingFooter()
|
|
465
505
|
|
|
466
506
|
async def on_mount(self) -> None:
|
|
@@ -533,8 +573,27 @@ class BikeRackApp(App):
|
|
|
533
573
|
save_last_panel(key, project_dir=None)
|
|
534
574
|
self._update_tab_bar(key)
|
|
535
575
|
|
|
576
|
+
# Refresh context meter on panel switch (110-12)
|
|
577
|
+
if self._context_meter is not None:
|
|
578
|
+
self._context_meter.request_refresh()
|
|
579
|
+
|
|
536
580
|
def action_next_panel(self) -> None:
|
|
537
|
-
"""Cycle to the next panel."""
|
|
581
|
+
"""Cycle to the next panel, or toggle pane focus in split mode."""
|
|
582
|
+
if self._split_mode:
|
|
583
|
+
# In split mode, Tab toggles between left and right pane
|
|
584
|
+
if self._active_split_pane == "left":
|
|
585
|
+
self._active_split_pane = "right"
|
|
586
|
+
try:
|
|
587
|
+
self.query_one("#split-right").focus()
|
|
588
|
+
except Exception:
|
|
589
|
+
pass
|
|
590
|
+
else:
|
|
591
|
+
self._active_split_pane = "left"
|
|
592
|
+
try:
|
|
593
|
+
self.query_one("#split-left").focus()
|
|
594
|
+
except Exception:
|
|
595
|
+
pass
|
|
596
|
+
return
|
|
538
597
|
try:
|
|
539
598
|
idx = _PANEL_KEYS.index(self._focused_panel)
|
|
540
599
|
except ValueError:
|
|
@@ -596,6 +655,131 @@ class BikeRackApp(App):
|
|
|
596
655
|
except Exception:
|
|
597
656
|
pass
|
|
598
657
|
|
|
658
|
+
# ------------------------------------------------------------------
|
|
659
|
+
# Split-pane layout (Story 110-4)
|
|
660
|
+
# ------------------------------------------------------------------
|
|
661
|
+
|
|
662
|
+
def _sync_reparent(self, widget: Any, new_parent: Any) -> None:
|
|
663
|
+
"""Move widget from current parent to new parent synchronously.
|
|
664
|
+
|
|
665
|
+
Uses internal Textual DOM API so the move is visible immediately
|
|
666
|
+
without awaiting an async mount/remove cycle.
|
|
667
|
+
"""
|
|
668
|
+
old_parent = widget._parent
|
|
669
|
+
if old_parent is not None:
|
|
670
|
+
try:
|
|
671
|
+
old_parent._nodes._remove(widget)
|
|
672
|
+
except (ValueError, AttributeError):
|
|
673
|
+
# Fallback: remove from internal list directly
|
|
674
|
+
try:
|
|
675
|
+
old_parent._nodes._nodes.remove(widget)
|
|
676
|
+
except Exception:
|
|
677
|
+
pass
|
|
678
|
+
try:
|
|
679
|
+
new_parent._nodes._append(widget)
|
|
680
|
+
except AttributeError:
|
|
681
|
+
new_parent._nodes._nodes.append(widget)
|
|
682
|
+
widget._parent = new_parent
|
|
683
|
+
|
|
684
|
+
def _enter_split(self, left_key: str, right_key: str) -> None:
|
|
685
|
+
"""Activate split mode with specified panels (synchronous)."""
|
|
686
|
+
if left_key not in _PANEL_KEYS or right_key not in _PANEL_KEYS:
|
|
687
|
+
return
|
|
688
|
+
if left_key == right_key:
|
|
689
|
+
return
|
|
690
|
+
|
|
691
|
+
self._split_mode = True
|
|
692
|
+
self._split_left_key = left_key
|
|
693
|
+
self._split_right_key = right_key
|
|
694
|
+
self._active_split_pane = "left"
|
|
695
|
+
|
|
696
|
+
try:
|
|
697
|
+
main_content = self.query_one("#main-content")
|
|
698
|
+
split_container = self.query_one("#split-container")
|
|
699
|
+
split_left = self.query_one("#split-left")
|
|
700
|
+
split_right = self.query_one("#split-right")
|
|
701
|
+
except Exception:
|
|
702
|
+
return
|
|
703
|
+
|
|
704
|
+
# Move left panel to split-left pane
|
|
705
|
+
try:
|
|
706
|
+
left_panel = self.query_one(f"#panel-{left_key}")
|
|
707
|
+
left_panel.display = True
|
|
708
|
+
self._sync_reparent(left_panel, split_left)
|
|
709
|
+
except Exception:
|
|
710
|
+
pass
|
|
711
|
+
|
|
712
|
+
# Move right panel to split-right pane
|
|
713
|
+
try:
|
|
714
|
+
right_panel = self.query_one(f"#panel-{right_key}")
|
|
715
|
+
right_panel.display = True
|
|
716
|
+
self._sync_reparent(right_panel, split_right)
|
|
717
|
+
except Exception:
|
|
718
|
+
pass
|
|
719
|
+
|
|
720
|
+
# Hide all other panels remaining in main-content
|
|
721
|
+
for panel_key in _PANEL_KEYS:
|
|
722
|
+
if panel_key not in (left_key, right_key):
|
|
723
|
+
try:
|
|
724
|
+
p = self.query_one(f"#panel-{panel_key}")
|
|
725
|
+
p.display = False
|
|
726
|
+
except Exception:
|
|
727
|
+
pass
|
|
728
|
+
|
|
729
|
+
main_content.display = False
|
|
730
|
+
split_container.display = True
|
|
731
|
+
|
|
732
|
+
def _exit_split(self) -> None:
|
|
733
|
+
"""Deactivate split mode, return panels to main-content."""
|
|
734
|
+
self._split_mode = False
|
|
735
|
+
|
|
736
|
+
try:
|
|
737
|
+
main_content = self.query_one("#main-content")
|
|
738
|
+
split_container = self.query_one("#split-container")
|
|
739
|
+
split_left = self.query_one("#split-left")
|
|
740
|
+
split_right = self.query_one("#split-right")
|
|
741
|
+
except Exception:
|
|
742
|
+
return
|
|
743
|
+
|
|
744
|
+
# Move panels back from split panes to main-content
|
|
745
|
+
for pane in (split_left, split_right):
|
|
746
|
+
for child in list(pane.children):
|
|
747
|
+
self._sync_reparent(child, main_content)
|
|
748
|
+
|
|
749
|
+
split_container.display = False
|
|
750
|
+
main_content.display = True
|
|
751
|
+
|
|
752
|
+
# Restore single-panel visibility
|
|
753
|
+
for panel_key in _PANEL_KEYS:
|
|
754
|
+
try:
|
|
755
|
+
p = self.query_one(f"#panel-{panel_key}")
|
|
756
|
+
p.display = (panel_key == self._focused_panel)
|
|
757
|
+
except Exception:
|
|
758
|
+
pass
|
|
759
|
+
|
|
760
|
+
def action_toggle_split(self) -> None:
|
|
761
|
+
"""Toggle split mode on/off (Shift+S keybinding)."""
|
|
762
|
+
if self._split_mode:
|
|
763
|
+
self._exit_split()
|
|
764
|
+
else:
|
|
765
|
+
# Default: current panel left, next panel right
|
|
766
|
+
left_key = self._focused_panel
|
|
767
|
+
try:
|
|
768
|
+
idx = _PANEL_KEYS.index(left_key)
|
|
769
|
+
except ValueError:
|
|
770
|
+
idx = 0
|
|
771
|
+
right_key = _PANEL_KEYS[(idx + 1) % len(_PANEL_KEYS)]
|
|
772
|
+
self._enter_split(left_key, right_key)
|
|
773
|
+
|
|
774
|
+
def action_apply_split_preset(self, name: str) -> None:
|
|
775
|
+
"""Apply a named split preset (synchronous)."""
|
|
776
|
+
if name not in SPLIT_PRESETS:
|
|
777
|
+
return
|
|
778
|
+
left_key, right_key = SPLIT_PRESETS[name]
|
|
779
|
+
if self._split_mode:
|
|
780
|
+
self._exit_split()
|
|
781
|
+
self._enter_split(left_key, right_key)
|
|
782
|
+
|
|
599
783
|
def _update_tab_bar(self, panel_key: str) -> None:
|
|
600
784
|
"""Update the tab bar widget with the given panel key.
|
|
601
785
|
|
|
@@ -628,7 +812,10 @@ class BikeRackApp(App):
|
|
|
628
812
|
def _handle_focus_message(self, message: dict[str, Any] | None) -> None:
|
|
629
813
|
"""Handle incoming focus channel messages.
|
|
630
814
|
|
|
631
|
-
Expected format:
|
|
815
|
+
Expected format:
|
|
816
|
+
Single panel: {type: 'update', focus: '<panel>'}
|
|
817
|
+
Split layout: {type: 'update', focus: 'split', split: {left: ..., right: ...}}
|
|
818
|
+
Split preset: {type: 'update', focus: 'split:<preset-name>'}
|
|
632
819
|
Only 'update' messages trigger panel switches (matching React hook).
|
|
633
820
|
Routes through Textual message system via post_message for proper repaint.
|
|
634
821
|
"""
|
|
@@ -638,7 +825,8 @@ class BikeRackApp(App):
|
|
|
638
825
|
return
|
|
639
826
|
if "focus" not in message:
|
|
640
827
|
return
|
|
641
|
-
|
|
828
|
+
split_config = message.get("split")
|
|
829
|
+
self.post_message(self.FocusUpdate(message["focus"], split_config=split_config))
|
|
642
830
|
|
|
643
831
|
def _handle_persona_message(self, message: dict[str, Any] | None) -> None:
|
|
644
832
|
"""Handle incoming persona channel messages.
|
|
@@ -667,7 +855,23 @@ class BikeRackApp(App):
|
|
|
667
855
|
def on_bike_rack_app_focus_update(self, event: FocusUpdate) -> None:
|
|
668
856
|
"""Apply focus change in Textual message context."""
|
|
669
857
|
focus = event.focus
|
|
670
|
-
|
|
858
|
+
split_config = event.split_config
|
|
859
|
+
|
|
860
|
+
if focus == "split" and split_config:
|
|
861
|
+
# Explicit split layout: {focus: "split", split: {left: ..., right: ...}}
|
|
862
|
+
left = split_config.get("left", "sprint")
|
|
863
|
+
right = split_config.get("right", "diffs")
|
|
864
|
+
if self._split_mode:
|
|
865
|
+
self._exit_split()
|
|
866
|
+
self._enter_split(left, right)
|
|
867
|
+
elif focus is not None and focus.startswith("split:"):
|
|
868
|
+
# Preset reference: {focus: "split:progress+debug"}
|
|
869
|
+
preset_name = focus[len("split:"):]
|
|
870
|
+
self.action_apply_split_preset(preset_name)
|
|
871
|
+
elif focus is not None and focus in _PANEL_KEYS:
|
|
872
|
+
# Single panel focus — exit split if active
|
|
873
|
+
if self._split_mode:
|
|
874
|
+
self._exit_split()
|
|
671
875
|
self.action_switch_panel(focus)
|
|
672
876
|
elif focus is not None:
|
|
673
877
|
self._previous_panel = self._focused_panel
|
|
@@ -693,7 +897,7 @@ def main(
|
|
|
693
897
|
"""Launch BikeRack TUI as a standalone application.
|
|
694
898
|
|
|
695
899
|
Args:
|
|
696
|
-
port: Explicit WheelHub port. If None, reads from .
|
|
900
|
+
port: Explicit WheelHub port. If None, reads from .bikerack-port file.
|
|
697
901
|
project_dir: Project directory for port file discovery. Defaults to cwd.
|
|
698
902
|
"""
|
|
699
903
|
# Detect terminal image protocol BEFORE App.run() claims the terminal
|
|
@@ -703,7 +907,7 @@ def main(
|
|
|
703
907
|
|
|
704
908
|
if port is None:
|
|
705
909
|
if project_dir is not None:
|
|
706
|
-
port_file = project_dir / ".
|
|
910
|
+
port_file = project_dir / ".bikerack-port"
|
|
707
911
|
if port_file.exists():
|
|
708
912
|
try:
|
|
709
913
|
port = int(port_file.read_text().strip())
|
|
@@ -75,14 +75,14 @@ class WheelHubClient:
|
|
|
75
75
|
cb(new_state)
|
|
76
76
|
|
|
77
77
|
def discover_port(self) -> int:
|
|
78
|
-
"""Read port from .
|
|
78
|
+
"""Read port from .bikerack-port file, fallback to DEFAULT_PORT.
|
|
79
79
|
|
|
80
80
|
Priority: explicit port > port file > DEFAULT_PORT.
|
|
81
81
|
"""
|
|
82
82
|
if self._port is not None:
|
|
83
83
|
return self._port
|
|
84
84
|
if self._project_dir is not None:
|
|
85
|
-
port_file = self._project_dir / ".
|
|
85
|
+
port_file = self._project_dir / ".bikerack-port"
|
|
86
86
|
if port_file.exists():
|
|
87
87
|
try:
|
|
88
88
|
return int(port_file.read_text().strip())
|
|
@@ -147,6 +147,11 @@ from pennyfarthing_scripts.hooks.cli import hooks # noqa: E402
|
|
|
147
147
|
|
|
148
148
|
cli.add_command(hooks)
|
|
149
149
|
|
|
150
|
+
# Import and register settings group
|
|
151
|
+
from pennyfarthing_scripts.settings.cli import settings # noqa: E402
|
|
152
|
+
|
|
153
|
+
cli.add_command(settings)
|
|
154
|
+
|
|
150
155
|
|
|
151
156
|
@cli.group()
|
|
152
157
|
def agent():
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -82,11 +82,38 @@ def load_yaml_config(path: Path) -> dict[str, Any] | None:
|
|
|
82
82
|
return yaml.safe_load(f)
|
|
83
83
|
|
|
84
84
|
|
|
85
|
-
def load_pennyfarthing_config() -> dict[str, Any]:
|
|
85
|
+
def load_pennyfarthing_config(project_root: Path | None = None) -> dict[str, Any]:
|
|
86
86
|
"""Load .pennyfarthing/config.local.yaml.
|
|
87
87
|
|
|
88
|
+
Args:
|
|
89
|
+
project_root: Project root path (defaults to auto-detect)
|
|
90
|
+
|
|
88
91
|
Returns:
|
|
89
92
|
Config dict, or empty dict if not found
|
|
90
93
|
"""
|
|
91
|
-
|
|
94
|
+
root = project_root or get_project_root()
|
|
95
|
+
config_path = root / ".pennyfarthing" / "config.local.yaml"
|
|
92
96
|
return load_yaml_config(config_path) or {}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def save_pennyfarthing_config_key(
|
|
100
|
+
key: str, value: Any, project_root: Path | None = None
|
|
101
|
+
) -> None:
|
|
102
|
+
"""Set a top-level key in .pennyfarthing/config.local.yaml.
|
|
103
|
+
|
|
104
|
+
Creates the file if it doesn't exist. Preserves existing keys.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
key: Top-level key (e.g., "sprint")
|
|
108
|
+
value: Value to set (dict, str, etc.)
|
|
109
|
+
project_root: Project root path (defaults to auto-detect)
|
|
110
|
+
"""
|
|
111
|
+
root = project_root or get_project_root()
|
|
112
|
+
config_path = root / ".pennyfarthing" / "config.local.yaml"
|
|
113
|
+
|
|
114
|
+
config = load_yaml_config(config_path) or {}
|
|
115
|
+
config[key] = value
|
|
116
|
+
|
|
117
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
118
|
+
with open(config_path, "w") as f:
|
|
119
|
+
yaml.dump(config, f, default_flow_style=False, sort_keys=False)
|
|
Binary file
|
|
Binary file
|
|
@@ -6,7 +6,7 @@ the bash wrapper dialogue-manager.sh interface.
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
-
from datetime import
|
|
9
|
+
from datetime import UTC, datetime
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
|
|
12
12
|
import click
|
|
@@ -44,7 +44,7 @@ def init(story_id: str, workflow: str, leader: str, partner: str) -> None:
|
|
|
44
44
|
workflow=workflow,
|
|
45
45
|
leader=leader,
|
|
46
46
|
partner=partner,
|
|
47
|
-
started_at=datetime.now(
|
|
47
|
+
started_at=datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
48
48
|
)
|
|
49
49
|
|
|
50
50
|
content = create_dialogue_content(header)
|
|
@@ -80,7 +80,7 @@ def append(story_id: str, question: str, recommendation: str, confidence: str) -
|
|
|
80
80
|
|
|
81
81
|
exchange = DialogueExchange(
|
|
82
82
|
number=next_num,
|
|
83
|
-
timestamp=datetime.now(
|
|
83
|
+
timestamp=datetime.now(UTC).strftime("%H:%M"),
|
|
84
84
|
leader="",
|
|
85
85
|
partner="",
|
|
86
86
|
question=question,
|
|
@@ -269,7 +269,7 @@ def detect_cyclist(project_dir: str | None = None) -> bool:
|
|
|
269
269
|
|
|
270
270
|
Checks:
|
|
271
271
|
1. CYCLIST env var set to '1' (Electron mode - definitive)
|
|
272
|
-
2. .
|
|
272
|
+
2. .bikerack-port file exists AND port is responding (Web mode)
|
|
273
273
|
"""
|
|
274
274
|
# Env var is definitive - set by Cyclist when it spawns Claude
|
|
275
275
|
if os.environ.get("CYCLIST") == "1":
|
|
@@ -284,8 +284,8 @@ def detect_cyclist(project_dir: str | None = None) -> bool:
|
|
|
284
284
|
)
|
|
285
285
|
|
|
286
286
|
port_files = [
|
|
287
|
-
Path(project_dir) / "packages" / "cyclist" / ".
|
|
288
|
-
Path(os.getcwd()) / ".
|
|
287
|
+
Path(project_dir) / "packages" / "cyclist" / ".bikerack-port",
|
|
288
|
+
Path(os.getcwd()) / ".bikerack-port",
|
|
289
289
|
]
|
|
290
290
|
|
|
291
291
|
for port_file in port_files:
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -10,7 +10,6 @@ Usage via CLI:
|
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
|
-
import re
|
|
14
13
|
from pathlib import Path
|
|
15
14
|
|
|
16
15
|
from pennyfarthing_scripts.common.config import get_project_root
|
|
@@ -70,8 +69,8 @@ def install_git_hooks(project_root: Path | None = None) -> int:
|
|
|
70
69
|
return 1
|
|
71
70
|
|
|
72
71
|
print("Installing git hooks with .d/ dispatcher pattern...")
|
|
73
|
-
print(
|
|
74
|
-
print(
|
|
72
|
+
print(" Source: pennyfarthing-dist/scripts/hooks/")
|
|
73
|
+
print(" Dest: .git/hooks/")
|
|
75
74
|
print()
|
|
76
75
|
|
|
77
76
|
for source_file, dest_name in HOOKS:
|
|
@@ -291,7 +291,7 @@ async def main(brief: bool = False) -> int:
|
|
|
291
291
|
Returns:
|
|
292
292
|
0 if all repos clean, 1 if any have changes/unpushed
|
|
293
293
|
"""
|
|
294
|
-
from pennyfarthing_scripts.git.repos import
|
|
294
|
+
from pennyfarthing_scripts.git.repos import get_repo_paths, load_repos_config
|
|
295
295
|
|
|
296
296
|
repos_with_upstream: list[tuple[str, Path, str]] = []
|
|
297
297
|
repo_paths = get_repo_paths()
|
|
@@ -199,7 +199,7 @@ def remove_worktree(name: str) -> int:
|
|
|
199
199
|
shutil.rmtree(wt_path)
|
|
200
200
|
|
|
201
201
|
# Prune worktree references
|
|
202
|
-
for
|
|
202
|
+
for _repo_name, cfg in repos.items():
|
|
203
203
|
full_path = (project_root / cfg.path).resolve()
|
|
204
204
|
if full_path.exists():
|
|
205
205
|
_git(["worktree", "prune"], full_path)
|
|
@@ -277,7 +277,7 @@ def show_worktree_status() -> int:
|
|
|
277
277
|
if repo_wt.exists():
|
|
278
278
|
branch, _ = _git(["branch", "--show-current"], repo_wt)
|
|
279
279
|
status_out, _ = _git(["status", "--short"], repo_wt)
|
|
280
|
-
count = len([
|
|
280
|
+
count = len([line for line in status_out.split("\n") if line.strip()]) if status_out else 0
|
|
281
281
|
print(f" {repo_name} ({cfg.repo_type}): {branch} ({count} uncommitted)")
|
|
282
282
|
|
|
283
283
|
# Check for session files referencing this worktree
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -30,7 +30,7 @@ import yaml
|
|
|
30
30
|
|
|
31
31
|
# WheelHub port file - central coordination server for all communication
|
|
32
32
|
# Per ADR-0004: "the hub where all communication converges"
|
|
33
|
-
CYCLIST_PORT_FILE = ".
|
|
33
|
+
CYCLIST_PORT_FILE = ".bikerack-port"
|
|
34
34
|
|
|
35
35
|
# Default port if file not found
|
|
36
36
|
DEFAULT_CYCLIST_PORT = 7431
|
|
@@ -48,7 +48,7 @@ def find_project_root(start_dir: Path | None = None) -> Path | None:
|
|
|
48
48
|
"""Find the project root by looking for marker files.
|
|
49
49
|
|
|
50
50
|
Searches for (in order):
|
|
51
|
-
1. .
|
|
51
|
+
1. .bikerack-port (WheelHub is running)
|
|
52
52
|
2. .pennyfarthing directory
|
|
53
53
|
3. .claude directory
|
|
54
54
|
|
|
@@ -84,7 +84,7 @@ def read_port_file(file_name: str, project_root: Path | None = None) -> int | No
|
|
|
84
84
|
"""Read a port number from a Cyclist port file.
|
|
85
85
|
|
|
86
86
|
Args:
|
|
87
|
-
file_name: Name of the port file (e.g. .
|
|
87
|
+
file_name: Name of the port file (e.g. .bikerack-port)
|
|
88
88
|
project_root: Project root directory (auto-detected if not provided)
|
|
89
89
|
|
|
90
90
|
Returns:
|
|
@@ -140,6 +140,7 @@ class CyclistSettings:
|
|
|
140
140
|
permission_mode: str = "manual" # plan, manual, accept
|
|
141
141
|
relay_mode: bool = False
|
|
142
142
|
bell_mode: bool = False
|
|
143
|
+
git_monitor: bool = False
|
|
143
144
|
theme: str | None = None
|
|
144
145
|
|
|
145
146
|
|
|
@@ -206,6 +207,10 @@ def load_settings(project_root: Path | None = None) -> CyclistSettings:
|
|
|
206
207
|
if "bell_mode" in workflow and isinstance(workflow["bell_mode"], bool):
|
|
207
208
|
settings.bell_mode = workflow["bell_mode"]
|
|
208
209
|
|
|
210
|
+
# Handle git_monitor
|
|
211
|
+
if "git_monitor" in workflow and isinstance(workflow["git_monitor"], bool):
|
|
212
|
+
settings.git_monitor = workflow["git_monitor"]
|
|
213
|
+
|
|
209
214
|
return settings
|
|
210
215
|
|
|
211
216
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|