@pennyfarthing/core 11.2.0 → 11.2.2
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 +100 -40
- package/package.json +2 -1
- package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/doctor.js +474 -66
- package/packages/core/dist/cli/commands/doctor.js.map +1 -1
- package/packages/core/dist/cli/commands/init.js +4 -4
- package/packages/core/dist/cli/commands/init.js.map +1 -1
- package/packages/core/dist/cli/commands/update.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/update.js +4 -5
- package/packages/core/dist/cli/commands/update.js.map +1 -1
- package/packages/core/dist/cli/utils/constants.d.ts +3 -8
- package/packages/core/dist/cli/utils/constants.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/constants.js +3 -4
- package/packages/core/dist/cli/utils/constants.js.map +1 -1
- package/packages/core/dist/cli/utils/settings.d.ts +7 -0
- package/packages/core/dist/cli/utils/settings.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/settings.js +70 -29
- package/packages/core/dist/cli/utils/settings.js.map +1 -1
- package/packages/core/dist/cli/utils/symlinks.js +16 -16
- package/packages/core/dist/cli/utils/symlinks.js.map +1 -1
- package/packages/core/dist/consultation/dialogue-manager.d.ts +1 -1
- package/packages/core/dist/consultation/dialogue-manager.d.ts.map +1 -1
- package/packages/core/dist/consultation/dialogue-manager.js +1 -1
- package/packages/core/dist/consultation/dialogue-manager.js.map +1 -1
- package/packages/core/dist/consultation/dialogue-manager.test.js.map +1 -1
- package/packages/core/dist/consultation/tandem-metrics.d.ts +91 -0
- package/packages/core/dist/consultation/tandem-metrics.d.ts.map +1 -0
- package/packages/core/dist/consultation/tandem-metrics.js +131 -0
- package/packages/core/dist/consultation/tandem-metrics.js.map +1 -0
- package/packages/core/dist/consultation/tandem-metrics.test.d.ts +18 -0
- package/packages/core/dist/consultation/tandem-metrics.test.d.ts.map +1 -0
- package/packages/core/dist/consultation/tandem-metrics.test.js +457 -0
- package/packages/core/dist/consultation/tandem-metrics.test.js.map +1 -0
- package/packages/core/dist/public/css/react.css +1 -1
- package/packages/core/dist/public/js/react/react.js +14 -14
- package/packages/core/dist/server/api/agent-load.js +1 -1
- package/packages/core/dist/server/api/agent-load.js.map +1 -1
- package/packages/core/dist/server/api/git.d.ts.map +1 -1
- package/packages/core/dist/server/api/git.js +0 -1
- package/packages/core/dist/server/api/git.js.map +1 -1
- package/packages/core/dist/server/api/index.d.ts +2 -0
- package/packages/core/dist/server/api/index.d.ts.map +1 -1
- package/packages/core/dist/server/api/index.js +2 -0
- package/packages/core/dist/server/api/index.js.map +1 -1
- package/packages/core/dist/server/api/project-info.d.ts +11 -0
- package/packages/core/dist/server/api/project-info.d.ts.map +1 -0
- package/packages/core/dist/server/api/project-info.js +18 -0
- package/packages/core/dist/server/api/project-info.js.map +1 -0
- package/packages/core/dist/server/otlp-receiver.d.ts.map +1 -1
- package/packages/core/dist/server/otlp-receiver.js +18 -1
- package/packages/core/dist/server/otlp-receiver.js.map +1 -1
- package/packages/core/dist/server/otlp-receiver.test.js +1 -1
- package/packages/core/dist/server/otlp-receiver.test.js.map +1 -1
- package/packages/core/dist/server/server.d.ts +0 -3
- package/packages/core/dist/server/server.d.ts.map +1 -1
- package/packages/core/dist/server/server.js +5 -38
- package/packages/core/dist/server/server.js.map +1 -1
- package/packages/core/dist/server/server.test.d.ts +1 -1
- package/packages/core/dist/server/server.test.js +12 -23
- package/packages/core/dist/server/server.test.js.map +1 -1
- package/packages/core/dist/server/settings.d.ts +1 -0
- package/packages/core/dist/server/settings.d.ts.map +1 -1
- package/packages/core/dist/server/settings.js +13 -0
- package/packages/core/dist/server/settings.js.map +1 -1
- package/packages/core/dist/shared/capabilities.d.ts +88 -0
- package/packages/core/dist/shared/capabilities.d.ts.map +1 -0
- package/packages/core/dist/shared/capabilities.js +133 -0
- package/packages/core/dist/shared/capabilities.js.map +1 -0
- package/packages/core/dist/shared/capabilities.test.d.ts +2 -0
- package/packages/core/dist/shared/capabilities.test.d.ts.map +1 -0
- package/packages/core/dist/shared/capabilities.test.js +217 -0
- package/packages/core/dist/shared/capabilities.test.js.map +1 -0
- package/packages/core/dist/shared/spawn-prompt.d.ts +47 -0
- package/packages/core/dist/shared/spawn-prompt.d.ts.map +1 -0
- package/packages/core/dist/shared/spawn-prompt.js +82 -0
- package/packages/core/dist/shared/spawn-prompt.js.map +1 -0
- package/packages/core/dist/shared/spawn-prompt.test.d.ts +2 -0
- package/packages/core/dist/shared/spawn-prompt.test.d.ts.map +1 -0
- package/packages/core/dist/shared/spawn-prompt.test.js +251 -0
- package/packages/core/dist/shared/spawn-prompt.test.js.map +1 -0
- package/packages/core/dist/workflow/tandem-workflow-templates.test.d.ts +18 -0
- package/packages/core/dist/workflow/tandem-workflow-templates.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/tandem-workflow-templates.test.js +434 -0
- package/packages/core/dist/workflow/tandem-workflow-templates.test.js.map +1 -0
- package/packages/core/dist/workflow/team-lifecycle.d.ts +169 -0
- package/packages/core/dist/workflow/team-lifecycle.d.ts.map +1 -0
- package/packages/core/dist/workflow/team-lifecycle.js +217 -0
- package/packages/core/dist/workflow/team-lifecycle.js.map +1 -0
- package/packages/core/dist/workflow/team-lifecycle.test.d.ts +20 -0
- package/packages/core/dist/workflow/team-lifecycle.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/team-lifecycle.test.js +966 -0
- package/packages/core/dist/workflow/team-lifecycle.test.js.map +1 -0
- package/packages/core/dist/workflow/workflow-schema.d.ts +32 -0
- package/packages/core/dist/workflow/workflow-schema.d.ts.map +1 -1
- package/packages/core/dist/workflow/workflow-schema.js +120 -0
- package/packages/core/dist/workflow/workflow-schema.js.map +1 -1
- package/packages/core/dist/workflow/workflow-schema.test.d.ts.map +1 -1
- package/packages/core/dist/workflow/workflow-schema.test.js +570 -1
- package/packages/core/dist/workflow/workflow-schema.test.js.map +1 -1
- package/packages/core/dist/workflow/workflow-team-templates.test.d.ts +17 -0
- package/packages/core/dist/workflow/workflow-team-templates.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/workflow-team-templates.test.js +275 -0
- package/packages/core/dist/workflow/workflow-team-templates.test.js.map +1 -0
- package/pennyfarthing-dist/agents/dev.md +21 -12
- package/pennyfarthing-dist/agents/reviewer.md +23 -4
- package/pennyfarthing-dist/agents/sm-finish.md +19 -2
- package/pennyfarthing-dist/agents/sm-setup.md +7 -7
- package/pennyfarthing-dist/agents/sm.md +12 -12
- package/pennyfarthing-dist/agents/tea.md +2 -2
- package/pennyfarthing-dist/agents/testing-runner.md +1 -1
- package/pennyfarthing-dist/commands/pf-architect.md +1 -1
- package/pennyfarthing-dist/commands/pf-ba.md +1 -1
- package/pennyfarthing-dist/commands/pf-chore.md +2 -2
- package/pennyfarthing-dist/commands/pf-dev.md +1 -1
- package/pennyfarthing-dist/commands/pf-devops.md +1 -1
- package/pennyfarthing-dist/commands/pf-epic.md +6 -6
- package/pennyfarthing-dist/commands/pf-git.md +12 -10
- package/pennyfarthing-dist/commands/pf-health-check.md +1 -1
- package/pennyfarthing-dist/commands/pf-help.md +12 -12
- package/pennyfarthing-dist/commands/pf-orchestrator.md +1 -1
- package/pennyfarthing-dist/commands/pf-pm.md +1 -1
- package/pennyfarthing-dist/commands/pf-prime.md +8 -8
- package/pennyfarthing-dist/commands/pf-reviewer.md +1 -1
- package/pennyfarthing-dist/commands/pf-session.md +7 -7
- package/pennyfarthing-dist/commands/pf-sm.md +1 -1
- package/pennyfarthing-dist/commands/pf-sprint.md +7 -7
- package/pennyfarthing-dist/commands/pf-tea.md +1 -1
- package/pennyfarthing-dist/commands/pf-tech-writer.md +1 -1
- package/pennyfarthing-dist/commands/pf-theme.md +9 -9
- package/pennyfarthing-dist/commands/pf-ux-designer.md +1 -1
- package/pennyfarthing-dist/commands/pf-work.md +1 -1
- package/pennyfarthing-dist/gates/approval.md +63 -0
- package/pennyfarthing-dist/gates/confidence-sm.md +71 -0
- package/pennyfarthing-dist/gates/context-ok.md +56 -0
- package/pennyfarthing-dist/gates/evaluations/confidence-sm.md +54 -0
- package/pennyfarthing-dist/gates/quality-pass.md +67 -0
- package/pennyfarthing-dist/gates/tests-fail.md +84 -0
- package/pennyfarthing-dist/gates/tests-pass.md +79 -0
- package/pennyfarthing-dist/guides/agent-behavior.md +84 -29
- package/pennyfarthing-dist/guides/agent-coordination.md +10 -10
- package/pennyfarthing-dist/guides/agent-tag-taxonomy.md +6 -6
- package/pennyfarthing-dist/guides/agent-template-tactical.md +1 -1
- package/pennyfarthing-dist/guides/bell-mode.md +1 -1
- package/pennyfarthing-dist/guides/bikerack.md +10 -10
- package/pennyfarthing-dist/guides/brownfield-tools.md +24 -24
- package/pennyfarthing-dist/guides/command-tag-taxonomy.md +1 -1
- package/pennyfarthing-dist/guides/gate-schema.md +2 -2
- package/pennyfarthing-dist/guides/gates.md +3 -3
- package/pennyfarthing-dist/guides/handoff-cli.md +8 -8
- package/pennyfarthing-dist/guides/hooks.md +29 -29
- package/pennyfarthing-dist/guides/prime.md +2 -2
- package/pennyfarthing-dist/guides/reflector.md +1 -1
- package/pennyfarthing-dist/guides/skill-schema.md +6 -6
- package/pennyfarthing-dist/guides/tandem-protocol.md +3 -3
- package/pennyfarthing-dist/guides/workflow-schema.md +1 -1
- package/pennyfarthing-dist/guides/worktree-mode.md +3 -3
- package/pennyfarthing-dist/guides/xml-tags.md +8 -8
- package/pennyfarthing-dist/scripts/README.md +4 -4
- package/pennyfarthing-dist/scripts/core/agent-session.sh +2 -5
- package/pennyfarthing-dist/scripts/core/check-context.sh +3 -1
- package/pennyfarthing-dist/scripts/core/pf.sh +5 -0
- package/pennyfarthing-dist/scripts/core/phase-check-start.sh +4 -89
- package/pennyfarthing-dist/scripts/core/prime.sh +2 -25
- package/pennyfarthing-dist/scripts/git/README.md +14 -14
- package/pennyfarthing-dist/scripts/git/create-feature-branches.sh +2 -3
- package/pennyfarthing-dist/scripts/git/git-status-all.sh +2 -3
- package/pennyfarthing-dist/scripts/git/install-git-hooks.sh +2 -3
- package/pennyfarthing-dist/scripts/git/worktree-manager.sh +2 -4
- package/pennyfarthing-dist/scripts/hooks/README.md +6 -6
- package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +4 -183
- package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +4 -95
- package/pennyfarthing-dist/scripts/hooks/context-warning.sh +4 -65
- package/pennyfarthing-dist/scripts/hooks/cyclist-pretooluse-hook.sh +3 -31
- package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +5 -4
- package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +29 -34
- package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +4 -71
- package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +3 -19
- package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +4 -30
- package/pennyfarthing-dist/scripts/hooks/session-start.sh +3 -32
- package/pennyfarthing-dist/scripts/hooks/session-stop.sh +4 -65
- package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +4 -78
- package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +3 -93
- package/pennyfarthing-dist/scripts/lib/env.sh +34 -0
- package/pennyfarthing-dist/scripts/lib/run-pf.sh +39 -0
- package/pennyfarthing-dist/scripts/misc/README.md +1 -1
- package/pennyfarthing-dist/scripts/misc/statusline.sh +4 -301
- package/pennyfarthing-dist/scripts/sprint/README.md +21 -21
- package/pennyfarthing-dist/scripts/workflow/README.md +2 -2
- package/pennyfarthing-dist/scripts/workflow/finish-story.sh +2 -16
- package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +3 -3
- package/pennyfarthing-dist/scripts/workflow/get-workflow-type.sh +3 -3
- package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +3 -3
- package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +3 -3
- package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +3 -3
- package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +3 -3
- package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +3 -3
- package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +3 -3
- package/pennyfarthing-dist/skills/pf-bc/examples.md +23 -23
- package/pennyfarthing-dist/skills/pf-bc/skill.md +17 -17
- package/pennyfarthing-dist/skills/pf-bc/usage.md +8 -8
- package/pennyfarthing-dist/skills/pf-jira/SKILL.md +15 -15
- package/pennyfarthing-dist/skills/pf-jira/examples.md +48 -48
- package/pennyfarthing-dist/skills/pf-jira/usage.md +15 -15
- package/pennyfarthing-dist/skills/pf-sprint/examples.md +80 -80
- package/pennyfarthing-dist/skills/pf-sprint/skill.md +35 -35
- package/pennyfarthing-dist/skills/pf-sprint/usage.md +30 -30
- package/pennyfarthing-dist/skills/pf-theme/examples.md +15 -15
- package/pennyfarthing-dist/skills/pf-theme/skill.md +6 -6
- package/pennyfarthing-dist/skills/pf-theme/usage.md +5 -5
- package/pennyfarthing-dist/skills/pf-workflow/examples.md +27 -27
- package/pennyfarthing-dist/skills/pf-workflow/skill.md +11 -11
- package/pennyfarthing-dist/skills/pf-workflow/usage.md +11 -11
- package/pennyfarthing-dist/skills/skill-registry.yaml +19 -19
- package/pennyfarthing-dist/templates/settings.local.json.template +19 -10
- package/pennyfarthing-dist/workflows/bdd-team.yaml +89 -0
- package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-05-import-to-future.md +1 -1
- package/pennyfarthing-dist/workflows/git-cleanup/steps/step-01-analyze.md +1 -1
- package/pennyfarthing-dist/workflows/git-cleanup/steps/step-04-verify.md +1 -1
- package/pennyfarthing-dist/workflows/git-cleanup/steps/step-05-complete.md +1 -1
- package/pennyfarthing-dist/workflows/project-setup/steps/step-01-discover.md +47 -0
- package/pennyfarthing-dist/workflows/tdd-team.yaml +80 -0
- package/pennyfarthing-dist/workflows/tdd.yaml +11 -2
- package/pennyfarthing_scripts/CLAUDE.md +19 -10
- package/pennyfarthing_scripts/__init__.py +1 -1
- 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__/context.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/hooks.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/bc/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/__pycache__/focus.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/__pycache__/split.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/cli.py +2 -2
- package/pennyfarthing_scripts/bellmode_hook.py +9 -296
- package/pennyfarthing_scripts/bikerack/__pycache__/audit_log_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/background_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/changed_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/context_meter_footer.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/debug_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/diffs_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/events.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/git_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/portrait_resolver.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/progress_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/story_detail_data.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/story_detail_screen.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/ws_client.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/audit_log_panel.py +161 -0
- package/pennyfarthing_scripts/bikerack/base_panel.py +27 -4
- package/pennyfarthing_scripts/bikerack/changed_panel.py +96 -4
- package/pennyfarthing_scripts/bikerack/context_meter_footer.py +88 -0
- package/pennyfarthing_scripts/bikerack/debug_panel.py +1 -1
- package/pennyfarthing_scripts/bikerack/diffs_panel.py +30 -0
- package/pennyfarthing_scripts/bikerack/events.py +28 -0
- package/pennyfarthing_scripts/bikerack/launcher.py +6 -6
- package/pennyfarthing_scripts/bikerack/portrait_resolver.py +139 -0
- package/pennyfarthing_scripts/bikerack/progress_panel.py +0 -1
- package/pennyfarthing_scripts/bikerack/sprint_panel.py +373 -142
- package/pennyfarthing_scripts/bikerack/story_detail_data.py +247 -0
- package/pennyfarthing_scripts/bikerack/story_detail_screen.py +177 -0
- package/pennyfarthing_scripts/bikerack/tui.py +304 -62
- package/pennyfarthing_scripts/bikerack/ws_client.py +2 -2
- package/pennyfarthing_scripts/cli.py +5 -0
- package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/config.py +29 -2
- package/pennyfarthing_scripts/common/pr_config.py +38 -0
- package/pennyfarthing_scripts/consultation/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/consultation/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/consultation/cli.py +3 -3
- package/pennyfarthing_scripts/context.py +3 -3
- 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__/repos.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/hooks_installer.py +2 -3
- package/pennyfarthing_scripts/git/status_all.py +1 -1
- package/pennyfarthing_scripts/git/worktree.py +2 -2
- package/pennyfarthing_scripts/git_group/__pycache__/cli.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__/marker.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/phase_check.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/cli.py +33 -1
- package/pennyfarthing_scripts/handoff/complete_phase.py +28 -0
- package/pennyfarthing_scripts/handoff/marker.py +15 -15
- package/pennyfarthing_scripts/handoff/phase_check.py +96 -0
- package/pennyfarthing_scripts/handoff/resolve_gate.py +13 -1
- package/pennyfarthing_scripts/hooks/__init__.py +442 -0
- package/pennyfarthing_scripts/hooks/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/bell_mode.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/context_breaker.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/context_warning.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/cyclist_pretooluse.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/pre_edit_check.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/reflector_check.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/schema_validation.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/session_start.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/session_stop.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/sprint_yaml_validation.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/statusline.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/bell_mode.py +214 -0
- package/pennyfarthing_scripts/hooks/cli.py +96 -0
- package/pennyfarthing_scripts/hooks/context_breaker.py +104 -0
- package/pennyfarthing_scripts/hooks/context_warning.py +66 -0
- package/pennyfarthing_scripts/hooks/cyclist_pretooluse.py +129 -0
- package/pennyfarthing_scripts/hooks/pre_edit_check.py +77 -0
- package/pennyfarthing_scripts/hooks/reflector_check.py +270 -0
- package/pennyfarthing_scripts/hooks/schema_validation.py +202 -0
- package/pennyfarthing_scripts/hooks/session_start.py +294 -0
- package/pennyfarthing_scripts/hooks/session_stop.py +111 -0
- package/pennyfarthing_scripts/hooks/sprint_yaml_validation.py +97 -0
- package/pennyfarthing_scripts/hooks/statusline.py +429 -0
- package/pennyfarthing_scripts/hooks.py +27 -432
- package/pennyfarthing_scripts/pretooluse_hook.py +3 -185
- package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/heatmap.py +3 -15
- package/pennyfarthing_scripts/prime/workflow.py +2 -1
- package/pennyfarthing_scripts/schema_validation_hook.py +3 -298
- package/pennyfarthing_scripts/session_start_hook.py +4 -186
- package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/loader.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/cli.py +121 -0
- package/pennyfarthing_scripts/sprint/loader.py +154 -3
- package/pennyfarthing_scripts/sprint/story_update.py +26 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_bikerack.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_workflow_list_team.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/test_bikerack.py +26 -26
- package/pennyfarthing_scripts/tests/test_dialogue_manager.py +0 -1
- package/pennyfarthing_scripts/tests/test_sprint_panel.py +344 -265
- package/pennyfarthing_scripts/tests/test_workflow_list_team.py +147 -0
- package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/skill_command.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/tandem_awareness.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/team_mode.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/team_mode.py +323 -0
- package/pennyfarthing_scripts/validate/adapters/workflow.py +19 -0
- package/pennyfarthing_scripts/welcome_hook.py +3 -149
- package/pennyfarthing_scripts/workflow/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/helpers.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/scale.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/state.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/team_lifecycle.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/cli.py +22 -20
- package/pennyfarthing_scripts/workflow/state.py +0 -1
- package/pennyfarthing_scripts/workflow/team_lifecycle.py +256 -0
- package/packages/core/dist/cli/cyclist-migration.test.d.ts +0 -16
- package/packages/core/dist/cli/cyclist-migration.test.d.ts.map +0 -1
- package/packages/core/dist/cli/cyclist-migration.test.js +0 -229
- package/packages/core/dist/cli/cyclist-migration.test.js.map +0 -1
- package/packages/core/dist/scripts/theme-detail.test.d.ts +0 -10
- package/packages/core/dist/scripts/theme-detail.test.d.ts.map +0 -1
- package/packages/core/dist/scripts/theme-detail.test.js +0 -199
- package/packages/core/dist/scripts/theme-detail.test.js.map +0 -1
|
@@ -16,6 +16,8 @@ Usage:
|
|
|
16
16
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
|
+
from datetime import UTC
|
|
20
|
+
|
|
19
21
|
import click
|
|
20
22
|
|
|
21
23
|
|
|
@@ -90,17 +92,18 @@ def workflow_phase_check(workflow_name: str, phase: str):
|
|
|
90
92
|
@workflow.command("handoff")
|
|
91
93
|
@click.argument("next_agent")
|
|
92
94
|
def workflow_handoff(next_agent: str):
|
|
93
|
-
"""Emit
|
|
95
|
+
"""Emit an environment-aware handoff marker.
|
|
96
|
+
|
|
97
|
+
Delegates to generate_marker() which detects Cyclist, relay mode,
|
|
98
|
+
and context usage to choose the appropriate marker type.
|
|
94
99
|
|
|
95
100
|
\b
|
|
96
101
|
Arguments:
|
|
97
102
|
NEXT_AGENT - The agent to hand off to (tea, dev, reviewer, etc.)
|
|
98
103
|
"""
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
click.echo(
|
|
102
|
-
click.echo(f' fallback: "Run `/{next_agent}` to continue"')
|
|
103
|
-
click.echo("---")
|
|
104
|
+
from pennyfarthing_scripts.handoff.marker import generate_marker
|
|
105
|
+
|
|
106
|
+
click.echo(generate_marker(next_agent))
|
|
104
107
|
|
|
105
108
|
|
|
106
109
|
# ---------------------------------------------------------------------------
|
|
@@ -140,7 +143,6 @@ def workflow_list_cmd():
|
|
|
140
143
|
|
|
141
144
|
Shows a markdown table with type, phases/steps, modes, and descriptions.
|
|
142
145
|
"""
|
|
143
|
-
import yaml as yaml_mod
|
|
144
146
|
|
|
145
147
|
from pennyfarthing_scripts.workflow.helpers import (
|
|
146
148
|
count_steps,
|
|
@@ -352,7 +354,7 @@ def workflow_start_cmd(name: str, mode: str | None):
|
|
|
352
354
|
Arguments:
|
|
353
355
|
NAME - Workflow name (e.g., architecture, release)
|
|
354
356
|
"""
|
|
355
|
-
from datetime import datetime
|
|
357
|
+
from datetime import datetime
|
|
356
358
|
|
|
357
359
|
from pennyfarthing_scripts.common.config import get_project_root
|
|
358
360
|
from pennyfarthing_scripts.workflow.helpers import (
|
|
@@ -443,7 +445,7 @@ def workflow_start_cmd(name: str, mode: str | None):
|
|
|
443
445
|
return
|
|
444
446
|
|
|
445
447
|
# Create session file
|
|
446
|
-
now = datetime.now(
|
|
448
|
+
now = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
447
449
|
wf_agent = wf.get("agent", "pm")
|
|
448
450
|
wf_desc = wf.get("description", "-")
|
|
449
451
|
|
|
@@ -515,7 +517,7 @@ def workflow_resume_cmd(name: str | None):
|
|
|
515
517
|
Arguments:
|
|
516
518
|
NAME - Workflow name (auto-detects from active session if omitted)
|
|
517
519
|
"""
|
|
518
|
-
from datetime import datetime
|
|
520
|
+
from datetime import datetime
|
|
519
521
|
|
|
520
522
|
from pennyfarthing_scripts.common.config import get_project_root
|
|
521
523
|
from pennyfarthing_scripts.workflow.helpers import (
|
|
@@ -600,7 +602,7 @@ def workflow_resume_cmd(name: str | None):
|
|
|
600
602
|
raise SystemExit(1)
|
|
601
603
|
|
|
602
604
|
# Update last updated timestamp
|
|
603
|
-
now = datetime.now(
|
|
605
|
+
now = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
604
606
|
import re
|
|
605
607
|
|
|
606
608
|
updated_content = re.sub(
|
|
@@ -787,7 +789,7 @@ def workflow_fix_phase_cmd(story_id: str, target_phase: str, dry_run: bool):
|
|
|
787
789
|
TARGET_PHASE - Target phase to set (e.g., review, approved, finish)
|
|
788
790
|
"""
|
|
789
791
|
import re
|
|
790
|
-
from datetime import datetime
|
|
792
|
+
from datetime import datetime
|
|
791
793
|
|
|
792
794
|
from pennyfarthing_scripts.common.config import get_project_root
|
|
793
795
|
from pennyfarthing_scripts.workflow.helpers import (
|
|
@@ -848,17 +850,17 @@ def workflow_fix_phase_cmd(story_id: str, target_phase: str, dry_run: bool):
|
|
|
848
850
|
# Find indices
|
|
849
851
|
try:
|
|
850
852
|
current_idx = phases.index(current_phase)
|
|
851
|
-
except ValueError:
|
|
853
|
+
except ValueError as err:
|
|
852
854
|
click.echo(f"Error: Current phase '{current_phase}' not found in {workflow_name} workflow", err=True)
|
|
853
855
|
click.echo(f"Valid phases: {', '.join(phases)}", err=True)
|
|
854
|
-
raise SystemExit(1)
|
|
856
|
+
raise SystemExit(1) from err
|
|
855
857
|
|
|
856
858
|
try:
|
|
857
859
|
target_idx = phases.index(target_phase)
|
|
858
|
-
except ValueError:
|
|
860
|
+
except ValueError as err:
|
|
859
861
|
click.echo(f"Error: Target phase '{target_phase}' not found in {workflow_name} workflow", err=True)
|
|
860
862
|
click.echo(f"Valid phases: {', '.join(phases)}", err=True)
|
|
861
|
-
raise SystemExit(1)
|
|
863
|
+
raise SystemExit(1) from err
|
|
862
864
|
|
|
863
865
|
if target_idx <= current_idx:
|
|
864
866
|
click.echo(f"Error: Target phase '{target_phase}' is not ahead of current phase '{current_phase}'", err=True)
|
|
@@ -866,7 +868,7 @@ def workflow_fix_phase_cmd(story_id: str, target_phase: str, dry_run: bool):
|
|
|
866
868
|
raise SystemExit(1)
|
|
867
869
|
|
|
868
870
|
# Calculate transitions
|
|
869
|
-
now = datetime.now(
|
|
871
|
+
now = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
870
872
|
click.echo("")
|
|
871
873
|
click.echo("Transitions needed:")
|
|
872
874
|
|
|
@@ -908,7 +910,7 @@ def workflow_fix_phase_cmd(story_id: str, target_phase: str, dry_run: bool):
|
|
|
908
910
|
|
|
909
911
|
# Build handoff history additions
|
|
910
912
|
handoff_lines = []
|
|
911
|
-
for
|
|
913
|
+
for _from_phase, _to_phase, from_agent, to_agent, gate in transitions:
|
|
912
914
|
handoff_lines.append(f"| {from_agent} | {to_agent} | {gate} | PASSED | {now} |")
|
|
913
915
|
|
|
914
916
|
# Insert handoff rows after the last PASSED/FAILED row
|
|
@@ -950,7 +952,7 @@ def workflow_complete_step_cmd(name: str | None, step_override: int | None):
|
|
|
950
952
|
NAME - Workflow name (auto-detects from session if omitted)
|
|
951
953
|
"""
|
|
952
954
|
import re
|
|
953
|
-
from datetime import datetime
|
|
955
|
+
from datetime import datetime
|
|
954
956
|
|
|
955
957
|
from pennyfarthing_scripts.common.config import get_project_root
|
|
956
958
|
from pennyfarthing_scripts.workflow.helpers import (
|
|
@@ -1035,7 +1037,7 @@ def workflow_complete_step_cmd(name: str | None, step_override: int | None):
|
|
|
1035
1037
|
new_status = "completed" if completed_count >= step_count else "in_progress"
|
|
1036
1038
|
|
|
1037
1039
|
# Update session file
|
|
1038
|
-
now = datetime.now(
|
|
1040
|
+
now = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
1039
1041
|
|
|
1040
1042
|
content = re.sub(
|
|
1041
1043
|
r"^- \*\*Current Step:\*\*.*$",
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Phase-scoped Team Lifecycle for Story 86-10.
|
|
3
|
+
|
|
4
|
+
Manages the lifecycle of native Agent Teams within workflow phases.
|
|
5
|
+
When a workflow phase has a `team:` config block, the lead agent creates
|
|
6
|
+
a team at phase start, spawns teammates, and cleans up before handoff.
|
|
7
|
+
|
|
8
|
+
Python port of packages/core/dist/workflow/team-lifecycle.js.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from datetime import UTC, datetime
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
# =============================================================================
|
|
18
|
+
# In-memory registries
|
|
19
|
+
# =============================================================================
|
|
20
|
+
|
|
21
|
+
_active_teams: dict[str, dict[str, Any]] = {}
|
|
22
|
+
"""Active teams keyed by story_id."""
|
|
23
|
+
|
|
24
|
+
_sidecar_locks: dict[str, dict[str, Any]] = {}
|
|
25
|
+
"""Sidecar locks keyed by file path."""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _reset_for_testing() -> None:
|
|
29
|
+
"""Reset all in-memory state. For testing only."""
|
|
30
|
+
_active_teams.clear()
|
|
31
|
+
_sidecar_locks.clear()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
async def create_team(
|
|
35
|
+
phase: dict[str, Any],
|
|
36
|
+
story_id: str,
|
|
37
|
+
adapter: Any | None = None,
|
|
38
|
+
) -> dict[str, Any]:
|
|
39
|
+
"""Create a phase-scoped team.
|
|
40
|
+
|
|
41
|
+
If phase has no team config, returns success with no handle (no-op).
|
|
42
|
+
If phase has team config, creates team and returns handle.
|
|
43
|
+
"""
|
|
44
|
+
if not phase.get("team"):
|
|
45
|
+
return {"success": True}
|
|
46
|
+
|
|
47
|
+
team_name = f"{story_id}-{phase['name']}"
|
|
48
|
+
|
|
49
|
+
# Clean up existing team for same story
|
|
50
|
+
existing = _active_teams.get(story_id)
|
|
51
|
+
if existing and adapter:
|
|
52
|
+
try:
|
|
53
|
+
await adapter.delete_team(existing["teamName"])
|
|
54
|
+
except Exception:
|
|
55
|
+
pass
|
|
56
|
+
del _active_teams[story_id]
|
|
57
|
+
|
|
58
|
+
handle: dict[str, Any] = {
|
|
59
|
+
"teamName": team_name,
|
|
60
|
+
"storyId": story_id,
|
|
61
|
+
"phase": phase["name"],
|
|
62
|
+
"teammates": [],
|
|
63
|
+
"createdAt": datetime.now(UTC).isoformat(),
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if adapter:
|
|
67
|
+
try:
|
|
68
|
+
await adapter.create_team({"teamName": team_name})
|
|
69
|
+
except Exception as exc:
|
|
70
|
+
return {"success": False, "error": str(exc)}
|
|
71
|
+
|
|
72
|
+
_active_teams[story_id] = handle
|
|
73
|
+
return {"success": True, "data": handle}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
async def spawn_teammates(
|
|
77
|
+
handle: dict[str, Any],
|
|
78
|
+
config: dict[str, Any],
|
|
79
|
+
story_id: str,
|
|
80
|
+
phase: dict[str, Any],
|
|
81
|
+
adapter: Any | None = None,
|
|
82
|
+
) -> dict[str, Any]:
|
|
83
|
+
"""Spawn all teammates for a team based on workflow YAML config."""
|
|
84
|
+
teammates: list[dict[str, Any]] = []
|
|
85
|
+
|
|
86
|
+
for member in config["teammates"]:
|
|
87
|
+
teammate: dict[str, Any] = {
|
|
88
|
+
"agent": member["agent"],
|
|
89
|
+
"task": member["task"],
|
|
90
|
+
"status": "spawned",
|
|
91
|
+
}
|
|
92
|
+
if adapter:
|
|
93
|
+
try:
|
|
94
|
+
await adapter.spawn_teammate({
|
|
95
|
+
"teamName": handle["teamName"],
|
|
96
|
+
"agent": member["agent"],
|
|
97
|
+
"prompt": f'pf agent start "{member["agent"]}"',
|
|
98
|
+
"model": config.get("model"),
|
|
99
|
+
})
|
|
100
|
+
except Exception:
|
|
101
|
+
teammate["status"] = "crashed"
|
|
102
|
+
teammates.append(teammate)
|
|
103
|
+
|
|
104
|
+
handle["teammates"] = teammates
|
|
105
|
+
|
|
106
|
+
# Update registry
|
|
107
|
+
if handle.get("storyId") in _active_teams:
|
|
108
|
+
_active_teams[handle["storyId"]] = handle
|
|
109
|
+
|
|
110
|
+
return {"success": True, "data": teammates}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
async def shutdown_all_teammates(
|
|
114
|
+
handle: dict[str, Any],
|
|
115
|
+
adapter: Any | None = None,
|
|
116
|
+
) -> dict[str, Any]:
|
|
117
|
+
"""Shut down all active teammates in a team."""
|
|
118
|
+
shutdown_count = 0
|
|
119
|
+
|
|
120
|
+
for teammate in handle["teammates"]:
|
|
121
|
+
if teammate["status"] in ("shutdown", "crashed"):
|
|
122
|
+
continue
|
|
123
|
+
if adapter:
|
|
124
|
+
try:
|
|
125
|
+
await adapter.shutdown_teammate({
|
|
126
|
+
"teamName": handle["teamName"],
|
|
127
|
+
"agent": teammate["agent"],
|
|
128
|
+
})
|
|
129
|
+
except Exception:
|
|
130
|
+
pass # Graceful degradation — swallow adapter errors
|
|
131
|
+
teammate["status"] = "shutdown"
|
|
132
|
+
shutdown_count += 1
|
|
133
|
+
|
|
134
|
+
return {"success": True, "data": {"shutdownCount": shutdown_count}}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
async def cleanup_team(
|
|
138
|
+
handle: dict[str, Any],
|
|
139
|
+
adapter: Any | None = None,
|
|
140
|
+
) -> dict[str, Any]:
|
|
141
|
+
"""Clean up a team entirely (TeamDelete). Must run before pf handoff."""
|
|
142
|
+
if adapter:
|
|
143
|
+
try:
|
|
144
|
+
await adapter.delete_team(handle["teamName"])
|
|
145
|
+
except Exception:
|
|
146
|
+
pass
|
|
147
|
+
|
|
148
|
+
_active_teams.pop(handle.get("storyId", ""), None)
|
|
149
|
+
return {"success": True, "data": {"cleaned": True}}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def check_gate_on_task_completed(
|
|
153
|
+
handle: dict[str, Any],
|
|
154
|
+
phase: dict[str, Any],
|
|
155
|
+
) -> dict[str, Any]:
|
|
156
|
+
"""Check gate condition when a TaskCompleted event fires."""
|
|
157
|
+
if not phase.get("gate"):
|
|
158
|
+
return {"passed": True, "gate": "none"}
|
|
159
|
+
|
|
160
|
+
gate_type = phase["gate"].get("type", "unknown")
|
|
161
|
+
has_active = any(t["status"] == "active" for t in handle["teammates"])
|
|
162
|
+
|
|
163
|
+
if has_active:
|
|
164
|
+
return {"passed": False, "gate": gate_type, "reason": "Teammates still active"}
|
|
165
|
+
|
|
166
|
+
return {"passed": True, "gate": gate_type}
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def check_gate_on_teammate_idle(
|
|
170
|
+
handle: dict[str, Any],
|
|
171
|
+
teammate: dict[str, Any],
|
|
172
|
+
phase: dict[str, Any],
|
|
173
|
+
) -> dict[str, Any]:
|
|
174
|
+
"""Check gate condition when a TeammateIdle event fires."""
|
|
175
|
+
if not phase.get("gate"):
|
|
176
|
+
return {"passed": True, "gate": "none"}
|
|
177
|
+
|
|
178
|
+
gate_type = phase["gate"].get("type", "unknown")
|
|
179
|
+
|
|
180
|
+
if teammate["status"] == "crashed":
|
|
181
|
+
return {"passed": False, "gate": gate_type, "reason": "Teammate crashed"}
|
|
182
|
+
|
|
183
|
+
return {"passed": True, "gate": gate_type}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def generate_team_summary(handle: dict[str, Any]) -> dict[str, Any]:
|
|
187
|
+
"""Generate a team activity summary for the session file audit trail."""
|
|
188
|
+
return {
|
|
189
|
+
"teamName": handle["teamName"],
|
|
190
|
+
"storyId": handle["storyId"],
|
|
191
|
+
"phase": handle["phase"],
|
|
192
|
+
"members": [
|
|
193
|
+
{"agent": t["agent"], "status": t["status"], "task": t["task"]}
|
|
194
|
+
for t in handle["teammates"]
|
|
195
|
+
],
|
|
196
|
+
"cleanShutdown": all(t["status"] == "shutdown" for t in handle["teammates"]),
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def acquire_sidecar_lock(
|
|
201
|
+
file_path: str,
|
|
202
|
+
story_id: str,
|
|
203
|
+
timeout: int = 5000,
|
|
204
|
+
) -> dict[str, Any]:
|
|
205
|
+
"""Acquire an exclusive lock for sidecar file writing."""
|
|
206
|
+
existing = _sidecar_locks.get(file_path)
|
|
207
|
+
if existing:
|
|
208
|
+
if existing["storyId"] == story_id:
|
|
209
|
+
return {"success": True, "data": existing}
|
|
210
|
+
return {"success": False, "error": f"Lock held by story {existing['storyId']}"}
|
|
211
|
+
|
|
212
|
+
lock: dict[str, Any] = {
|
|
213
|
+
"lockPath": f"{file_path}.lock",
|
|
214
|
+
"storyId": story_id,
|
|
215
|
+
"acquiredAt": datetime.now(UTC).isoformat(),
|
|
216
|
+
}
|
|
217
|
+
_sidecar_locks[file_path] = lock
|
|
218
|
+
return {"success": True, "data": lock}
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def release_sidecar_lock(lock: dict[str, Any]) -> dict[str, Any]:
|
|
222
|
+
"""Release a sidecar file lock."""
|
|
223
|
+
for path, held in list(_sidecar_locks.items()):
|
|
224
|
+
if held["lockPath"] == lock["lockPath"]:
|
|
225
|
+
del _sidecar_locks[path]
|
|
226
|
+
break
|
|
227
|
+
return {"success": True}
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def get_active_team(story_id: str) -> dict[str, Any] | None:
|
|
231
|
+
"""Get the active team for a story, if any."""
|
|
232
|
+
return _active_teams.get(story_id)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def update_session_with_summary(
|
|
236
|
+
session_path: str | Path,
|
|
237
|
+
summary: dict[str, Any],
|
|
238
|
+
) -> dict[str, Any]:
|
|
239
|
+
"""Write team activity summary to session file for audit trail."""
|
|
240
|
+
path = Path(session_path)
|
|
241
|
+
content = path.read_text() if path.exists() else ""
|
|
242
|
+
|
|
243
|
+
lines = [
|
|
244
|
+
"\n## Team Activity Summary\n",
|
|
245
|
+
f"**Team:** {summary['teamName']}",
|
|
246
|
+
f"**Phase:** {summary['phase']}",
|
|
247
|
+
f"**Clean Shutdown:** {'Yes' if summary.get('cleanShutdown') else 'No'}\n",
|
|
248
|
+
"| Agent | Status | Task |",
|
|
249
|
+
"|-------|--------|------|",
|
|
250
|
+
]
|
|
251
|
+
for member in summary.get("members", []):
|
|
252
|
+
lines.append(f"| {member['agent']} | {member['status']} | {member['task']} |")
|
|
253
|
+
|
|
254
|
+
content += "\n".join(lines) + "\n"
|
|
255
|
+
path.write_text(content)
|
|
256
|
+
return {"success": True}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Story 11-3: Cyclist Migration Tests
|
|
3
|
-
*
|
|
4
|
-
* These tests verify the Cyclist Electron GUI is correctly migrated
|
|
5
|
-
* into the pnpm workspace as @pennyfarthing/cyclist.
|
|
6
|
-
* Tests are designed to FAIL until the migration is complete.
|
|
7
|
-
*
|
|
8
|
-
* Acceptance Criteria:
|
|
9
|
-
* 1. packages/cyclist/ contains Cyclist source
|
|
10
|
-
* 2. Dependencies use workspace:* for internal packages
|
|
11
|
-
* 3. Portrait resolution uses @pennyfarthing/shared
|
|
12
|
-
* 4. Electron app starts and shows portraits
|
|
13
|
-
* 5. Dogfooding scenario works (portraits load from pennyfarthing-dist/)
|
|
14
|
-
*/
|
|
15
|
-
export {};
|
|
16
|
-
//# sourceMappingURL=cyclist-migration.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cyclist-migration.test.d.ts","sourceRoot":"","sources":["../../src/cli/cyclist-migration.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Story 11-3: Cyclist Migration Tests
|
|
3
|
-
*
|
|
4
|
-
* These tests verify the Cyclist Electron GUI is correctly migrated
|
|
5
|
-
* into the pnpm workspace as @pennyfarthing/cyclist.
|
|
6
|
-
* Tests are designed to FAIL until the migration is complete.
|
|
7
|
-
*
|
|
8
|
-
* Acceptance Criteria:
|
|
9
|
-
* 1. packages/cyclist/ contains Cyclist source
|
|
10
|
-
* 2. Dependencies use workspace:* for internal packages
|
|
11
|
-
* 3. Portrait resolution uses @pennyfarthing/shared
|
|
12
|
-
* 4. Electron app starts and shows portraits
|
|
13
|
-
* 5. Dogfooding scenario works (portraits load from pennyfarthing-dist/)
|
|
14
|
-
*/
|
|
15
|
-
import { describe, it } from 'node:test';
|
|
16
|
-
import assert from 'node:assert';
|
|
17
|
-
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
18
|
-
import { join } from 'node:path';
|
|
19
|
-
import { findMonorepoRoot } from './utils/files.js';
|
|
20
|
-
const __dirname = import.meta.dirname;
|
|
21
|
-
const PROJECT_ROOT = findMonorepoRoot(__dirname);
|
|
22
|
-
describe('Story 11-3: Cyclist Migration into Monorepo', () => {
|
|
23
|
-
describe('AC1: packages/cyclist/ contains Cyclist source', () => {
|
|
24
|
-
it('should have packages/cyclist directory', () => {
|
|
25
|
-
const cyclistDir = join(PROJECT_ROOT, 'packages', 'cyclist');
|
|
26
|
-
assert.ok(existsSync(cyclistDir), 'packages/cyclist/ directory must exist');
|
|
27
|
-
});
|
|
28
|
-
it('should have packages/cyclist/src directory', () => {
|
|
29
|
-
const srcDir = join(PROJECT_ROOT, 'packages', 'cyclist', 'src');
|
|
30
|
-
assert.ok(existsSync(srcDir), 'packages/cyclist/src/ directory must exist');
|
|
31
|
-
});
|
|
32
|
-
it('should have packages/cyclist/src/main.ts (Electron main process)', () => {
|
|
33
|
-
const mainTs = join(PROJECT_ROOT, 'packages', 'cyclist', 'src', 'main.ts');
|
|
34
|
-
assert.ok(existsSync(mainTs), 'packages/cyclist/src/main.ts must exist');
|
|
35
|
-
});
|
|
36
|
-
it('should have packages/cyclist/src/paths.ts (path resolution)', () => {
|
|
37
|
-
const pathsTs = join(PROJECT_ROOT, 'packages', 'cyclist', 'src', 'paths.ts');
|
|
38
|
-
assert.ok(existsSync(pathsTs), 'packages/cyclist/src/paths.ts must exist');
|
|
39
|
-
});
|
|
40
|
-
it('should have packages/cyclist/src/public directory', () => {
|
|
41
|
-
const publicDir = join(PROJECT_ROOT, 'packages', 'cyclist', 'src', 'public');
|
|
42
|
-
assert.ok(existsSync(publicDir), 'packages/cyclist/src/public/ directory must exist');
|
|
43
|
-
});
|
|
44
|
-
it('should have packages/cyclist/package.json', () => {
|
|
45
|
-
const packageJson = join(PROJECT_ROOT, 'packages', 'cyclist', 'package.json');
|
|
46
|
-
assert.ok(existsSync(packageJson), 'packages/cyclist/package.json must exist');
|
|
47
|
-
});
|
|
48
|
-
it('should have @pennyfarthing/cyclist as package name', () => {
|
|
49
|
-
const packageJsonPath = join(PROJECT_ROOT, 'packages', 'cyclist', 'package.json');
|
|
50
|
-
if (!existsSync(packageJsonPath)) {
|
|
51
|
-
assert.fail('packages/cyclist/package.json must exist first');
|
|
52
|
-
}
|
|
53
|
-
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
54
|
-
assert.strictEqual(pkg.name, '@pennyfarthing/cyclist', 'Package name must be @pennyfarthing/cyclist');
|
|
55
|
-
});
|
|
56
|
-
it('should have packages/cyclist/tsconfig.json', () => {
|
|
57
|
-
const tsconfig = join(PROJECT_ROOT, 'packages', 'cyclist', 'tsconfig.json');
|
|
58
|
-
assert.ok(existsSync(tsconfig), 'packages/cyclist/tsconfig.json must exist');
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
describe('AC2: Dependencies use workspace:* for internal packages', () => {
|
|
62
|
-
it('should have @pennyfarthing/shared as workspace dependency', () => {
|
|
63
|
-
const packageJsonPath = join(PROJECT_ROOT, 'packages', 'cyclist', 'package.json');
|
|
64
|
-
if (!existsSync(packageJsonPath)) {
|
|
65
|
-
assert.fail('packages/cyclist/package.json must exist first');
|
|
66
|
-
}
|
|
67
|
-
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
68
|
-
const deps = pkg.dependencies || {};
|
|
69
|
-
assert.ok(deps['@pennyfarthing/shared'], 'packages/cyclist should depend on @pennyfarthing/shared');
|
|
70
|
-
// Dependency can use workspace:* or a version range
|
|
71
|
-
assert.ok(deps['@pennyfarthing/shared'].includes('workspace') || /^\^?\d/.test(deps['@pennyfarthing/shared']), '@pennyfarthing/shared dependency should use workspace protocol or version range');
|
|
72
|
-
});
|
|
73
|
-
it('should NOT have pennyfarthing GitHub dependency', () => {
|
|
74
|
-
const packageJsonPath = join(PROJECT_ROOT, 'packages', 'cyclist', 'package.json');
|
|
75
|
-
if (!existsSync(packageJsonPath)) {
|
|
76
|
-
assert.fail('packages/cyclist/package.json must exist first');
|
|
77
|
-
}
|
|
78
|
-
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
79
|
-
const deps = pkg.dependencies || {};
|
|
80
|
-
assert.ok(!deps['pennyfarthing'] || !deps['pennyfarthing'].includes('github'), 'packages/cyclist should NOT have GitHub pennyfarthing dependency (use workspace instead)');
|
|
81
|
-
});
|
|
82
|
-
it('should be listed in pnpm-workspace.yaml packages', () => {
|
|
83
|
-
const workspaceYamlPath = join(PROJECT_ROOT, 'pnpm-workspace.yaml');
|
|
84
|
-
const content = readFileSync(workspaceYamlPath, 'utf-8');
|
|
85
|
-
// packages/* pattern should include packages/cyclist
|
|
86
|
-
assert.ok(content.includes('packages/*') || content.includes('packages/cyclist'), 'pnpm-workspace.yaml should include packages/cyclist (via packages/* pattern)');
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
describe('AC3: Portrait resolution uses shared resolver pattern', () => {
|
|
90
|
-
// Note: paths.ts inlines the shared resolver functions for standalone npm distribution
|
|
91
|
-
// This is valid - we test for the functions existing, not the import pattern
|
|
92
|
-
it('should have resolvePennyfarthingDist function', () => {
|
|
93
|
-
const pathsPath = join(PROJECT_ROOT, 'packages', 'cyclist', 'src', 'paths.ts');
|
|
94
|
-
if (!existsSync(pathsPath)) {
|
|
95
|
-
assert.fail('packages/cyclist/src/paths.ts must exist first');
|
|
96
|
-
}
|
|
97
|
-
const content = readFileSync(pathsPath, 'utf-8');
|
|
98
|
-
assert.ok(content.includes('resolvePennyfarthingDist'), 'paths.ts should have resolvePennyfarthingDist function (inlined or imported)');
|
|
99
|
-
});
|
|
100
|
-
it('should have getPortraitPaths function', () => {
|
|
101
|
-
const pathsPath = join(PROJECT_ROOT, 'packages', 'cyclist', 'src', 'paths.ts');
|
|
102
|
-
if (!existsSync(pathsPath)) {
|
|
103
|
-
assert.fail('packages/cyclist/src/paths.ts must exist first');
|
|
104
|
-
}
|
|
105
|
-
const content = readFileSync(pathsPath, 'utf-8');
|
|
106
|
-
assert.ok(content.includes('getPortraitPaths'), 'paths.ts should have getPortraitPaths function (inlined or imported)');
|
|
107
|
-
});
|
|
108
|
-
it('should NOT have hardcoded pennyfarthing path in getPortraitsDir', () => {
|
|
109
|
-
const pathsPath = join(PROJECT_ROOT, 'packages', 'cyclist', 'src', 'paths.ts');
|
|
110
|
-
if (!existsSync(pathsPath)) {
|
|
111
|
-
assert.fail('packages/cyclist/src/paths.ts must exist first');
|
|
112
|
-
}
|
|
113
|
-
const content = readFileSync(pathsPath, 'utf-8');
|
|
114
|
-
// Should not contain the old hardcoded path logic (join calls, not comments)
|
|
115
|
-
// Old pattern was: join(..., 'pennyfarthing', 'pennyfarthing-dist')
|
|
116
|
-
const hasOldPattern = content.includes("join(__dirname, 'pennyfarthing', 'pennyfarthing-dist')") ||
|
|
117
|
-
content.includes('join(__dirname, "pennyfarthing", "pennyfarthing-dist")');
|
|
118
|
-
assert.ok(!hasOldPattern, 'paths.ts should NOT have hardcoded pennyfarthing path - use shared resolver instead');
|
|
119
|
-
});
|
|
120
|
-
it('should depend on @pennyfarthing/shared in package.json', () => {
|
|
121
|
-
const packageJsonPath = join(PROJECT_ROOT, 'packages', 'cyclist', 'package.json');
|
|
122
|
-
if (!existsSync(packageJsonPath)) {
|
|
123
|
-
assert.fail('packages/cyclist/package.json must exist first');
|
|
124
|
-
}
|
|
125
|
-
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
126
|
-
const deps = pkg.dependencies || {};
|
|
127
|
-
assert.ok(deps['@pennyfarthing/shared'], 'packages/cyclist should depend on @pennyfarthing/shared');
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
describe('AC4: Electron app starts and shows portraits', () => {
|
|
131
|
-
// Note: Full build verification requires pnpm install which needs workspace resolution
|
|
132
|
-
// These tests verify the package configuration is correct for building
|
|
133
|
-
it('should have Electron dependency', () => {
|
|
134
|
-
const packageJsonPath = join(PROJECT_ROOT, 'packages', 'cyclist', 'package.json');
|
|
135
|
-
if (!existsSync(packageJsonPath)) {
|
|
136
|
-
assert.fail('packages/cyclist/package.json must exist first');
|
|
137
|
-
}
|
|
138
|
-
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
139
|
-
const devDeps = pkg.devDependencies || {};
|
|
140
|
-
assert.ok(devDeps['electron'], 'packages/cyclist should have electron as devDependency');
|
|
141
|
-
});
|
|
142
|
-
it('should have build script in package.json', () => {
|
|
143
|
-
const packageJsonPath = join(PROJECT_ROOT, 'packages', 'cyclist', 'package.json');
|
|
144
|
-
if (!existsSync(packageJsonPath)) {
|
|
145
|
-
assert.fail('packages/cyclist/package.json must exist first');
|
|
146
|
-
}
|
|
147
|
-
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
148
|
-
assert.ok(pkg.scripts?.build, 'packages/cyclist should have a build script');
|
|
149
|
-
});
|
|
150
|
-
it('should have main entry point configured', () => {
|
|
151
|
-
const packageJsonPath = join(PROJECT_ROOT, 'packages', 'cyclist', 'package.json');
|
|
152
|
-
if (!existsSync(packageJsonPath)) {
|
|
153
|
-
assert.fail('packages/cyclist/package.json must exist first');
|
|
154
|
-
}
|
|
155
|
-
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
156
|
-
assert.ok(pkg.main, 'packages/cyclist should have a main entry point');
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
describe('AC5: Dogfooding scenario works', () => {
|
|
160
|
-
it('should resolve portraits from pennyfarthing-dist in monorepo', () => {
|
|
161
|
-
// This test validates that portrait resolution works in the monorepo context
|
|
162
|
-
const pennyfarthingDist = join(PROJECT_ROOT, 'pennyfarthing-dist');
|
|
163
|
-
assert.ok(existsSync(pennyfarthingDist), 'pennyfarthing-dist/ should exist at monorepo root for dogfooding');
|
|
164
|
-
});
|
|
165
|
-
it('should have portraits directory in pennyfarthing-dist', () => {
|
|
166
|
-
const portraitsDir = join(PROJECT_ROOT, 'pennyfarthing-dist', 'personas', 'portraits');
|
|
167
|
-
assert.ok(existsSync(portraitsDir), 'pennyfarthing-dist/personas/portraits/ should exist');
|
|
168
|
-
});
|
|
169
|
-
it('should have at least one theme with portraits', () => {
|
|
170
|
-
const portraitsDir = join(PROJECT_ROOT, 'pennyfarthing-dist', 'personas', 'portraits');
|
|
171
|
-
if (!existsSync(portraitsDir)) {
|
|
172
|
-
assert.fail('portraits directory must exist first');
|
|
173
|
-
}
|
|
174
|
-
const themes = readdirSync(portraitsDir);
|
|
175
|
-
assert.ok(themes.length > 0, 'Should have at least one theme directory in portraits');
|
|
176
|
-
});
|
|
177
|
-
it('should have paths.ts configured to use shared resolver', () => {
|
|
178
|
-
// Verify paths.ts source code uses the shared resolver
|
|
179
|
-
// Runtime verification requires pnpm install + build
|
|
180
|
-
const pathsPath = join(PROJECT_ROOT, 'packages', 'cyclist', 'src', 'paths.ts');
|
|
181
|
-
if (!existsSync(pathsPath)) {
|
|
182
|
-
assert.fail('packages/cyclist/src/paths.ts must exist');
|
|
183
|
-
}
|
|
184
|
-
const content = readFileSync(pathsPath, 'utf-8');
|
|
185
|
-
assert.ok(content.includes('resolvePennyfarthingDist') && content.includes('getPortraitPaths'), 'paths.ts should use resolvePennyfarthingDist and getPortraitPaths from shared');
|
|
186
|
-
});
|
|
187
|
-
});
|
|
188
|
-
describe('Build Integration', () => {
|
|
189
|
-
// Note: Full TypeScript compilation requires pnpm install to resolve workspace dependencies
|
|
190
|
-
// These tests verify the build configuration is correct
|
|
191
|
-
it('should have valid tsconfig.json extending base', () => {
|
|
192
|
-
const tsconfigPath = join(PROJECT_ROOT, 'packages', 'cyclist', 'tsconfig.json');
|
|
193
|
-
if (!existsSync(tsconfigPath)) {
|
|
194
|
-
assert.fail('packages/cyclist/tsconfig.json must exist');
|
|
195
|
-
}
|
|
196
|
-
const content = readFileSync(tsconfigPath, 'utf-8');
|
|
197
|
-
const tsconfig = JSON.parse(content);
|
|
198
|
-
assert.ok(tsconfig.extends?.includes('tsconfig.base.json'), 'tsconfig.json should extend ../../tsconfig.base.json');
|
|
199
|
-
});
|
|
200
|
-
it('should have outDir configured for dist', () => {
|
|
201
|
-
const tsconfigPath = join(PROJECT_ROOT, 'packages', 'cyclist', 'tsconfig.json');
|
|
202
|
-
if (!existsSync(tsconfigPath)) {
|
|
203
|
-
assert.fail('packages/cyclist/tsconfig.json must exist');
|
|
204
|
-
}
|
|
205
|
-
const content = readFileSync(tsconfigPath, 'utf-8');
|
|
206
|
-
const tsconfig = JSON.parse(content);
|
|
207
|
-
assert.strictEqual(tsconfig.compilerOptions?.outDir, 'dist', 'tsconfig.json should output to dist directory');
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
describe('Source File Completeness', () => {
|
|
211
|
-
it('should have pennyfarthing.ts (persona/theme loading)', () => {
|
|
212
|
-
const pennyfarthingTs = join(PROJECT_ROOT, 'packages', 'cyclist', 'src', 'pennyfarthing.ts');
|
|
213
|
-
assert.ok(existsSync(pennyfarthingTs), 'packages/cyclist/src/pennyfarthing.ts must exist');
|
|
214
|
-
});
|
|
215
|
-
it('should have server.ts (Express server)', () => {
|
|
216
|
-
const serverTs = join(PROJECT_ROOT, 'packages', 'cyclist', 'src', 'server.ts');
|
|
217
|
-
assert.ok(existsSync(serverTs), 'packages/cyclist/src/server.ts must exist');
|
|
218
|
-
});
|
|
219
|
-
it('should have api/ directory', () => {
|
|
220
|
-
const apiDir = join(PROJECT_ROOT, 'packages', 'cyclist', 'src', 'api');
|
|
221
|
-
assert.ok(existsSync(apiDir), 'packages/cyclist/src/api/ directory must exist');
|
|
222
|
-
});
|
|
223
|
-
it('should have tests/ directory', () => {
|
|
224
|
-
const testsDir = join(PROJECT_ROOT, 'packages', 'cyclist', 'tests');
|
|
225
|
-
assert.ok(existsSync(testsDir), 'packages/cyclist/tests/ directory must exist');
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
//# sourceMappingURL=cyclist-migration.test.js.map
|