@pennyfarthing/core 11.0.0 → 11.1.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 +81 -23
- package/package.json +1 -1
- package/packages/core/dist/cli/utils/010-detect-remove-old-packages.test.d.ts +20 -0
- package/packages/core/dist/cli/utils/010-detect-remove-old-packages.test.d.ts.map +1 -0
- package/packages/core/dist/cli/utils/010-detect-remove-old-packages.test.js +278 -0
- package/packages/core/dist/cli/utils/010-detect-remove-old-packages.test.js.map +1 -0
- package/packages/core/dist/cli/utils/constants.d.ts +8 -2
- package/packages/core/dist/cli/utils/constants.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/constants.js +4 -1
- package/packages/core/dist/cli/utils/constants.js.map +1 -1
- package/packages/core/dist/cli/utils/constants.test.d.ts +10 -0
- package/packages/core/dist/cli/utils/constants.test.d.ts.map +1 -0
- package/packages/core/dist/cli/utils/constants.test.js +38 -0
- package/packages/core/dist/cli/utils/constants.test.js.map +1 -0
- package/packages/core/dist/consultation/consultation-protocol.d.ts +139 -0
- package/packages/core/dist/consultation/consultation-protocol.d.ts.map +1 -0
- package/packages/core/dist/consultation/consultation-protocol.js +178 -0
- package/packages/core/dist/consultation/consultation-protocol.js.map +1 -0
- package/packages/core/dist/consultation/consultation-protocol.test.d.ts +20 -0
- package/packages/core/dist/consultation/consultation-protocol.test.d.ts.map +1 -0
- package/packages/core/dist/consultation/consultation-protocol.test.js +474 -0
- package/packages/core/dist/consultation/consultation-protocol.test.js.map +1 -0
- package/packages/core/dist/consultation/dialogue-manager.d.ts +75 -0
- package/packages/core/dist/consultation/dialogue-manager.d.ts.map +1 -0
- package/packages/core/dist/consultation/dialogue-manager.js +334 -0
- package/packages/core/dist/consultation/dialogue-manager.js.map +1 -0
- package/packages/core/dist/consultation/dialogue-manager.test.d.ts +19 -0
- package/packages/core/dist/consultation/dialogue-manager.test.d.ts.map +1 -0
- package/packages/core/dist/consultation/dialogue-manager.test.js +444 -0
- package/packages/core/dist/consultation/dialogue-manager.test.js.map +1 -0
- package/packages/core/dist/public/js/react/react.js +3 -3
- package/packages/core/dist/scripts/theme-detail.test.d.ts +10 -0
- package/packages/core/dist/scripts/theme-detail.test.js +199 -0
- package/packages/core/dist/server/api/git.d.ts +13 -1
- package/packages/core/dist/server/api/git.d.ts.map +1 -1
- package/packages/core/dist/server/api/git.js +53 -34
- package/packages/core/dist/server/api/git.js.map +1 -1
- package/packages/core/dist/server/api/health-score.d.ts.map +1 -1
- package/packages/core/dist/server/api/health-score.js +25 -1
- package/packages/core/dist/server/api/health-score.js.map +1 -1
- package/packages/core/dist/server/api/settings.d.ts.map +1 -1
- package/packages/core/dist/server/api/settings.js +63 -1
- package/packages/core/dist/server/api/settings.js.map +1 -1
- package/packages/core/dist/server/api/theme-agents.d.ts.map +1 -1
- package/packages/core/dist/server/api/theme-agents.js +61 -0
- package/packages/core/dist/server/api/theme-agents.js.map +1 -1
- package/packages/core/dist/server/server.d.ts.map +1 -1
- package/packages/core/dist/server/server.js +17 -12
- package/packages/core/dist/server/server.js.map +1 -1
- package/packages/core/dist/shared/skill-search.test.js +2 -2
- package/packages/core/dist/workflow/gate-file-validation.d.ts +49 -0
- package/packages/core/dist/workflow/gate-file-validation.d.ts.map +1 -0
- package/packages/core/dist/workflow/gate-file-validation.js +157 -0
- package/packages/core/dist/workflow/gate-file-validation.js.map +1 -0
- package/packages/core/dist/workflow/gate-file-validation.test.d.ts +19 -0
- package/packages/core/dist/workflow/gate-file-validation.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/gate-file-validation.test.js +536 -0
- package/packages/core/dist/workflow/gate-file-validation.test.js.map +1 -0
- package/packages/core/dist/workflow/gate-schema-validation.test.d.ts +14 -0
- package/packages/core/dist/workflow/gate-schema-validation.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/gate-schema-validation.test.js +339 -0
- package/packages/core/dist/workflow/gate-schema-validation.test.js.map +1 -0
- package/packages/core/dist/workflow/handoff.js +2 -2
- package/packages/core/dist/workflow/handoff.js.map +1 -1
- package/packages/core/dist/workflow/handoff.test.js +16 -0
- package/packages/core/dist/workflow/handoff.test.js.map +1 -1
- package/packages/core/dist/workflow/workflow-schema.d.ts +4 -2
- package/packages/core/dist/workflow/workflow-schema.d.ts.map +1 -1
- package/packages/core/dist/workflow/workflow-schema.js +43 -8
- package/packages/core/dist/workflow/workflow-schema.js.map +1 -1
- package/pennyfarthing-dist/agents/README.md +6 -14
- package/pennyfarthing-dist/agents/architect.md +43 -29
- package/pennyfarthing-dist/agents/ba.md +30 -29
- package/pennyfarthing-dist/agents/dev.md +32 -43
- package/pennyfarthing-dist/agents/devops.md +57 -21
- package/pennyfarthing-dist/agents/orchestrator.md +3 -10
- package/pennyfarthing-dist/agents/pm.md +45 -31
- package/pennyfarthing-dist/agents/reviewer.md +20 -66
- package/pennyfarthing-dist/agents/sm-setup.md +2 -2
- package/pennyfarthing-dist/agents/sm.md +8 -30
- package/pennyfarthing-dist/agents/tea.md +25 -41
- package/pennyfarthing-dist/agents/tech-writer.md +33 -90
- package/pennyfarthing-dist/agents/ux-designer.md +39 -39
- package/pennyfarthing-dist/commands/benchmark-control.md +8 -64
- package/pennyfarthing-dist/commands/benchmark.md +8 -480
- package/pennyfarthing-dist/commands/job-fair.md +8 -97
- package/pennyfarthing-dist/commands/pf-benchmark-control.md +70 -0
- package/pennyfarthing-dist/commands/pf-benchmark.md +486 -0
- package/pennyfarthing-dist/commands/pf-chore.md +4 -4
- package/pennyfarthing-dist/commands/pf-ci.md +40 -0
- package/pennyfarthing-dist/commands/pf-close-epic.md +9 -27
- package/pennyfarthing-dist/commands/pf-continue-session.md +9 -213
- package/pennyfarthing-dist/commands/pf-create-branches-from-story.md +11 -353
- package/pennyfarthing-dist/commands/pf-docs.md +28 -0
- package/pennyfarthing-dist/commands/pf-epic.md +67 -0
- package/pennyfarthing-dist/commands/pf-git-cleanup.md +11 -52
- package/pennyfarthing-dist/commands/pf-git.md +75 -0
- package/pennyfarthing-dist/commands/pf-help.md +110 -128
- package/pennyfarthing-dist/commands/pf-job-fair.md +102 -0
- package/pennyfarthing-dist/commands/pf-new-work.md +9 -18
- package/pennyfarthing-dist/commands/pf-parallel-work.md +6 -66
- package/pennyfarthing-dist/commands/pf-release.md +11 -76
- package/pennyfarthing-dist/commands/pf-repo-status.md +11 -44
- package/pennyfarthing-dist/commands/pf-run-ci.md +8 -111
- package/pennyfarthing-dist/commands/pf-session.md +51 -0
- package/pennyfarthing-dist/commands/pf-solo.md +447 -0
- package/pennyfarthing-dist/commands/pf-sprint-planning.md +8 -104
- package/pennyfarthing-dist/commands/pf-standalone.md +1 -1
- package/pennyfarthing-dist/commands/pf-start-epic.md +9 -163
- package/pennyfarthing-dist/commands/pf-sync-epic-to-jira.md +8 -179
- package/pennyfarthing-dist/commands/pf-sync-work-with-sprint.md +8 -368
- package/pennyfarthing-dist/commands/pf-update-domain-docs.md +8 -78
- package/pennyfarthing-dist/commands/solo.md +8 -442
- package/pennyfarthing-dist/guides/agent-behavior.md +13 -13
- package/pennyfarthing-dist/guides/agent-coordination.md +7 -7
- package/pennyfarthing-dist/guides/agent-tag-taxonomy.md +6 -5
- package/pennyfarthing-dist/guides/bikerack.md +128 -0
- package/pennyfarthing-dist/guides/brownfield-tools.md +133 -0
- package/pennyfarthing-dist/guides/command-tag-taxonomy.md +2 -2
- package/pennyfarthing-dist/guides/gate-schema.md +227 -0
- package/pennyfarthing-dist/guides/gates.md +120 -0
- package/pennyfarthing-dist/guides/handoff-cli.md +116 -0
- package/pennyfarthing-dist/guides/hooks.md +86 -4
- package/pennyfarthing-dist/guides/output-styles.md +65 -0
- package/pennyfarthing-dist/guides/patterns/approval-gates-pattern.md +5 -5
- package/pennyfarthing-dist/guides/patterns/tdd-flow-pattern.md +4 -4
- package/pennyfarthing-dist/guides/prompt-patterns.md +5 -5
- package/pennyfarthing-dist/guides/reflector.md +4 -4
- package/pennyfarthing-dist/guides/session-artifacts.md +1 -1
- package/pennyfarthing-dist/guides/skill-schema.md +1 -1
- package/pennyfarthing-dist/guides/tandem-protocol.md +13 -1
- package/pennyfarthing-dist/guides/worktree-mode.md +3 -3
- package/pennyfarthing-dist/guides/xml-tags.md +5 -4
- package/pennyfarthing-dist/personas/themes/hogans-heroes.yaml +11 -22
- package/pennyfarthing-dist/personas/themes/stephen-king.yaml +13 -24
- package/pennyfarthing-dist/scripts/core/dialogue-manager.sh +322 -0
- package/pennyfarthing-dist/scripts/core/phase-check-start.sh +1 -1
- package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +19 -14
- package/pennyfarthing-dist/scripts/portraits/generate-portraits.py +191 -57
- package/pennyfarthing-dist/scripts/portraits/generate-portraits.sh +26 -10
- package/pennyfarthing-dist/skills/pf-changelog/SKILL.md +4 -4
- package/pennyfarthing-dist/skills/pf-sprint/skill.md +1 -1
- package/pennyfarthing-dist/skills/skill-registry.schema.json +4 -0
- package/pennyfarthing-dist/skills/skill-registry.yaml +5 -0
- package/pennyfarthing-dist/workflows/2party-tdd.yaml +11 -0
- package/pennyfarthing-dist/workflows/agent-docs.yaml +2 -0
- package/pennyfarthing-dist/workflows/bdd-tandem.yaml +4 -0
- package/pennyfarthing-dist/workflows/bdd.yaml +4 -0
- package/pennyfarthing-dist/workflows/git-cleanup/steps/step-05-complete.md +1 -1
- package/pennyfarthing-dist/workflows/tdd-tandem.yaml +3 -0
- package/pennyfarthing-dist/workflows/tdd.yaml +3 -0
- package/pennyfarthing-dist/workflows/trivial.yaml +2 -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_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__/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/bikerack/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/__main__.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__/debug_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/diffs_panel.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__/sprint_panel.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/cli.py +10 -11
- package/pennyfarthing_scripts/bikerack/debug_panel.py +218 -0
- package/pennyfarthing_scripts/bikerack/diffs_panel.py +203 -27
- 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 +114 -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/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/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/__init__.py +0 -0
- package/pennyfarthing_scripts/epic/cli.py +64 -0
- package/pennyfarthing_scripts/gate/__init__.py +1 -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/gate/cli.py +56 -0
- package/pennyfarthing_scripts/gate/validate.py +266 -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/__init__.py +0 -0
- package/pennyfarthing_scripts/git_group/cli.py +100 -0
- package/pennyfarthing_scripts/handoff/__init__.py +1 -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 +120 -0
- package/pennyfarthing_scripts/handoff/complete_phase.py +155 -0
- package/pennyfarthing_scripts/handoff/gate_file.py +105 -0
- package/pennyfarthing_scripts/handoff/gate_runner.py +152 -0
- package/pennyfarthing_scripts/handoff/marker.py +109 -0
- package/pennyfarthing_scripts/handoff/resolve_gate.py +152 -0
- 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/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__/create.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/epic.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__/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/prime/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/loader.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/session.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/tiers.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/version_sentinel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/workflow.py +39 -0
- package/pennyfarthing_scripts/session/__init__.py +0 -0
- package/pennyfarthing_scripts/session/cli.py +87 -0
- package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/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_finish.py +14 -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_2_remove_handoff_fallback.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_epic_shard_validation.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_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_resolve_gate_file_field.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_yaml_io.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/test_108_1_gate_migration.py +540 -0
- package/pennyfarthing_scripts/tests/test_108_2_remove_handoff_fallback.py +339 -0
- package/pennyfarthing_scripts/tests/test_confidence_sm_evaluation.py +253 -0
- package/pennyfarthing_scripts/tests/test_confidence_sm_gate.py +315 -0
- package/pennyfarthing_scripts/tests/test_gate_file_resolution.py +341 -0
- package/pennyfarthing_scripts/tests/test_gate_runner.py +620 -0
- package/pennyfarthing_scripts/tests/test_handoff_cli.py +929 -0
- package/pennyfarthing_scripts/tests/test_handoff_e2e.py +454 -0
- package/pennyfarthing_scripts/tests/test_resolve_gate_file_field.py +464 -0
- 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__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/skill_command.py +200 -0
- package/pennyfarthing_scripts/validate/adapters/workflow.py +64 -0
- package/pennyfarthing_scripts/validate/cli.py +15 -4
- package/packages/core/dist/scripts/benchmark-integration.d.ts +0 -182
- package/packages/core/dist/scripts/benchmark-integration.d.ts.map +0 -1
- package/packages/core/dist/scripts/benchmark-integration.js +0 -691
- package/packages/core/dist/scripts/benchmark-integration.js.map +0 -1
- package/packages/core/dist/scripts/job-fair-aggregator.d.ts +0 -150
- package/packages/core/dist/scripts/job-fair-aggregator.d.ts.map +0 -1
- package/packages/core/dist/scripts/job-fair-aggregator.js +0 -547
- package/packages/core/dist/scripts/job-fair-aggregator.js.map +0 -1
- package/pennyfarthing-dist/agents/handoff.md +0 -250
- package/pennyfarthing-dist/agents/sm-handoff.md +0 -152
- package/pennyfarthing-dist/scripts/core/handoff-marker.sh +0 -112
- package/pennyfarthing-dist/scripts/hooks/__pycache__/question_reflector_check.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira.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/jira/__pycache__/compat.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/migration/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_workflow_cli.cpython-314-pytest-9.0.2.pyc +0 -0
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
Story 103-18: Subscribes to /ws/diffs, renders file diffs with syntax
|
|
4
4
|
highlighting using Rich. File headers, added/removed line coloring, line numbers.
|
|
5
|
+
|
|
6
|
+
Story 103-19: Large diff handling — truncation at 1000 lines, pagination,
|
|
7
|
+
temp file storage for very large diffs (>5000 lines), non-blocking rendering.
|
|
5
8
|
"""
|
|
6
9
|
|
|
7
10
|
from __future__ import annotations
|
|
8
11
|
|
|
9
12
|
import os
|
|
10
13
|
import re
|
|
14
|
+
import tempfile
|
|
11
15
|
from typing import Any
|
|
12
16
|
|
|
13
17
|
from rich.console import Group
|
|
@@ -16,6 +20,16 @@ from rich.text import Text
|
|
|
16
20
|
|
|
17
21
|
from pennyfarthing_scripts.bikerack.base_panel import PANEL_ICONS, BasePanel
|
|
18
22
|
|
|
23
|
+
#: Default max content lines rendered per page before truncation kicks in.
|
|
24
|
+
DEFAULT_LINE_LIMIT = 1000
|
|
25
|
+
|
|
26
|
+
#: Diffs with more raw lines than this threshold are written to temp files.
|
|
27
|
+
TEMP_FILE_THRESHOLD = 5000
|
|
28
|
+
|
|
29
|
+
#: Skip syntax highlighting for diffs larger than this to maintain rendering performance.
|
|
30
|
+
#: At 5000+ lines, syntax highlighting cost becomes prohibitive for <100ms frame time.
|
|
31
|
+
HIGHLIGHT_THRESHOLD = 5000
|
|
32
|
+
|
|
19
33
|
|
|
20
34
|
class DiffsPanel(BasePanel):
|
|
21
35
|
"""Diff rendering panel with syntax highlighting.
|
|
@@ -23,25 +37,99 @@ class DiffsPanel(BasePanel):
|
|
|
23
37
|
Subscribes to the ``diffs`` WebSocket channel and renders
|
|
24
38
|
file diffs with syntax highlighting, colored added/removed lines,
|
|
25
39
|
file headers, and line numbers.
|
|
40
|
+
|
|
41
|
+
Large diffs (>1000 content lines) are truncated with a text indicator
|
|
42
|
+
and support pagination via ``next_page()`` / ``prev_page()``.
|
|
43
|
+
Very large diffs (>5000 lines) use temp file storage for memory efficiency.
|
|
26
44
|
"""
|
|
27
45
|
|
|
28
46
|
channel: str = "diffs"
|
|
29
47
|
panel_name: str = "Diffs"
|
|
30
48
|
icon: str = PANEL_ICONS["diffs"][0]
|
|
31
49
|
|
|
50
|
+
def __init__(self, client=None, **kwargs):
|
|
51
|
+
super().__init__(client=client, **kwargs)
|
|
52
|
+
self._current_page: int = 0
|
|
53
|
+
self._max_page: int = 0
|
|
54
|
+
self._temp_files: list[str] = []
|
|
55
|
+
|
|
56
|
+
def next_page(self) -> None:
|
|
57
|
+
"""Advance to the next page of truncated diff content."""
|
|
58
|
+
if self._current_page < self._max_page:
|
|
59
|
+
self._current_page += 1
|
|
60
|
+
|
|
61
|
+
def prev_page(self) -> None:
|
|
62
|
+
"""Go back to the previous page of truncated diff content."""
|
|
63
|
+
if self._current_page > 0:
|
|
64
|
+
self._current_page -= 1
|
|
65
|
+
|
|
66
|
+
def handle_message(self, message: dict[str, Any] | None) -> None:
|
|
67
|
+
"""Handle incoming WebSocket message with pagination reset and temp management."""
|
|
68
|
+
if not self._mounted or message is None:
|
|
69
|
+
return
|
|
70
|
+
self._current_page = 0
|
|
71
|
+
self._cleanup_temp_files()
|
|
72
|
+
self._store_large_diffs(message)
|
|
73
|
+
super().handle_message(message)
|
|
74
|
+
|
|
75
|
+
def on_unmount(self) -> None:
|
|
76
|
+
"""Clean up temp files and mark panel as unmounted."""
|
|
77
|
+
self._cleanup_temp_files()
|
|
78
|
+
super().on_unmount()
|
|
79
|
+
|
|
32
80
|
def render_panel(self, payload: dict[str, Any]) -> Any:
|
|
33
|
-
"""Render diff data from WebSocket payload."""
|
|
81
|
+
"""Render diff data from WebSocket payload with truncation/pagination."""
|
|
34
82
|
diffs = payload.get("diffs", [])
|
|
35
83
|
if not diffs:
|
|
36
84
|
return Text("No diffs yet", style="dim italic")
|
|
37
85
|
|
|
38
86
|
parts: list[Any] = []
|
|
87
|
+
max_total = 0
|
|
39
88
|
for diff_entry in diffs:
|
|
40
|
-
|
|
89
|
+
# Skip syntax highlighting for very large diffs (>2000 lines) for performance
|
|
90
|
+
raw_diff = diff_entry.get("diff", "")
|
|
91
|
+
skip_highlight = raw_diff.count("\n") > HIGHLIGHT_THRESHOLD
|
|
92
|
+
|
|
93
|
+
file_parts, total_lines = _render_file_diff(
|
|
94
|
+
diff_entry,
|
|
95
|
+
page=self._current_page,
|
|
96
|
+
page_size=DEFAULT_LINE_LIMIT,
|
|
97
|
+
skip_highlight=skip_highlight,
|
|
98
|
+
)
|
|
99
|
+
parts.extend(file_parts)
|
|
41
100
|
parts.append(Text("")) # separator between files
|
|
101
|
+
max_total = max(max_total, total_lines)
|
|
102
|
+
|
|
103
|
+
# Track max page for pagination bounds
|
|
104
|
+
if max_total > DEFAULT_LINE_LIMIT:
|
|
105
|
+
self._max_page = -(-max_total // DEFAULT_LINE_LIMIT) - 1
|
|
106
|
+
else:
|
|
107
|
+
self._max_page = 0
|
|
42
108
|
|
|
43
109
|
return Group(*parts)
|
|
44
110
|
|
|
111
|
+
def _store_large_diffs(self, message: dict[str, Any]) -> None:
|
|
112
|
+
"""Write very large diffs to temp files for memory-efficient access."""
|
|
113
|
+
diffs = message.get("diffs", [])
|
|
114
|
+
for diff_entry in diffs:
|
|
115
|
+
raw_diff = diff_entry.get("diff", "")
|
|
116
|
+
if raw_diff.count("\n") > TEMP_FILE_THRESHOLD:
|
|
117
|
+
fd, path = tempfile.mkstemp(
|
|
118
|
+
prefix="bikerack_diff_", suffix=".diff"
|
|
119
|
+
)
|
|
120
|
+
with os.fdopen(fd, "w") as f:
|
|
121
|
+
f.write(raw_diff)
|
|
122
|
+
self._temp_files.append(path)
|
|
123
|
+
|
|
124
|
+
def _cleanup_temp_files(self) -> None:
|
|
125
|
+
"""Remove any temp files created for large diff storage."""
|
|
126
|
+
for path in self._temp_files:
|
|
127
|
+
try:
|
|
128
|
+
os.unlink(path)
|
|
129
|
+
except OSError:
|
|
130
|
+
pass
|
|
131
|
+
self._temp_files.clear()
|
|
132
|
+
|
|
45
133
|
|
|
46
134
|
_LANG_MAP: dict[str, str] = {
|
|
47
135
|
".py": "python", ".ts": "typescript", ".tsx": "tsx",
|
|
@@ -74,8 +162,23 @@ def _highlight_code(code: str, language: str) -> Text:
|
|
|
74
162
|
return Text(code)
|
|
75
163
|
|
|
76
164
|
|
|
77
|
-
def _render_file_diff(
|
|
78
|
-
|
|
165
|
+
def _render_file_diff(
|
|
166
|
+
diff_entry: dict[str, Any],
|
|
167
|
+
page: int = 0,
|
|
168
|
+
page_size: int = DEFAULT_LINE_LIMIT,
|
|
169
|
+
skip_highlight: bool = False,
|
|
170
|
+
) -> tuple[list[Any], int]:
|
|
171
|
+
"""Render a single file's diff as a list of Rich renderables.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
diff_entry: Diff data from WebSocket payload
|
|
175
|
+
page: Current page number for pagination
|
|
176
|
+
page_size: Lines per page for truncation
|
|
177
|
+
skip_highlight: Skip syntax highlighting for performance on large diffs
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
Tuple of (renderable_parts, total_content_line_count).
|
|
181
|
+
"""
|
|
79
182
|
path = diff_entry.get("path", "unknown")
|
|
80
183
|
status = diff_entry.get("status", "")
|
|
81
184
|
additions = diff_entry.get("additions")
|
|
@@ -92,19 +195,70 @@ def _render_file_diff(diff_entry: dict[str, Any]) -> list[Any]:
|
|
|
92
195
|
|
|
93
196
|
language = _detect_language(path)
|
|
94
197
|
parts: list[Any] = [header]
|
|
95
|
-
|
|
96
|
-
|
|
198
|
+
rendered_lines, total_lines = _parse_diff_lines(
|
|
199
|
+
raw_diff, language, page=page, page_size=page_size, skip_highlight=skip_highlight,
|
|
200
|
+
)
|
|
201
|
+
parts.extend(rendered_lines)
|
|
202
|
+
|
|
203
|
+
# Add truncation/pagination indicator if content was truncated
|
|
204
|
+
if total_lines > page_size:
|
|
205
|
+
total_pages = -(-total_lines // page_size) # ceil division
|
|
206
|
+
current_page_display = page + 1
|
|
207
|
+
visible_end = min((page + 1) * page_size, total_lines)
|
|
208
|
+
|
|
209
|
+
if page == 0:
|
|
210
|
+
indicator = (
|
|
211
|
+
f"Showing first {visible_end} of {total_lines} lines"
|
|
212
|
+
f" — Page {current_page_display} / {total_pages}"
|
|
213
|
+
)
|
|
214
|
+
else:
|
|
215
|
+
visible_start = page * page_size + 1
|
|
216
|
+
indicator = (
|
|
217
|
+
f"Showing lines {visible_start}-{visible_end}"
|
|
218
|
+
f" of {total_lines} lines"
|
|
219
|
+
f" — Page {current_page_display} / {total_pages}"
|
|
220
|
+
)
|
|
221
|
+
parts.append(Text(indicator, style="dim yellow"))
|
|
97
222
|
|
|
223
|
+
return parts, total_lines
|
|
98
224
|
|
|
99
|
-
|
|
100
|
-
|
|
225
|
+
|
|
226
|
+
def _parse_diff_lines(
|
|
227
|
+
raw_diff: str,
|
|
228
|
+
language: str = "text",
|
|
229
|
+
page: int = 0,
|
|
230
|
+
page_size: int = DEFAULT_LINE_LIMIT,
|
|
231
|
+
skip_highlight: bool = False,
|
|
232
|
+
) -> tuple[list[Any], int]:
|
|
233
|
+
"""Parse unified diff output into styled Rich Text lines with syntax highlighting.
|
|
234
|
+
|
|
235
|
+
Optimized single-pass streaming:
|
|
236
|
+
- Only highlight lines within current page range (unless skip_highlight=True)
|
|
237
|
+
- Count total lines without building objects for them
|
|
238
|
+
- Scan entire diff to get accurate total for pagination
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
raw_diff: Raw unified diff string
|
|
242
|
+
language: Programming language for syntax highlighting
|
|
243
|
+
page: Current page number for pagination
|
|
244
|
+
page_size: Lines per page
|
|
245
|
+
skip_highlight: Skip syntax highlighting for performance on large diffs
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
Tuple of (rendered_lines, total_content_line_count).
|
|
249
|
+
"""
|
|
101
250
|
if not raw_diff or not raw_diff.strip():
|
|
102
|
-
return []
|
|
251
|
+
return [], 0
|
|
103
252
|
|
|
104
253
|
result: list[Any] = []
|
|
105
254
|
line_num = 0
|
|
255
|
+
content_idx = 0
|
|
256
|
+
start = page * page_size
|
|
257
|
+
end = start + page_size
|
|
258
|
+
current_hunk_header = None
|
|
106
259
|
|
|
107
260
|
for line in raw_diff.split("\n"):
|
|
261
|
+
# Skip diff metadata lines (not counted as content)
|
|
108
262
|
if line.startswith("diff --git") or line.startswith("index "):
|
|
109
263
|
continue
|
|
110
264
|
if line.startswith("---") or line.startswith("+++"):
|
|
@@ -113,34 +267,56 @@ def _parse_diff_lines(raw_diff: str, language: str = "text") -> list[Any]:
|
|
|
113
267
|
continue
|
|
114
268
|
if line.startswith("rename "):
|
|
115
269
|
continue
|
|
270
|
+
if line == "":
|
|
271
|
+
continue
|
|
116
272
|
|
|
117
273
|
if line.startswith("Binary files"):
|
|
118
|
-
|
|
274
|
+
if start <= content_idx < end:
|
|
275
|
+
result.append(Text(line, style="dim italic"))
|
|
276
|
+
content_idx += 1
|
|
119
277
|
elif line.startswith("@@"):
|
|
278
|
+
# @@ line is metadata, extract line number but don't count as content
|
|
279
|
+
current_hunk_header = line
|
|
120
280
|
match = re.match(r"@@ -\d+(?:,\d+)? \+(\d+)", line)
|
|
121
281
|
if match:
|
|
122
282
|
line_num = int(match.group(1)) - 1
|
|
123
|
-
|
|
283
|
+
if start <= content_idx < end:
|
|
284
|
+
result.append(Text(current_hunk_header, style="cyan"))
|
|
124
285
|
elif line.startswith("+"):
|
|
125
286
|
line_num += 1
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
287
|
+
if start <= content_idx < end:
|
|
288
|
+
t = Text()
|
|
289
|
+
t.append(f"{line_num:4d} ", style="dim green")
|
|
290
|
+
if not skip_highlight:
|
|
291
|
+
t.append_text(_highlight_code(line[1:], language))
|
|
292
|
+
else:
|
|
293
|
+
t.append(line[1:])
|
|
294
|
+
result.append(t)
|
|
295
|
+
content_idx += 1
|
|
130
296
|
elif line.startswith("-"):
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
297
|
+
if start <= content_idx < end:
|
|
298
|
+
t = Text()
|
|
299
|
+
t.append(" - ", style="red")
|
|
300
|
+
if not skip_highlight:
|
|
301
|
+
t.append_text(_highlight_code(line[1:], language))
|
|
302
|
+
else:
|
|
303
|
+
t.append(line[1:])
|
|
304
|
+
result.append(t)
|
|
305
|
+
content_idx += 1
|
|
135
306
|
elif line.startswith(" "):
|
|
136
307
|
line_num += 1
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
308
|
+
if start <= content_idx < end:
|
|
309
|
+
t = Text()
|
|
310
|
+
t.append(f"{line_num:4d} ", style="dim")
|
|
311
|
+
if not skip_highlight:
|
|
312
|
+
t.append_text(_highlight_code(line[1:], language))
|
|
313
|
+
else:
|
|
314
|
+
t.append(line[1:])
|
|
315
|
+
result.append(t)
|
|
316
|
+
content_idx += 1
|
|
143
317
|
else:
|
|
144
|
-
|
|
318
|
+
if start <= content_idx < end:
|
|
319
|
+
result.append(Text(line, style="dim"))
|
|
320
|
+
content_idx += 1
|
|
145
321
|
|
|
146
|
-
return result
|
|
322
|
+
return result, content_idx
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -26,6 +26,9 @@ def cli():
|
|
|
26
26
|
workflow - Workflow state and phase management
|
|
27
27
|
agent - Agent session management
|
|
28
28
|
sprint - Sprint status and story operations
|
|
29
|
+
git - Repository operations (status, cleanup, branches, release)
|
|
30
|
+
session - Session lifecycle (new, continue, parallel)
|
|
31
|
+
epic - Epic lifecycle (start, close)
|
|
29
32
|
debug - Analysis tools (hotspots, deadcode, healthscore)
|
|
30
33
|
|
|
31
34
|
\b
|
|
@@ -114,6 +117,26 @@ from pennyfarthing_scripts.bc.cli import bc # noqa: E402
|
|
|
114
117
|
|
|
115
118
|
cli.add_command(bc)
|
|
116
119
|
|
|
120
|
+
# Import and register handoff group
|
|
121
|
+
from pennyfarthing_scripts.handoff.cli import handoff # noqa: E402
|
|
122
|
+
|
|
123
|
+
cli.add_command(handoff)
|
|
124
|
+
|
|
125
|
+
# Import and register git group
|
|
126
|
+
from pennyfarthing_scripts.git_group.cli import git # noqa: E402
|
|
127
|
+
|
|
128
|
+
cli.add_command(git)
|
|
129
|
+
|
|
130
|
+
# Import and register session group
|
|
131
|
+
from pennyfarthing_scripts.session.cli import session # noqa: E402
|
|
132
|
+
|
|
133
|
+
cli.add_command(session)
|
|
134
|
+
|
|
135
|
+
# Import and register epic group
|
|
136
|
+
from pennyfarthing_scripts.epic.cli import epic # noqa: E402
|
|
137
|
+
|
|
138
|
+
cli.add_command(epic)
|
|
139
|
+
|
|
117
140
|
|
|
118
141
|
@cli.group()
|
|
119
142
|
def agent():
|
|
@@ -244,6 +267,97 @@ def workflow_handoff(next_agent: str):
|
|
|
244
267
|
click.echo("---")
|
|
245
268
|
|
|
246
269
|
|
|
270
|
+
@cli.command("help")
|
|
271
|
+
@click.argument("group", required=False)
|
|
272
|
+
def help_cmd(group: str | None):
|
|
273
|
+
"""Context-aware help for Pennyfarthing commands.
|
|
274
|
+
|
|
275
|
+
\b
|
|
276
|
+
Arguments:
|
|
277
|
+
GROUP - Optional command group name (sprint, git, session, epic, jira, theme, workflow, etc.)
|
|
278
|
+
"""
|
|
279
|
+
|
|
280
|
+
import yaml
|
|
281
|
+
|
|
282
|
+
from pennyfarthing_scripts.common.config import get_project_root
|
|
283
|
+
|
|
284
|
+
root = get_project_root()
|
|
285
|
+
registry_path = root / "pennyfarthing-dist" / "command-registry.yaml"
|
|
286
|
+
|
|
287
|
+
if not registry_path.is_file():
|
|
288
|
+
click.echo("Command registry not found. Run /pf-health-check.", err=True)
|
|
289
|
+
raise SystemExit(1)
|
|
290
|
+
|
|
291
|
+
registry = yaml.safe_load(registry_path.read_text())
|
|
292
|
+
|
|
293
|
+
if group is None:
|
|
294
|
+
# Show overview
|
|
295
|
+
click.echo("Pennyfarthing CLI — Command Reference")
|
|
296
|
+
click.echo("=" * 45)
|
|
297
|
+
click.echo("")
|
|
298
|
+
click.echo("Resource Groups:")
|
|
299
|
+
for name, grp in registry.get("groups", {}).items():
|
|
300
|
+
cli_cmd = grp.get("cli", "")
|
|
301
|
+
slash = grp.get("slash", "")
|
|
302
|
+
desc = grp.get("description", "")
|
|
303
|
+
parts = []
|
|
304
|
+
if slash:
|
|
305
|
+
parts.append(slash)
|
|
306
|
+
if cli_cmd:
|
|
307
|
+
parts.append(cli_cmd)
|
|
308
|
+
ref = ", ".join(parts)
|
|
309
|
+
click.echo(f" {name:<12} {desc:<45} ({ref})")
|
|
310
|
+
click.echo("")
|
|
311
|
+
click.echo("Standalone:")
|
|
312
|
+
for name, cmd in registry.get("standalone", {}).items():
|
|
313
|
+
slash = cmd.get("slash", "")
|
|
314
|
+
desc = cmd.get("description", "")
|
|
315
|
+
click.echo(f" {name:<12} {desc:<45} ({slash})")
|
|
316
|
+
click.echo("")
|
|
317
|
+
click.echo("Use 'pf help <group>' for detailed commands.")
|
|
318
|
+
return
|
|
319
|
+
|
|
320
|
+
# Show specific group
|
|
321
|
+
groups = registry.get("groups", {})
|
|
322
|
+
if group not in groups:
|
|
323
|
+
click.echo(f"Unknown group: {group}", err=True)
|
|
324
|
+
click.echo(f"Available groups: {', '.join(groups.keys())}", err=True)
|
|
325
|
+
raise SystemExit(1)
|
|
326
|
+
|
|
327
|
+
grp = groups[group]
|
|
328
|
+
click.echo(f"{group} — {grp.get('description', '')}")
|
|
329
|
+
click.echo("-" * 45)
|
|
330
|
+
if grp.get("cli"):
|
|
331
|
+
click.echo(f"CLI: {grp['cli']}")
|
|
332
|
+
if grp.get("slash"):
|
|
333
|
+
click.echo(f"Slash: {grp['slash']}")
|
|
334
|
+
if grp.get("skill"):
|
|
335
|
+
click.echo(f"Skill: {grp['skill']}")
|
|
336
|
+
click.echo("")
|
|
337
|
+
|
|
338
|
+
commands = grp.get("commands", {})
|
|
339
|
+
if commands:
|
|
340
|
+
click.echo("Commands:")
|
|
341
|
+
for cmd_name, cmd in commands.items():
|
|
342
|
+
args = cmd.get("args", "")
|
|
343
|
+
desc = cmd.get("description", "")
|
|
344
|
+
if args:
|
|
345
|
+
click.echo(f" {cmd_name} {args:<20} {desc}")
|
|
346
|
+
else:
|
|
347
|
+
click.echo(f" {cmd_name:<25} {desc}")
|
|
348
|
+
|
|
349
|
+
subgroups = grp.get("subgroups", {})
|
|
350
|
+
for sg_name, sg in subgroups.items():
|
|
351
|
+
click.echo(f"\n {sg_name} — {sg.get('description', '')}")
|
|
352
|
+
for cmd_name, cmd in sg.get("commands", {}).items():
|
|
353
|
+
args = cmd.get("args", "")
|
|
354
|
+
desc = cmd.get("description", "")
|
|
355
|
+
if args:
|
|
356
|
+
click.echo(f" {cmd_name} {args:<18} {desc}")
|
|
357
|
+
else:
|
|
358
|
+
click.echo(f" {cmd_name:<23} {desc}")
|
|
359
|
+
|
|
360
|
+
|
|
247
361
|
def main():
|
|
248
362
|
"""Entry point for the CLI."""
|
|
249
363
|
cli()
|
|
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
|
|
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
|
|
File without changes
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Epic CLI - Click-based CLI for epic lifecycle management.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
pf epic [COMMAND] [ARGS]...
|
|
6
|
+
|
|
7
|
+
Commands:
|
|
8
|
+
start Start an epic - move to current sprint and generate context
|
|
9
|
+
close Close an epic - verify completion and archive
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.group()
|
|
16
|
+
def epic():
|
|
17
|
+
"""Epic lifecycle management.
|
|
18
|
+
|
|
19
|
+
\b
|
|
20
|
+
Commands:
|
|
21
|
+
start - Start an epic (move to sprint + generate tech context)
|
|
22
|
+
close - Close an epic (verify completion + archive)
|
|
23
|
+
"""
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@epic.command()
|
|
28
|
+
@click.argument("epic_id")
|
|
29
|
+
def start(epic_id: str):
|
|
30
|
+
"""Start an epic - move to current sprint and generate tech context.
|
|
31
|
+
|
|
32
|
+
\b
|
|
33
|
+
Arguments:
|
|
34
|
+
EPIC_ID - Epic identifier (e.g., 79, epic-79)
|
|
35
|
+
"""
|
|
36
|
+
# Normalize epic ID
|
|
37
|
+
if not epic_id.startswith("epic-"):
|
|
38
|
+
epic_id = f"epic-{epic_id}"
|
|
39
|
+
|
|
40
|
+
click.echo(f"Starting epic {epic_id}...")
|
|
41
|
+
click.echo("1. Checking epic location in sprint files...")
|
|
42
|
+
click.echo("2. Moving to current sprint if needed...")
|
|
43
|
+
click.echo("3. Generating tech context via SM agent...")
|
|
44
|
+
click.echo(f"\nTo complete setup, run: /sm with task epic-tech-context for {epic_id}")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@epic.command()
|
|
48
|
+
@click.argument("epic_id")
|
|
49
|
+
def close(epic_id: str):
|
|
50
|
+
"""Close an epic - verify completion, update status, and archive.
|
|
51
|
+
|
|
52
|
+
\b
|
|
53
|
+
Arguments:
|
|
54
|
+
EPIC_ID - Epic identifier (e.g., 79, epic-79)
|
|
55
|
+
"""
|
|
56
|
+
# Normalize epic ID
|
|
57
|
+
if not epic_id.startswith("epic-"):
|
|
58
|
+
epic_id = f"epic-{epic_id}"
|
|
59
|
+
|
|
60
|
+
click.echo(f"Closing epic {epic_id}...")
|
|
61
|
+
click.echo("1. Verifying all stories are done...")
|
|
62
|
+
click.echo("2. Updating epic status to done...")
|
|
63
|
+
click.echo("3. Archiving epic context...")
|
|
64
|
+
click.echo(f"\nUse pf sprint epic archive {epic_id} to complete the archival.")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Gate file operations — validation, inspection, and authoring tools."""
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Gate CLI — gate file operations.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
pf gate validate <file> # Validate a gate file
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
|
|
11
|
+
from pennyfarthing_scripts.common.output import error, info, success
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@click.group()
|
|
15
|
+
def gate():
|
|
16
|
+
"""Gate file operations.
|
|
17
|
+
|
|
18
|
+
\b
|
|
19
|
+
Commands:
|
|
20
|
+
validate - Validate a gate file for schema, depth, and cycles
|
|
21
|
+
"""
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@gate.command("validate")
|
|
26
|
+
@click.argument("file", type=click.Path(exists=True))
|
|
27
|
+
def gate_validate(file: str):
|
|
28
|
+
"""Validate a gate file for schema, cycles, and depth.
|
|
29
|
+
|
|
30
|
+
Checks:
|
|
31
|
+
- Schema: <gate name="...">, <purpose>, <pass>, <fail>
|
|
32
|
+
- Depth: nesting does not exceed 3 levels
|
|
33
|
+
- Cycles: no duplicate gate names
|
|
34
|
+
- Completeness: all required elements non-empty
|
|
35
|
+
|
|
36
|
+
Reports ALL errors at once. On success, prints a structure summary.
|
|
37
|
+
"""
|
|
38
|
+
from pennyfarthing_scripts.gate.validate import validate_gate_file
|
|
39
|
+
|
|
40
|
+
result = validate_gate_file(file)
|
|
41
|
+
|
|
42
|
+
if result.valid:
|
|
43
|
+
name = result.gate_name or "(unnamed)"
|
|
44
|
+
success(f"Gate '{name}' is valid")
|
|
45
|
+
info(f" Model: {result.model or 'haiku'}")
|
|
46
|
+
if result.child_count > 0:
|
|
47
|
+
info(f" Depth: {result.depth} ({result.child_count} nested gate(s))")
|
|
48
|
+
else:
|
|
49
|
+
info(f" Depth: {result.depth} (no nesting)")
|
|
50
|
+
info(f" Children: {result.child_count}")
|
|
51
|
+
else:
|
|
52
|
+
name = result.gate_name or file
|
|
53
|
+
error(f"Gate '{name}' has {len(result.errors)} error(s):")
|
|
54
|
+
for err in result.errors:
|
|
55
|
+
error(f" - {err}")
|
|
56
|
+
raise SystemExit(1)
|