@pennyfarthing/core 11.0.0-alpha.0 → 11.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -26
- package/package.json +14 -16
- package/packages/core/dist/cli/cyclist-migration.test.js +2 -1
- package/packages/core/dist/cli/cyclist-migration.test.js.map +1 -1
- package/packages/core/dist/cli/ocean-profiles.test.js +5 -4
- package/packages/core/dist/cli/ocean-profiles.test.js.map +1 -1
- package/packages/core/dist/cli/theme-maker.test.js +5 -4
- package/packages/core/dist/cli/theme-maker.test.js.map +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 +7 -1
- package/packages/core/dist/cli/utils/constants.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/constants.js +2 -0
- package/packages/core/dist/cli/utils/constants.js.map +1 -1
- package/packages/core/dist/cli/utils/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/public/js/react/react.js +30 -30
- package/packages/core/dist/scripts/generate-report.test.js +2 -2
- package/packages/core/dist/scripts/generate-spider-report.test.js +2 -2
- package/packages/core/dist/scripts/generate-spider.test.js +2 -1
- package/packages/core/dist/scripts/generate-spider.test.js.map +1 -1
- package/packages/core/dist/server/api/file-browser.d.ts.map +1 -1
- package/packages/core/dist/server/api/file-browser.js +19 -1
- package/packages/core/dist/server/api/file-browser.js.map +1 -1
- package/packages/core/dist/server/api/git-fetch-cooldown.test.d.ts +10 -0
- package/packages/core/dist/server/api/git-fetch-cooldown.test.d.ts.map +1 -0
- package/packages/core/dist/server/api/git-fetch-cooldown.test.js +30 -0
- package/packages/core/dist/server/api/git-fetch-cooldown.test.js.map +1 -0
- package/packages/core/dist/server/api/git.d.ts +8 -0
- package/packages/core/dist/server/api/git.d.ts.map +1 -1
- package/packages/core/dist/server/api/git.js +37 -10
- 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/index.d.ts +1 -1
- package/packages/core/dist/server/api/index.d.ts.map +1 -1
- package/packages/core/dist/server/api/index.js +1 -1
- package/packages/core/dist/server/api/index.js.map +1 -1
- package/packages/core/dist/server/api/settings.d.ts.map +1 -1
- package/packages/core/dist/server/api/settings.js +73 -2
- 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/otlp-receiver.d.ts +35 -13
- package/packages/core/dist/server/otlp-receiver.d.ts.map +1 -1
- package/packages/core/dist/server/otlp-receiver.js +76 -16
- package/packages/core/dist/server/otlp-receiver.js.map +1 -1
- package/packages/core/dist/server/paths.d.ts.map +1 -1
- package/packages/core/dist/server/paths.js +11 -1
- package/packages/core/dist/server/paths.js.map +1 -1
- package/packages/core/dist/server/server.d.ts +3 -1
- package/packages/core/dist/server/server.d.ts.map +1 -1
- package/packages/core/dist/server/server.js +23 -16
- package/packages/core/dist/server/server.js.map +1 -1
- package/packages/core/dist/server/server.test.js.map +1 -1
- 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/variable-resolver.test.js +1 -1
- package/packages/core/dist/workflow/variable-resolver.test.js.map +1 -1
- package/packages/core/dist/workflow/workflow-migration.test.js +4 -3
- package/packages/core/dist/workflow/workflow-migration.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 -30
- package/pennyfarthing-dist/agents/ba.md +30 -29
- package/pennyfarthing-dist/agents/dev.md +76 -41
- package/pennyfarthing-dist/agents/devops.md +57 -21
- package/pennyfarthing-dist/agents/orchestrator.md +3 -11
- 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 -40
- 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 +14 -14
- package/pennyfarthing-dist/guides/agent-coordination.md +7 -7
- package/pennyfarthing-dist/guides/agent-tag-taxonomy.md +6 -6
- 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/agent-session.sh +0 -0
- package/pennyfarthing-dist/scripts/core/check-context.sh +0 -0
- package/pennyfarthing-dist/scripts/core/phase-check-start.sh +1 -1
- package/pennyfarthing-dist/scripts/core/prime.sh +0 -0
- package/pennyfarthing-dist/scripts/cyclist/is-cyclist.sh +0 -0
- package/pennyfarthing-dist/scripts/git/create-feature-branches.sh +0 -0
- package/pennyfarthing-dist/scripts/git/git-status-all.sh +0 -0
- package/pennyfarthing-dist/scripts/git/install-git-hooks.sh +0 -0
- package/pennyfarthing-dist/scripts/git/release.sh +0 -0
- package/pennyfarthing-dist/scripts/git/worktree-manager.sh +0 -0
- package/pennyfarthing-dist/scripts/health/drift-detection.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/context-warning.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/cyclist-pretooluse-hook.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/dispatcher-template.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +19 -14
- package/pennyfarthing-dist/scripts/hooks/post-merge.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/pre-push.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/question_reflector_check.py +0 -0
- package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/session-start.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/session-stop.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +0 -0
- package/pennyfarthing-dist/scripts/jira/create-jira-epic.sh +0 -0
- package/pennyfarthing-dist/scripts/jira/create-jira-story.sh +0 -0
- package/pennyfarthing-dist/scripts/jira/jira-claim-story.sh +0 -0
- package/pennyfarthing-dist/scripts/jira/jira-reconcile.sh +0 -0
- package/pennyfarthing-dist/scripts/jira/jira-sync-story.sh +0 -0
- package/pennyfarthing-dist/scripts/jira/sync-epic-jira.sh +0 -0
- package/pennyfarthing-dist/scripts/lib/background-tasks.sh +0 -0
- package/pennyfarthing-dist/scripts/lib/checkpoint.sh +0 -0
- package/pennyfarthing-dist/scripts/lib/common.sh +0 -0
- package/pennyfarthing-dist/scripts/lib/file-lock.sh +0 -0
- package/pennyfarthing-dist/scripts/lib/logging.sh +0 -0
- package/pennyfarthing-dist/scripts/lib/retry.sh +0 -0
- package/pennyfarthing-dist/scripts/maintenance/migrate-theme-schema.mjs +0 -0
- package/pennyfarthing-dist/scripts/maintenance/sidecar-health.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/add-short-names.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/add_short_names.py +0 -0
- package/pennyfarthing-dist/scripts/misc/backlog.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/check-status.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/find-related-work.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/generate-skill-docs.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/log-skill-usage.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/migrate-bmad-workflow.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/migrate_bmad_workflow.py +0 -0
- package/pennyfarthing-dist/scripts/misc/repo-scan.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/repo-utils.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/run-ci.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/run-timestamp.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/session-cleanup.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/skill-usage-report.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/statusline.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/uninstall.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/validate-subagent-frontmatter.sh +0 -0
- package/pennyfarthing-dist/scripts/portraits/generate-portraits.py +191 -57
- package/pennyfarthing-dist/scripts/portraits/generate-portraits.sh +26 -10
- package/pennyfarthing-dist/scripts/story/create-story.sh +0 -0
- package/pennyfarthing-dist/scripts/story/size-story.sh +0 -0
- package/pennyfarthing-dist/scripts/story/story-template.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/check.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/dev-story-workflow-import.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/epics-and-stories-workflow-import.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/handoff-phase-update.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/implementation-readiness-workflow-import.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/migrate-bmad-workflow.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/prd-workflow-import.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/project-context-workflow-import.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/test-character-voice.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/test-drift-detection.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/test-post-merge-hook.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/test-session-checkpoint.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/test-solo-command.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/ux-design-workflow-import.test.sh +0 -0
- package/pennyfarthing-dist/scripts/theme/list-themes.sh +0 -0
- package/pennyfarthing-dist/scripts/validation/validate-agent-schema.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/check.py +0 -0
- package/pennyfarthing-dist/scripts/workflow/check.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/complete-step.py +0 -0
- package/pennyfarthing-dist/scripts/workflow/finish-story.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/get-workflow-type.py +0 -0
- package/pennyfarthing-dist/scripts/workflow/get-workflow-type.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +0 -0
- package/pennyfarthing-dist/skills/pf-changelog/SKILL.md +4 -4
- package/pennyfarthing-dist/skills/pf-sprint/skill.md +1 -1
- package/pennyfarthing-dist/skills/pf-story/scripts/create-story.sh +0 -0
- package/pennyfarthing-dist/skills/pf-story/scripts/size-story.sh +0 -0
- package/pennyfarthing-dist/skills/pf-story/scripts/story-template.sh +0 -0
- package/pennyfarthing-dist/skills/pf-systematic-debugging/SKILL.md +0 -1
- package/pennyfarthing-dist/skills/pf-workflow/scripts/list-workflows.sh +0 -0
- package/pennyfarthing-dist/skills/pf-workflow/scripts/resume-workflow.sh +0 -0
- package/pennyfarthing-dist/skills/pf-workflow/scripts/show-workflow.sh +0 -0
- package/pennyfarthing-dist/skills/pf-workflow/scripts/start-workflow.sh +0 -0
- package/pennyfarthing-dist/skills/pf-workflow/scripts/workflow-status.sh +0 -0
- package/pennyfarthing-dist/skills/skill-registry.schema.json +4 -0
- package/pennyfarthing-dist/skills/skill-registry.yaml +8 -21
- 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__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/context.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/session_start_hook.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__/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/changed_panel.py +105 -0
- package/pennyfarthing_scripts/bikerack/debug_panel.py +218 -0
- package/pennyfarthing_scripts/bikerack/diffs_panel.py +203 -27
- package/pennyfarthing_scripts/cli.py +114 -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_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__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/cyclist-pretooluse-hook.sh +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/cli.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/prime/__pycache__/persona.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/session_start_hook.py +4 -4
- 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__/loader.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__/yaml_io.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/archive_epic.py +8 -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_cli_normalization.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_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_resolve_gate_file_field.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_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_version_sentinel.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_archive_epic.py +1 -2
- 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__/cli.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/benchmark/package-exports.test.d.ts.map +0 -1
- package/packages/core/dist/benchmark/package-exports.test.js.map +0 -1
- 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/benchmark-integration.test.d.ts +0 -13
- package/packages/core/dist/scripts/benchmark-integration.test.d.ts.map +0 -1
- package/packages/core/dist/scripts/benchmark-integration.test.js +0 -680
- package/packages/core/dist/scripts/benchmark-integration.test.js.map +0 -1
- package/packages/core/dist/scripts/debugging-scenarios.test.d.ts +0 -18
- package/packages/core/dist/scripts/debugging-scenarios.test.d.ts.map +0 -1
- package/packages/core/dist/scripts/debugging-scenarios.test.js +0 -317
- package/packages/core/dist/scripts/debugging-scenarios.test.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/packages/core/dist/scripts/job-fair-aggregator.test.d.ts +0 -14
- package/packages/core/dist/scripts/job-fair-aggregator.test.d.ts.map +0 -1
- package/packages/core/dist/scripts/job-fair-aggregator.test.js +0 -616
- package/packages/core/dist/scripts/job-fair-aggregator.test.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/skills/pf-dev-patterns/SKILL.md +0 -461
- package/scripts/README.md +0 -41
|
@@ -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)
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""Gate file validation — schema, depth, cycles, and mandatory elements.
|
|
2
|
+
|
|
3
|
+
Validates a gate file for:
|
|
4
|
+
1. Schema: <gate name="..."> with <purpose>, <pass>, <fail>
|
|
5
|
+
2. Depth: max nesting depth of 3
|
|
6
|
+
3. Cycles: no duplicate gate names
|
|
7
|
+
4. Completeness: all required elements non-empty
|
|
8
|
+
|
|
9
|
+
Reports ALL errors at once (not fail-fast).
|
|
10
|
+
|
|
11
|
+
Story: 107-3 (Gate authoring guide and validation command)
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import re
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
MAX_DEPTH = 3
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class GateInfo:
|
|
25
|
+
"""Parsed gate metadata."""
|
|
26
|
+
|
|
27
|
+
name: str
|
|
28
|
+
model: str = "haiku"
|
|
29
|
+
depth: int = 0
|
|
30
|
+
children: list[str] = field(default_factory=list)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class ValidationResult:
|
|
35
|
+
"""Result of gate file validation."""
|
|
36
|
+
|
|
37
|
+
valid: bool
|
|
38
|
+
gate_name: str | None = None
|
|
39
|
+
model: str | None = None
|
|
40
|
+
depth: int = 0
|
|
41
|
+
child_count: int = 0
|
|
42
|
+
errors: list[str] = field(default_factory=list)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def validate_gate_file(path: str | Path) -> ValidationResult:
|
|
46
|
+
"""Validate a gate file for schema compliance, depth, and cycles.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
path: Path to the gate file.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
ValidationResult with all errors collected.
|
|
53
|
+
"""
|
|
54
|
+
path = Path(path)
|
|
55
|
+
errors: list[str] = []
|
|
56
|
+
|
|
57
|
+
if not path.exists():
|
|
58
|
+
return ValidationResult(valid=False, errors=[f"File not found: {path}"])
|
|
59
|
+
|
|
60
|
+
content = path.read_text()
|
|
61
|
+
if not content.strip():
|
|
62
|
+
return ValidationResult(valid=False, errors=["File is empty"])
|
|
63
|
+
|
|
64
|
+
# Tokenize all gate-related tags
|
|
65
|
+
tokens = _tokenize(content)
|
|
66
|
+
|
|
67
|
+
if not tokens:
|
|
68
|
+
return ValidationResult(
|
|
69
|
+
valid=False,
|
|
70
|
+
errors=["No <gate> tag found in file"],
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Parse gate structure and collect errors
|
|
74
|
+
gate_names: list[str] = []
|
|
75
|
+
root_name: str | None = None
|
|
76
|
+
root_model: str | None = None
|
|
77
|
+
max_depth = 0
|
|
78
|
+
depth = 0
|
|
79
|
+
|
|
80
|
+
for token in tokens:
|
|
81
|
+
if token["type"] == "gate_open":
|
|
82
|
+
name = token.get("name")
|
|
83
|
+
model = token.get("model", "haiku")
|
|
84
|
+
|
|
85
|
+
if depth == 0:
|
|
86
|
+
root_name = name
|
|
87
|
+
root_model = model
|
|
88
|
+
|
|
89
|
+
if not name:
|
|
90
|
+
errors.append(
|
|
91
|
+
f"Gate element at depth {depth} missing required 'name' attribute"
|
|
92
|
+
)
|
|
93
|
+
else:
|
|
94
|
+
# Check for duplicate names (cycle indicator)
|
|
95
|
+
if name in gate_names:
|
|
96
|
+
errors.append(
|
|
97
|
+
f"Duplicate gate name '{name}' — names must be unique within a file"
|
|
98
|
+
)
|
|
99
|
+
gate_names.append(name)
|
|
100
|
+
|
|
101
|
+
depth += 1
|
|
102
|
+
if depth - 1 > max_depth:
|
|
103
|
+
max_depth = depth - 1
|
|
104
|
+
|
|
105
|
+
# Depth limit check (depth is 0-indexed, so depth-1 after increment)
|
|
106
|
+
if depth - 1 > MAX_DEPTH:
|
|
107
|
+
gate_label = name or "(unnamed)"
|
|
108
|
+
errors.append(
|
|
109
|
+
f"Gate depth limit exceeded: '{gate_label}' at depth {depth - 1} (max {MAX_DEPTH})"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
elif token["type"] == "gate_close":
|
|
113
|
+
depth -= 1
|
|
114
|
+
|
|
115
|
+
# Validate required child elements for each gate
|
|
116
|
+
_validate_required_elements(content, gate_names, errors)
|
|
117
|
+
|
|
118
|
+
child_count = len(gate_names) - 1 if len(gate_names) > 1 else 0
|
|
119
|
+
|
|
120
|
+
return ValidationResult(
|
|
121
|
+
valid=len(errors) == 0,
|
|
122
|
+
gate_name=root_name,
|
|
123
|
+
model=root_model,
|
|
124
|
+
depth=max_depth,
|
|
125
|
+
child_count=child_count,
|
|
126
|
+
errors=errors,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _tokenize(content: str) -> list[dict]:
|
|
131
|
+
"""Tokenize gate-related XML tags from content."""
|
|
132
|
+
tokens: list[dict] = []
|
|
133
|
+
|
|
134
|
+
# Match opening gate tags with attributes
|
|
135
|
+
open_pattern = re.compile(r"<gate\b([^>]*)>", re.IGNORECASE)
|
|
136
|
+
close_pattern = re.compile(r"</gate\s*>", re.IGNORECASE)
|
|
137
|
+
|
|
138
|
+
# Build a list of all tags with their positions
|
|
139
|
+
events: list[tuple[int, dict]] = []
|
|
140
|
+
|
|
141
|
+
for m in open_pattern.finditer(content):
|
|
142
|
+
attrs = m.group(1)
|
|
143
|
+
name_match = re.search(r'name="([^"]*)"', attrs)
|
|
144
|
+
model_match = re.search(r'model="([^"]*)"', attrs)
|
|
145
|
+
events.append((m.start(), {
|
|
146
|
+
"type": "gate_open",
|
|
147
|
+
"name": name_match.group(1) if name_match else None,
|
|
148
|
+
"model": model_match.group(1) if model_match else "haiku",
|
|
149
|
+
}))
|
|
150
|
+
|
|
151
|
+
for m in close_pattern.finditer(content):
|
|
152
|
+
events.append((m.start(), {"type": "gate_close"}))
|
|
153
|
+
|
|
154
|
+
# Sort by position
|
|
155
|
+
events.sort(key=lambda e: e[0])
|
|
156
|
+
tokens = [e[1] for e in events]
|
|
157
|
+
|
|
158
|
+
return tokens
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _validate_required_elements(
|
|
162
|
+
content: str,
|
|
163
|
+
gate_names: list[str],
|
|
164
|
+
errors: list[str],
|
|
165
|
+
) -> None:
|
|
166
|
+
"""Validate that each gate has <purpose>, <pass>, and <fail> elements.
|
|
167
|
+
|
|
168
|
+
For each gate, checks that required child elements exist and are non-empty
|
|
169
|
+
within the gate's scope.
|
|
170
|
+
"""
|
|
171
|
+
# For simple files with one gate, check globally
|
|
172
|
+
# For nested files, check per-gate scope
|
|
173
|
+
if len(gate_names) <= 1:
|
|
174
|
+
name = gate_names[0] if gate_names else "(unnamed)"
|
|
175
|
+
_check_required_for_gate(content, name, errors)
|
|
176
|
+
else:
|
|
177
|
+
# Split content by gate scopes and validate each
|
|
178
|
+
# Use a stack-based approach to extract each gate's direct content
|
|
179
|
+
_validate_nested_gates(content, errors)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _check_required_for_gate(
|
|
183
|
+
scope_content: str,
|
|
184
|
+
gate_name: str,
|
|
185
|
+
errors: list[str],
|
|
186
|
+
) -> None:
|
|
187
|
+
"""Check that a gate scope contains non-empty <purpose>, <pass>, <fail>."""
|
|
188
|
+
for element in ("purpose", "pass", "fail"):
|
|
189
|
+
pattern = re.compile(
|
|
190
|
+
rf"<{element}>(.*?)</{element}>",
|
|
191
|
+
re.DOTALL | re.IGNORECASE,
|
|
192
|
+
)
|
|
193
|
+
match = pattern.search(scope_content)
|
|
194
|
+
if not match:
|
|
195
|
+
errors.append(f"Gate '{gate_name}' missing required <{element}> block")
|
|
196
|
+
elif not match.group(1).strip():
|
|
197
|
+
errors.append(f"Gate '{gate_name}' has empty <{element}> block")
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _validate_nested_gates(content: str, errors: list[str]) -> None:
|
|
201
|
+
"""Validate required elements for each gate in a nested structure."""
|
|
202
|
+
# Extract each gate's scope using a stack-based parser
|
|
203
|
+
open_pattern = re.compile(r"<gate\b([^>]*)>", re.IGNORECASE)
|
|
204
|
+
close_pattern = re.compile(r"</gate\s*>", re.IGNORECASE)
|
|
205
|
+
|
|
206
|
+
events: list[tuple[int, str, str | None]] = []
|
|
207
|
+
|
|
208
|
+
for m in open_pattern.finditer(content):
|
|
209
|
+
name_match = re.search(r'name="([^"]*)"', m.group(1))
|
|
210
|
+
name = name_match.group(1) if name_match else None
|
|
211
|
+
events.append((m.start(), "open", name))
|
|
212
|
+
|
|
213
|
+
for m in close_pattern.finditer(content):
|
|
214
|
+
events.append((m.start(), "close", None))
|
|
215
|
+
|
|
216
|
+
events.sort(key=lambda e: e[0])
|
|
217
|
+
|
|
218
|
+
# Stack-based scope extraction
|
|
219
|
+
stack: list[tuple[str | None, int]] = [] # (name, start_pos)
|
|
220
|
+
|
|
221
|
+
for pos, event_type, name in events:
|
|
222
|
+
if event_type == "open":
|
|
223
|
+
stack.append((name, pos))
|
|
224
|
+
elif event_type == "close" and stack:
|
|
225
|
+
gate_name, start_pos = stack.pop()
|
|
226
|
+
# Extract this gate's direct content (between open and close)
|
|
227
|
+
gate_scope = content[start_pos:pos]
|
|
228
|
+
# Remove nested gate content to check only direct children
|
|
229
|
+
direct_content = _remove_nested_gates(gate_scope)
|
|
230
|
+
label = gate_name or "(unnamed)"
|
|
231
|
+
_check_required_for_gate(direct_content, label, errors)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _remove_nested_gates(scope: str) -> str:
|
|
235
|
+
"""Remove nested <gate>...</gate> blocks from scope content.
|
|
236
|
+
|
|
237
|
+
Keeps the outermost gate's direct content only.
|
|
238
|
+
"""
|
|
239
|
+
depth = 0
|
|
240
|
+
result: list[str] = []
|
|
241
|
+
i = 0
|
|
242
|
+
|
|
243
|
+
open_pattern = re.compile(r"<gate\b[^>]*>", re.IGNORECASE)
|
|
244
|
+
close_pattern = re.compile(r"</gate\s*>", re.IGNORECASE)
|
|
245
|
+
|
|
246
|
+
while i < len(scope):
|
|
247
|
+
open_match = open_pattern.match(scope, i)
|
|
248
|
+
close_match = close_pattern.match(scope, i)
|
|
249
|
+
|
|
250
|
+
if open_match:
|
|
251
|
+
depth += 1
|
|
252
|
+
if depth <= 1:
|
|
253
|
+
# Keep the outermost gate open tag
|
|
254
|
+
result.append(open_match.group())
|
|
255
|
+
i = open_match.end()
|
|
256
|
+
elif close_match:
|
|
257
|
+
if depth <= 1:
|
|
258
|
+
result.append(close_match.group())
|
|
259
|
+
depth -= 1
|
|
260
|
+
i = close_match.end()
|
|
261
|
+
else:
|
|
262
|
+
if depth <= 1:
|
|
263
|
+
result.append(scope[i])
|
|
264
|
+
i += 1
|
|
265
|
+
|
|
266
|
+
return "".join(result)
|
|
File without changes
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Git CLI - Click-based CLI for git repository operations.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
pf git [COMMAND] [ARGS]...
|
|
6
|
+
|
|
7
|
+
Commands:
|
|
8
|
+
status Check git status of all project repos
|
|
9
|
+
cleanup Organize changes into proper commits/branches
|
|
10
|
+
branches Create feature branches from a story
|
|
11
|
+
release Interactive release with verification gates
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import click
|
|
15
|
+
|
|
16
|
+
from pennyfarthing_scripts.common.config import get_project_root
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@click.group()
|
|
20
|
+
def git():
|
|
21
|
+
"""Repository operations across all configured repos.
|
|
22
|
+
|
|
23
|
+
\b
|
|
24
|
+
Commands:
|
|
25
|
+
status - Check git status of all repos
|
|
26
|
+
cleanup - Organize changes into commits/branches
|
|
27
|
+
branches - Create feature branches from a story
|
|
28
|
+
release - Interactive release with verification gates
|
|
29
|
+
"""
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@git.command()
|
|
34
|
+
@click.option("--brief", is_flag=True, help="One-line-per-repo summary")
|
|
35
|
+
def status(brief: bool):
|
|
36
|
+
"""Check git status of all project repos.
|
|
37
|
+
|
|
38
|
+
Shows branch, uncommitted changes, and ahead/behind status for each repo.
|
|
39
|
+
"""
|
|
40
|
+
import subprocess
|
|
41
|
+
|
|
42
|
+
root = get_project_root()
|
|
43
|
+
script = root / ".pennyfarthing" / "scripts" / "git" / "git-status-all.sh"
|
|
44
|
+
|
|
45
|
+
if not script.is_file():
|
|
46
|
+
click.echo("Error: git-status-all.sh not found", err=True)
|
|
47
|
+
raise SystemExit(1)
|
|
48
|
+
|
|
49
|
+
cmd = [str(script)]
|
|
50
|
+
if brief:
|
|
51
|
+
cmd.append("--brief")
|
|
52
|
+
|
|
53
|
+
result = subprocess.run(cmd, cwd=str(root))
|
|
54
|
+
raise SystemExit(result.returncode)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@git.command()
|
|
58
|
+
def cleanup():
|
|
59
|
+
"""Organize changes into proper commits and branches.
|
|
60
|
+
|
|
61
|
+
Starts the git-cleanup stepped workflow via BikeLane.
|
|
62
|
+
Equivalent to: /pf-workflow start git-cleanup
|
|
63
|
+
"""
|
|
64
|
+
click.echo("Starting git-cleanup workflow...")
|
|
65
|
+
click.echo("Run: /pf-workflow start git-cleanup")
|
|
66
|
+
click.echo("Or: pf workflow start git-cleanup")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@git.command()
|
|
70
|
+
@click.argument("story_id")
|
|
71
|
+
def branches(story_id: str):
|
|
72
|
+
"""Create feature branches in both repos from a story.
|
|
73
|
+
|
|
74
|
+
\b
|
|
75
|
+
Arguments:
|
|
76
|
+
STORY_ID - The story ID to create branches for (e.g., 86-3)
|
|
77
|
+
"""
|
|
78
|
+
import subprocess
|
|
79
|
+
|
|
80
|
+
root = get_project_root()
|
|
81
|
+
script = root / ".pennyfarthing" / "scripts" / "git" / "create-branches.sh"
|
|
82
|
+
|
|
83
|
+
if not script.is_file():
|
|
84
|
+
click.echo("Error: create-branches.sh not found", err=True)
|
|
85
|
+
raise SystemExit(1)
|
|
86
|
+
|
|
87
|
+
result = subprocess.run([str(script), story_id], cwd=str(root))
|
|
88
|
+
raise SystemExit(result.returncode)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@git.command()
|
|
92
|
+
def release():
|
|
93
|
+
"""Interactive release with verification gates.
|
|
94
|
+
|
|
95
|
+
Starts the release stepped workflow via BikeLane.
|
|
96
|
+
Equivalent to: /pf-workflow start release
|
|
97
|
+
"""
|
|
98
|
+
click.echo("Starting release workflow...")
|
|
99
|
+
click.echo("Run: /pf-workflow start release")
|
|
100
|
+
click.echo("Or: pf workflow start release")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Handoff CLI — Phase gate resolution and atomic session transitions."""
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Handoff CLI — Phase gate resolution, session transitions, and marker generation.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
pf handoff resolve-gate STORY_ID WORKFLOW PHASE
|
|
5
|
+
pf handoff complete-phase STORY_ID WORKFLOW FROM_PHASE TO_PHASE GATE_TYPE
|
|
6
|
+
pf handoff marker NEXT_AGENT [--error MESSAGE]
|
|
7
|
+
|
|
8
|
+
Stories: 105-1, 105-4 (Script-First Handoff)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import click
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.group()
|
|
17
|
+
def handoff():
|
|
18
|
+
"""Phase gate resolution and session transitions.
|
|
19
|
+
|
|
20
|
+
\b
|
|
21
|
+
Commands:
|
|
22
|
+
resolve-gate - Resolve gate for current phase
|
|
23
|
+
complete-phase - Complete phase transition atomically
|
|
24
|
+
marker - Generate AGENT_COMMAND handoff marker
|
|
25
|
+
"""
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@handoff.command("resolve-gate")
|
|
30
|
+
@click.argument("story_id")
|
|
31
|
+
@click.argument("workflow")
|
|
32
|
+
@click.argument("phase")
|
|
33
|
+
def resolve_gate_cmd(story_id: str, workflow: str, phase: str):
|
|
34
|
+
"""Resolve the gate for the current workflow phase.
|
|
35
|
+
|
|
36
|
+
Reads workflow YAML, checks assessment, returns RESOLVE_RESULT.
|
|
37
|
+
|
|
38
|
+
\b
|
|
39
|
+
Arguments:
|
|
40
|
+
STORY_ID - Story identifier (e.g., 105-1)
|
|
41
|
+
WORKFLOW - Workflow name (e.g., tdd, trivial, patch)
|
|
42
|
+
PHASE - Current phase name (e.g., green, implement, fix)
|
|
43
|
+
"""
|
|
44
|
+
from pennyfarthing_scripts.handoff.resolve_gate import resolve_gate
|
|
45
|
+
|
|
46
|
+
result = resolve_gate(story_id, workflow, phase)
|
|
47
|
+
|
|
48
|
+
import yaml
|
|
49
|
+
|
|
50
|
+
click.echo(yaml.dump({"RESOLVE_RESULT": result}, default_flow_style=False).rstrip())
|
|
51
|
+
|
|
52
|
+
if result.get("status") == "blocked":
|
|
53
|
+
raise SystemExit(1)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@handoff.command("complete-phase")
|
|
57
|
+
@click.argument("story_id")
|
|
58
|
+
@click.argument("workflow")
|
|
59
|
+
@click.argument("from_phase")
|
|
60
|
+
@click.argument("to_phase")
|
|
61
|
+
@click.argument("gate_type")
|
|
62
|
+
def complete_phase_cmd(
|
|
63
|
+
story_id: str,
|
|
64
|
+
workflow: str,
|
|
65
|
+
from_phase: str,
|
|
66
|
+
to_phase: str,
|
|
67
|
+
gate_type: str,
|
|
68
|
+
):
|
|
69
|
+
"""Complete a phase transition with atomic session update.
|
|
70
|
+
|
|
71
|
+
Updates session file: phase line, timestamps, history tables.
|
|
72
|
+
|
|
73
|
+
\b
|
|
74
|
+
Arguments:
|
|
75
|
+
STORY_ID - Story identifier (e.g., 105-1)
|
|
76
|
+
WORKFLOW - Workflow name (e.g., tdd, trivial)
|
|
77
|
+
FROM_PHASE - Phase being completed (e.g., green)
|
|
78
|
+
TO_PHASE - Phase being entered (e.g., review)
|
|
79
|
+
GATE_TYPE - Gate type that was passed (e.g., tests_pass)
|
|
80
|
+
"""
|
|
81
|
+
from pennyfarthing_scripts.handoff.complete_phase import complete_phase
|
|
82
|
+
|
|
83
|
+
result = complete_phase(story_id, workflow, from_phase, to_phase, gate_type)
|
|
84
|
+
|
|
85
|
+
import yaml
|
|
86
|
+
|
|
87
|
+
click.echo(yaml.dump({"COMPLETE_RESULT": result}, default_flow_style=False).rstrip())
|
|
88
|
+
|
|
89
|
+
if result.get("status") == "error":
|
|
90
|
+
raise SystemExit(1)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@handoff.command("marker")
|
|
94
|
+
@click.argument("next_agent", required=False, default=None)
|
|
95
|
+
@click.option("--error", "error_msg", default=None, help="Generate error marker")
|
|
96
|
+
def marker_cmd(next_agent: str | None, error_msg: str | None):
|
|
97
|
+
"""Generate AGENT_COMMAND handoff marker block.
|
|
98
|
+
|
|
99
|
+
Environment-aware marker generation. Detects Cyclist, relay mode,
|
|
100
|
+
and context usage to choose the appropriate marker type.
|
|
101
|
+
|
|
102
|
+
\b
|
|
103
|
+
Arguments:
|
|
104
|
+
NEXT_AGENT - Agent to hand off to (e.g., dev, tea, reviewer)
|
|
105
|
+
|
|
106
|
+
\b
|
|
107
|
+
Options:
|
|
108
|
+
--error MSG - Generate an error marker instead of a handoff
|
|
109
|
+
"""
|
|
110
|
+
from pennyfarthing_scripts.handoff.marker import generate_marker
|
|
111
|
+
|
|
112
|
+
if not next_agent and not error_msg:
|
|
113
|
+
raise click.UsageError(
|
|
114
|
+
"Provide NEXT_AGENT or --error MESSAGE.\n\n"
|
|
115
|
+
"Examples:\n"
|
|
116
|
+
" pf handoff marker dev\n"
|
|
117
|
+
" pf handoff marker --error 'Tests failing'"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
click.echo(generate_marker(next_agent, error=error_msg))
|