@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
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync, writeFileSync, chmodSync, statSync, readlinkSync, symlinkSync, unlinkSync, mkdirSync, renameSync } from 'fs';
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, writeFileSync, chmodSync, statSync, readlinkSync, symlinkSync, unlinkSync, mkdirSync, renameSync, copyFileSync } from 'fs';
|
|
2
2
|
import { join, relative, dirname } from 'path';
|
|
3
3
|
import YAML from 'yaml';
|
|
4
4
|
import { spawnSync } from 'child_process';
|
|
5
5
|
import fsExtra from 'fs-extra';
|
|
6
|
-
const { removeSync, ensureDirSync } = fsExtra;
|
|
6
|
+
const { removeSync, ensureDirSync, copySync } = fsExtra;
|
|
7
7
|
import { logger } from '../utils/logger.js';
|
|
8
8
|
import { readManifest } from '../utils/manifest.js';
|
|
9
|
-
import { pathExists, isDirectory, isSymlink, fileMatchesHash } from '../utils/files.js';
|
|
10
|
-
import { getPackageVersion } from '../utils/version.js';
|
|
9
|
+
import { pathExists, isDirectory, isSymlink, fileMatchesHash, filesMatch } from '../utils/files.js';
|
|
10
|
+
import { getPackageVersion, getAssetsPath } from '../utils/version.js';
|
|
11
11
|
import { findNodeModulesPath } from '../utils/node-modules.js';
|
|
12
12
|
import { ALL_SYMLINKS, CORE_AGENTS } from '../utils/constants.js';
|
|
13
13
|
import { getPfVersion, installPfCli } from '../utils/python.js';
|
|
14
|
+
import { LEGACY_HOOK_MIGRATIONS, migrateHookPaths } from '../utils/settings.js';
|
|
14
15
|
export async function doctorCommand(options) {
|
|
15
16
|
const projectRoot = process.cwd();
|
|
16
17
|
// Handle dogfood mode - run checks for framework/orchestrator development
|
|
@@ -47,6 +48,7 @@ export async function doctorCommand(options) {
|
|
|
47
48
|
// Run checks
|
|
48
49
|
results.push(...checkInstallation(projectRoot, manifest));
|
|
49
50
|
results.push(...checkCoreFiles(projectRoot, manifest));
|
|
51
|
+
results.push(...checkCommandsAndSkills(projectRoot, nodeModulesPath));
|
|
50
52
|
results.push(...checkUserFiles(projectRoot));
|
|
51
53
|
results.push(...checkDirectories(projectRoot));
|
|
52
54
|
results.push(...checkHooks(projectRoot));
|
|
@@ -54,6 +56,7 @@ export async function doctorCommand(options) {
|
|
|
54
56
|
results.push(...checkFileLayout(projectRoot));
|
|
55
57
|
results.push(...checkLegacyFiles(projectRoot));
|
|
56
58
|
results.push(checkLegacyStatuslinePath(projectRoot));
|
|
59
|
+
results.push(checkLegacyHookCommands(projectRoot));
|
|
57
60
|
results.push(...checkCyclist(projectRoot));
|
|
58
61
|
results.push(checkPfCli(nodeModulesPath));
|
|
59
62
|
// Output results
|
|
@@ -64,7 +67,8 @@ export async function doctorCommand(options) {
|
|
|
64
67
|
// Display results by category
|
|
65
68
|
const categories = [
|
|
66
69
|
{ name: 'Installation', filter: (r) => r.name.startsWith('manifest') },
|
|
67
|
-
{ name: 'Core Files', filter: (r) => r.name.startsWith('core/') },
|
|
70
|
+
{ name: 'Core Files', filter: (r) => r.name.startsWith('core/') && r.name !== 'core/commands' && r.name !== 'core/skills' },
|
|
71
|
+
{ name: 'Commands & Skills', filter: (r) => r.name === 'core/commands' || r.name === 'core/skills' },
|
|
68
72
|
{ name: 'User Files', filter: (r) => r.name.startsWith('project/') || r.name.startsWith('persona') || r.name.startsWith('settings') },
|
|
69
73
|
{ name: 'Directories', filter: (r) => r.name.startsWith('dir/') },
|
|
70
74
|
{ name: 'Hooks', filter: (r) => r.name.startsWith('hook/') },
|
|
@@ -188,6 +192,211 @@ function checkCoreFiles(projectRoot, manifest) {
|
|
|
188
192
|
}
|
|
189
193
|
return results;
|
|
190
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Check commands and skills are properly copied (not symlinked) and up to date.
|
|
197
|
+
* Commands and skills are file copies since v11.3.0 to avoid node_modules drift.
|
|
198
|
+
*/
|
|
199
|
+
function checkCommandsAndSkills(projectRoot, _nodeModulesPath) {
|
|
200
|
+
const results = [];
|
|
201
|
+
// Use assetsPath for source resolution (correct pf-* prefix in dogfood)
|
|
202
|
+
let assetsPath = null;
|
|
203
|
+
try {
|
|
204
|
+
assetsPath = getAssetsPath();
|
|
205
|
+
}
|
|
206
|
+
catch { /* no assets available */ }
|
|
207
|
+
if (!assetsPath) {
|
|
208
|
+
// Can't check freshness without assets, but check dirs exist
|
|
209
|
+
const commandsDir = join(projectRoot, '.claude/commands');
|
|
210
|
+
const skillsDir = join(projectRoot, '.claude/skills');
|
|
211
|
+
results.push({
|
|
212
|
+
name: 'core/commands',
|
|
213
|
+
status: pathExists(commandsDir) ? 'pass' : 'fail',
|
|
214
|
+
detail: pathExists(commandsDir) ? undefined : 'Missing .claude/commands/'
|
|
215
|
+
});
|
|
216
|
+
results.push({
|
|
217
|
+
name: 'core/skills',
|
|
218
|
+
status: pathExists(skillsDir) ? 'pass' : 'fail',
|
|
219
|
+
detail: pathExists(skillsDir) ? undefined : 'Missing .claude/skills/'
|
|
220
|
+
});
|
|
221
|
+
return results;
|
|
222
|
+
}
|
|
223
|
+
// Check commands
|
|
224
|
+
const commandsDir = join(projectRoot, '.claude/commands');
|
|
225
|
+
const builtInCommandsPath = join(assetsPath, 'commands');
|
|
226
|
+
if (!pathExists(commandsDir)) {
|
|
227
|
+
results.push({
|
|
228
|
+
name: 'core/commands',
|
|
229
|
+
status: 'fail',
|
|
230
|
+
detail: 'Missing .claude/commands/ — run pennyfarthing update'
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
else if (pathExists(builtInCommandsPath)) {
|
|
234
|
+
const sourceCommands = readdirSync(builtInCommandsPath).filter(f => f.endsWith('.md') && f.startsWith('pf-'));
|
|
235
|
+
const installedEntries = readdirSync(commandsDir).filter(f => f.startsWith('pf-'));
|
|
236
|
+
let staleCount = 0;
|
|
237
|
+
let symlinkCount = 0;
|
|
238
|
+
// Check for stale symlinks (legacy) or stale copies
|
|
239
|
+
for (const cmd of sourceCommands) {
|
|
240
|
+
const installedPath = join(commandsDir, cmd);
|
|
241
|
+
const sourcePath = join(builtInCommandsPath, cmd);
|
|
242
|
+
if (!pathExists(installedPath)) {
|
|
243
|
+
staleCount++;
|
|
244
|
+
}
|
|
245
|
+
else if (isSymlink(installedPath)) {
|
|
246
|
+
symlinkCount++;
|
|
247
|
+
}
|
|
248
|
+
else if (!filesMatch(installedPath, sourcePath)) {
|
|
249
|
+
staleCount++;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (symlinkCount > 0) {
|
|
253
|
+
results.push({
|
|
254
|
+
name: 'core/commands',
|
|
255
|
+
status: 'warn',
|
|
256
|
+
detail: `${symlinkCount} command(s) are symlinks — should be copies. Run pennyfarthing update`,
|
|
257
|
+
fix: () => {
|
|
258
|
+
refreshCommandsCopy(projectRoot, builtInCommandsPath);
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
else if (staleCount > 0) {
|
|
263
|
+
results.push({
|
|
264
|
+
name: 'core/commands',
|
|
265
|
+
status: 'warn',
|
|
266
|
+
detail: `${staleCount} command(s) out of date — run pennyfarthing update`,
|
|
267
|
+
fix: () => {
|
|
268
|
+
refreshCommandsCopy(projectRoot, builtInCommandsPath);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
results.push({
|
|
274
|
+
name: 'core/commands',
|
|
275
|
+
status: 'pass',
|
|
276
|
+
detail: `${installedEntries.length} commands`
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
// Check skills
|
|
281
|
+
const skillsDir = join(projectRoot, '.claude/skills');
|
|
282
|
+
const builtInSkillsPath = join(assetsPath, 'skills');
|
|
283
|
+
if (!pathExists(skillsDir)) {
|
|
284
|
+
results.push({
|
|
285
|
+
name: 'core/skills',
|
|
286
|
+
status: 'fail',
|
|
287
|
+
detail: 'Missing .claude/skills/ — run pennyfarthing update'
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
else if (pathExists(builtInSkillsPath)) {
|
|
291
|
+
const sourceSkills = readdirSync(builtInSkillsPath).filter(f => {
|
|
292
|
+
const fullPath = join(builtInSkillsPath, f);
|
|
293
|
+
return isDirectory(fullPath) && f.startsWith('pf-');
|
|
294
|
+
});
|
|
295
|
+
const installedEntries = readdirSync(skillsDir).filter(f => f.startsWith('pf-'));
|
|
296
|
+
let missingCount = 0;
|
|
297
|
+
let symlinkCount = 0;
|
|
298
|
+
for (const skill of sourceSkills) {
|
|
299
|
+
const installedPath = join(skillsDir, skill);
|
|
300
|
+
if (!pathExists(installedPath)) {
|
|
301
|
+
missingCount++;
|
|
302
|
+
}
|
|
303
|
+
else if (isSymlink(installedPath)) {
|
|
304
|
+
symlinkCount++;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (symlinkCount > 0) {
|
|
308
|
+
results.push({
|
|
309
|
+
name: 'core/skills',
|
|
310
|
+
status: 'warn',
|
|
311
|
+
detail: `${symlinkCount} skill(s) are symlinks — should be copies. Run pennyfarthing update`,
|
|
312
|
+
fix: () => {
|
|
313
|
+
refreshSkillsCopy(projectRoot, builtInSkillsPath);
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
else if (missingCount > 0) {
|
|
318
|
+
results.push({
|
|
319
|
+
name: 'core/skills',
|
|
320
|
+
status: 'warn',
|
|
321
|
+
detail: `${missingCount} skill(s) missing — run pennyfarthing update`,
|
|
322
|
+
fix: () => {
|
|
323
|
+
refreshSkillsCopy(projectRoot, builtInSkillsPath);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
results.push({
|
|
329
|
+
name: 'core/skills',
|
|
330
|
+
status: 'pass',
|
|
331
|
+
detail: `${installedEntries.length} skills`
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return results;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Fix function: refresh commands by cleaning managed entries and copying fresh
|
|
339
|
+
*/
|
|
340
|
+
function refreshCommandsCopy(projectRoot, builtInCommandsPath) {
|
|
341
|
+
const commandsDir = join(projectRoot, '.claude/commands');
|
|
342
|
+
ensureDirSync(commandsDir);
|
|
343
|
+
// Clean all pf-* entries (symlinks or files)
|
|
344
|
+
const entries = readdirSync(commandsDir).filter(f => f.startsWith('pf-'));
|
|
345
|
+
for (const entry of entries) {
|
|
346
|
+
const entryPath = join(commandsDir, entry);
|
|
347
|
+
try {
|
|
348
|
+
unlinkSync(entryPath);
|
|
349
|
+
}
|
|
350
|
+
catch {
|
|
351
|
+
// Already gone
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
// Copy fresh from source
|
|
355
|
+
const sourceCommands = readdirSync(builtInCommandsPath).filter(f => f.endsWith('.md') && f.startsWith('pf-'));
|
|
356
|
+
for (const cmd of sourceCommands) {
|
|
357
|
+
const sourcePath = join(builtInCommandsPath, cmd);
|
|
358
|
+
const destPath = join(commandsDir, cmd);
|
|
359
|
+
try {
|
|
360
|
+
copyFileSync(sourcePath, destPath);
|
|
361
|
+
}
|
|
362
|
+
catch (e) {
|
|
363
|
+
logger.warning(`Could not copy command ${cmd}: ${e}`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Fix function: refresh skills by cleaning managed entries and copying fresh
|
|
369
|
+
*/
|
|
370
|
+
function refreshSkillsCopy(projectRoot, builtInSkillsPath) {
|
|
371
|
+
const skillsDir = join(projectRoot, '.claude/skills');
|
|
372
|
+
ensureDirSync(skillsDir);
|
|
373
|
+
// Clean all pf-* entries (symlinks or directories)
|
|
374
|
+
const entries = readdirSync(skillsDir).filter(f => f.startsWith('pf-'));
|
|
375
|
+
for (const entry of entries) {
|
|
376
|
+
const entryPath = join(skillsDir, entry);
|
|
377
|
+
try {
|
|
378
|
+
removeSync(entryPath);
|
|
379
|
+
}
|
|
380
|
+
catch {
|
|
381
|
+
// Already gone
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
// Copy fresh from source
|
|
385
|
+
const sourceSkills = readdirSync(builtInSkillsPath).filter(f => {
|
|
386
|
+
const fullPath = join(builtInSkillsPath, f);
|
|
387
|
+
return isDirectory(fullPath) && f.startsWith('pf-');
|
|
388
|
+
});
|
|
389
|
+
for (const skill of sourceSkills) {
|
|
390
|
+
const sourcePath = join(builtInSkillsPath, skill);
|
|
391
|
+
const destPath = join(skillsDir, skill);
|
|
392
|
+
try {
|
|
393
|
+
copySync(sourcePath, destPath, { overwrite: true });
|
|
394
|
+
}
|
|
395
|
+
catch (e) {
|
|
396
|
+
logger.warning(`Could not copy skill ${skill}: ${e}`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
191
400
|
/**
|
|
192
401
|
* Check symlinks for symlink installation mode
|
|
193
402
|
*/
|
|
@@ -321,6 +530,9 @@ function checkUserFiles(projectRoot) {
|
|
|
321
530
|
// Check SessionStart hooks are configured (critical for PROJECT_ROOT)
|
|
322
531
|
const hookCheck = checkSessionStartHooks(projectRoot, installationType);
|
|
323
532
|
results.push(hookCheck);
|
|
533
|
+
// Check OTEL auto-configuration (WheelHub auto-start + telemetry env vars)
|
|
534
|
+
const otelCheck = checkOtelAutoStart(projectRoot, installationType);
|
|
535
|
+
results.push(otelCheck);
|
|
324
536
|
// Check auto-load-sm hook is configured (auto-invokes /sm on new sessions)
|
|
325
537
|
const autoLoadSmCheck = checkAutoLoadSmHook(projectRoot);
|
|
326
538
|
results.push(autoLoadSmCheck);
|
|
@@ -441,11 +653,11 @@ function checkSessionStartHooks(projectRoot, installationType) {
|
|
|
441
653
|
}
|
|
442
654
|
};
|
|
443
655
|
}
|
|
444
|
-
// Check if session-start
|
|
656
|
+
// Check if session-start hook is configured (pf hooks or legacy .sh)
|
|
445
657
|
const hasSessionStartHook = settings.hooks.SessionStart.some((entry) => {
|
|
446
658
|
if (typeof entry === 'object' && entry !== null) {
|
|
447
659
|
const hookEntry = entry;
|
|
448
|
-
return hookEntry.hooks?.some(h => h.command?.includes('session-start.sh'));
|
|
660
|
+
return hookEntry.hooks?.some(h => h.command?.includes('pf.sh hooks session-start') || h.command?.includes('pf hooks session-start') || h.command?.includes('session-start.sh'));
|
|
449
661
|
}
|
|
450
662
|
return false;
|
|
451
663
|
});
|
|
@@ -453,7 +665,7 @@ function checkSessionStartHooks(projectRoot, installationType) {
|
|
|
453
665
|
return {
|
|
454
666
|
name: 'settings/session-start-hook',
|
|
455
667
|
status: 'fail',
|
|
456
|
-
detail: 'session-start
|
|
668
|
+
detail: 'session-start hook not configured - PROJECT_ROOT will be undefined',
|
|
457
669
|
fix: () => {
|
|
458
670
|
addSessionStartHooks(projectRoot, installationType);
|
|
459
671
|
}
|
|
@@ -473,6 +685,96 @@ function checkSessionStartHooks(projectRoot, installationType) {
|
|
|
473
685
|
};
|
|
474
686
|
}
|
|
475
687
|
}
|
|
688
|
+
/**
|
|
689
|
+
* Check that session-start hook uses `pf hooks session-start` (Python version)
|
|
690
|
+
* which handles WheelHub auto-start + OTEL env var configuration.
|
|
691
|
+
* Legacy .sh hooks only set 2 of 5 required OTEL vars, breaking telemetry.
|
|
692
|
+
*/
|
|
693
|
+
function checkOtelAutoStart(projectRoot, installationType) {
|
|
694
|
+
const settingsPath = join(projectRoot, '.claude/settings.local.json');
|
|
695
|
+
try {
|
|
696
|
+
const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
|
|
697
|
+
if (!settings.hooks?.SessionStart) {
|
|
698
|
+
return {
|
|
699
|
+
name: 'settings/otel-auto-start',
|
|
700
|
+
status: 'warn',
|
|
701
|
+
detail: 'No SessionStart hooks — WheelHub auto-start and OTEL not configured',
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
// Check if `pf hooks session-start` is used (Python version with full OTEL support)
|
|
705
|
+
const hasPfHooksSessionStart = settings.hooks.SessionStart.some((entry) => {
|
|
706
|
+
if (typeof entry === 'object' && entry !== null) {
|
|
707
|
+
const hookEntry = entry;
|
|
708
|
+
return hookEntry.hooks?.some(h => h.command?.includes('pf.sh hooks session-start') || h.command?.includes('pf hooks session-start'));
|
|
709
|
+
}
|
|
710
|
+
return false;
|
|
711
|
+
});
|
|
712
|
+
if (hasPfHooksSessionStart) {
|
|
713
|
+
return {
|
|
714
|
+
name: 'settings/otel-auto-start',
|
|
715
|
+
status: 'pass',
|
|
716
|
+
detail: undefined,
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
// Check if using legacy .sh (only sets 2 of 5 OTEL vars)
|
|
720
|
+
const hasLegacySh = settings.hooks.SessionStart.some((entry) => {
|
|
721
|
+
if (typeof entry === 'object' && entry !== null) {
|
|
722
|
+
const hookEntry = entry;
|
|
723
|
+
return hookEntry.hooks?.some(h => h.command?.includes('session-start.sh'));
|
|
724
|
+
}
|
|
725
|
+
return false;
|
|
726
|
+
});
|
|
727
|
+
if (hasLegacySh) {
|
|
728
|
+
return {
|
|
729
|
+
name: 'settings/otel-auto-start',
|
|
730
|
+
status: 'warn',
|
|
731
|
+
detail: 'Using legacy session-start.sh — missing WheelHub auto-start and 3 OTEL env vars. Migrate to `pf hooks session-start`',
|
|
732
|
+
fix: () => {
|
|
733
|
+
migrateSessionStartToPfHooks(projectRoot);
|
|
734
|
+
},
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
return {
|
|
738
|
+
name: 'settings/otel-auto-start',
|
|
739
|
+
status: 'warn',
|
|
740
|
+
detail: 'session-start hook not found — WheelHub auto-start and OTEL not configured',
|
|
741
|
+
fix: () => {
|
|
742
|
+
addSessionStartHooks(projectRoot, installationType);
|
|
743
|
+
},
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
catch {
|
|
747
|
+
return {
|
|
748
|
+
name: 'settings/otel-auto-start',
|
|
749
|
+
status: 'warn',
|
|
750
|
+
detail: 'Could not parse settings.local.json',
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Migrate legacy session-start.sh hooks to `pf hooks session-start`
|
|
756
|
+
*/
|
|
757
|
+
function migrateSessionStartToPfHooks(projectRoot) {
|
|
758
|
+
const settingsPath = join(projectRoot, '.claude/settings.local.json');
|
|
759
|
+
try {
|
|
760
|
+
const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
|
|
761
|
+
if (Array.isArray(settings.hooks?.SessionStart)) {
|
|
762
|
+
for (const entry of settings.hooks.SessionStart) {
|
|
763
|
+
if (typeof entry === 'object' && entry !== null && Array.isArray(entry.hooks)) {
|
|
764
|
+
for (const h of entry.hooks) {
|
|
765
|
+
if (h.command?.includes('session-start.sh')) {
|
|
766
|
+
h.command = '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks session-start';
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
|
|
773
|
+
}
|
|
774
|
+
catch {
|
|
775
|
+
// Silent fail — doctor will re-report on next run
|
|
776
|
+
}
|
|
777
|
+
}
|
|
476
778
|
/**
|
|
477
779
|
* Check that auto-load-sm hook is configured in SessionStart
|
|
478
780
|
* This auto-invokes /sm agent on new session start
|
|
@@ -619,11 +921,11 @@ function checkStopHook(projectRoot, installationType) {
|
|
|
619
921
|
}
|
|
620
922
|
};
|
|
621
923
|
}
|
|
622
|
-
// Check if
|
|
924
|
+
// Check if reflector-check hook is configured (pf hooks or legacy .sh)
|
|
623
925
|
const hasReflectorHook = settings.hooks.Stop.some((entry) => {
|
|
624
926
|
if (typeof entry === 'object' && entry !== null) {
|
|
625
927
|
const hookEntry = entry;
|
|
626
|
-
return hookEntry.hooks?.some(h => h.command?.includes('question-reflector-check'));
|
|
928
|
+
return hookEntry.hooks?.some(h => h.command?.includes('pf.sh hooks reflector-check') || h.command?.includes('pf hooks reflector-check') || h.command?.includes('question-reflector-check'));
|
|
627
929
|
}
|
|
628
930
|
return false;
|
|
629
931
|
});
|
|
@@ -631,7 +933,7 @@ function checkStopHook(projectRoot, installationType) {
|
|
|
631
933
|
return {
|
|
632
934
|
name: 'settings/stop-hook',
|
|
633
935
|
status: 'warn',
|
|
634
|
-
detail: '
|
|
936
|
+
detail: 'reflector-check hook not configured',
|
|
635
937
|
fix: () => {
|
|
636
938
|
addStopHook(projectRoot, installationType);
|
|
637
939
|
}
|
|
@@ -670,11 +972,11 @@ function checkContextCircuitBreaker(projectRoot, installationType) {
|
|
|
670
972
|
}
|
|
671
973
|
};
|
|
672
974
|
}
|
|
673
|
-
// Check if context-
|
|
975
|
+
// Check if context-breaker hook is configured (pf hooks or legacy .sh)
|
|
674
976
|
const hasCircuitBreaker = settings.hooks.PreToolUse.some((entry) => {
|
|
675
977
|
if (typeof entry === 'object' && entry !== null) {
|
|
676
978
|
const hookEntry = entry;
|
|
677
|
-
return hookEntry.hooks?.some(h => h.command?.includes('context-circuit-breaker'));
|
|
979
|
+
return hookEntry.hooks?.some(h => h.command?.includes('pf.sh hooks context-breaker') || h.command?.includes('pf hooks context-breaker') || h.command?.includes('context-circuit-breaker'));
|
|
678
980
|
}
|
|
679
981
|
return false;
|
|
680
982
|
});
|
|
@@ -682,7 +984,7 @@ function checkContextCircuitBreaker(projectRoot, installationType) {
|
|
|
682
984
|
return {
|
|
683
985
|
name: 'settings/context-circuit-breaker',
|
|
684
986
|
status: 'warn',
|
|
685
|
-
detail: 'context-
|
|
987
|
+
detail: 'context-breaker hook not configured - context exhaustion protection disabled',
|
|
686
988
|
fix: () => {
|
|
687
989
|
addContextCircuitBreaker(projectRoot, installationType);
|
|
688
990
|
}
|
|
@@ -705,15 +1007,14 @@ function checkContextCircuitBreaker(projectRoot, installationType) {
|
|
|
705
1007
|
/**
|
|
706
1008
|
* Fix function: Add context-circuit-breaker hook to PreToolUse in settings.local.json
|
|
707
1009
|
*/
|
|
708
|
-
function addContextCircuitBreaker(projectRoot,
|
|
1010
|
+
function addContextCircuitBreaker(projectRoot, _installationType) {
|
|
709
1011
|
const settingsPath = join(projectRoot, '.claude/settings.local.json');
|
|
710
|
-
const scriptBase = getScriptBasePath(installationType);
|
|
711
1012
|
const requiredHook = {
|
|
712
1013
|
matcher: 'Edit|Write|Bash|Task',
|
|
713
1014
|
hooks: [
|
|
714
1015
|
{
|
|
715
1016
|
type: 'command',
|
|
716
|
-
command:
|
|
1017
|
+
command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks context-breaker'
|
|
717
1018
|
}
|
|
718
1019
|
]
|
|
719
1020
|
};
|
|
@@ -794,15 +1095,14 @@ function checkSchemaValidationHook(projectRoot, installationType) {
|
|
|
794
1095
|
/**
|
|
795
1096
|
* Fix function: Add schema-validation hook to PreToolUse in settings.local.json
|
|
796
1097
|
*/
|
|
797
|
-
function addSchemaValidationHook(projectRoot,
|
|
1098
|
+
function addSchemaValidationHook(projectRoot, _installationType) {
|
|
798
1099
|
const settingsPath = join(projectRoot, '.claude/settings.local.json');
|
|
799
|
-
const scriptBase = getScriptBasePath(installationType);
|
|
800
1100
|
const requiredHook = {
|
|
801
1101
|
matcher: 'Write',
|
|
802
1102
|
hooks: [
|
|
803
1103
|
{
|
|
804
1104
|
type: 'command',
|
|
805
|
-
command:
|
|
1105
|
+
command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks schema-validation'
|
|
806
1106
|
}
|
|
807
1107
|
]
|
|
808
1108
|
};
|
|
@@ -848,11 +1148,11 @@ function checkPostToolUseHook(projectRoot, installationType) {
|
|
|
848
1148
|
}
|
|
849
1149
|
};
|
|
850
1150
|
}
|
|
851
|
-
// Check if bell-mode
|
|
1151
|
+
// Check if bell-mode hook is configured (pf hooks or legacy .sh)
|
|
852
1152
|
const hasBellModeHook = settings.hooks.PostToolUse.some((entry) => {
|
|
853
1153
|
if (typeof entry === 'object' && entry !== null) {
|
|
854
1154
|
const hookEntry = entry;
|
|
855
|
-
return hookEntry.hooks?.some(h => h.command?.includes('bell-mode-hook'));
|
|
1155
|
+
return hookEntry.hooks?.some(h => h.command?.includes('pf.sh hooks bell-mode') || h.command?.includes('pf hooks bell-mode') || h.command?.includes('bell-mode-hook'));
|
|
856
1156
|
}
|
|
857
1157
|
return false;
|
|
858
1158
|
});
|
|
@@ -860,7 +1160,7 @@ function checkPostToolUseHook(projectRoot, installationType) {
|
|
|
860
1160
|
return {
|
|
861
1161
|
name: 'settings/post-tool-use-hook',
|
|
862
1162
|
status: 'warn',
|
|
863
|
-
detail: 'bell-mode
|
|
1163
|
+
detail: 'bell-mode hook not configured - bell mode will not work',
|
|
864
1164
|
fix: () => {
|
|
865
1165
|
addPostToolUseHook(projectRoot, installationType);
|
|
866
1166
|
}
|
|
@@ -884,15 +1184,14 @@ function checkPostToolUseHook(projectRoot, installationType) {
|
|
|
884
1184
|
* Fix function: Add PostToolUse hook to settings.local.json
|
|
885
1185
|
* Required for bell mode to inject queued messages via additionalContext
|
|
886
1186
|
*/
|
|
887
|
-
function addPostToolUseHook(projectRoot,
|
|
1187
|
+
function addPostToolUseHook(projectRoot, _installationType) {
|
|
888
1188
|
const settingsPath = join(projectRoot, '.claude/settings.local.json');
|
|
889
|
-
const scriptBase = getScriptBasePath(installationType);
|
|
890
1189
|
const requiredHook = {
|
|
891
1190
|
matcher: '',
|
|
892
1191
|
hooks: [
|
|
893
1192
|
{
|
|
894
1193
|
type: 'command',
|
|
895
|
-
command:
|
|
1194
|
+
command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks bell-mode'
|
|
896
1195
|
}
|
|
897
1196
|
]
|
|
898
1197
|
};
|
|
@@ -938,11 +1237,11 @@ function checkSprintYamlValidationHook(projectRoot, installationType) {
|
|
|
938
1237
|
}
|
|
939
1238
|
};
|
|
940
1239
|
}
|
|
941
|
-
// Check if sprint-yaml
|
|
1240
|
+
// Check if sprint-yaml hook is configured (pf hooks or legacy .sh)
|
|
942
1241
|
const hasSprintYamlValidation = settings.hooks.PostToolUse.some((entry) => {
|
|
943
1242
|
if (typeof entry === 'object' && entry !== null) {
|
|
944
1243
|
const hookEntry = entry;
|
|
945
|
-
return hookEntry.hooks?.some(h => h.command?.includes('sprint-yaml-validation'));
|
|
1244
|
+
return hookEntry.hooks?.some(h => h.command?.includes('pf.sh hooks sprint-yaml') || h.command?.includes('pf hooks sprint-yaml') || h.command?.includes('sprint-yaml-validation'));
|
|
946
1245
|
}
|
|
947
1246
|
return false;
|
|
948
1247
|
});
|
|
@@ -950,7 +1249,7 @@ function checkSprintYamlValidationHook(projectRoot, installationType) {
|
|
|
950
1249
|
return {
|
|
951
1250
|
name: 'settings/sprint-yaml-validation',
|
|
952
1251
|
status: 'warn',
|
|
953
|
-
detail: 'sprint-yaml
|
|
1252
|
+
detail: 'sprint-yaml hook not configured - sprint YAML errors may break SprintPanel',
|
|
954
1253
|
fix: () => {
|
|
955
1254
|
addSprintYamlValidationHook(projectRoot, installationType);
|
|
956
1255
|
}
|
|
@@ -974,15 +1273,14 @@ function checkSprintYamlValidationHook(projectRoot, installationType) {
|
|
|
974
1273
|
* Fix function: Add sprint-yaml-validation hook to PostToolUse in settings.local.json
|
|
975
1274
|
* Validates sprint YAML files after Edit/Write for Cyclist SprintPanel compatibility
|
|
976
1275
|
*/
|
|
977
|
-
function addSprintYamlValidationHook(projectRoot,
|
|
1276
|
+
function addSprintYamlValidationHook(projectRoot, _installationType) {
|
|
978
1277
|
const settingsPath = join(projectRoot, '.claude/settings.local.json');
|
|
979
|
-
const scriptBase = getScriptBasePath(installationType);
|
|
980
1278
|
const requiredHook = {
|
|
981
1279
|
matcher: 'Edit|Write',
|
|
982
1280
|
hooks: [
|
|
983
1281
|
{
|
|
984
1282
|
type: 'command',
|
|
985
|
-
command:
|
|
1283
|
+
command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks sprint-yaml'
|
|
986
1284
|
}
|
|
987
1285
|
]
|
|
988
1286
|
};
|
|
@@ -1012,18 +1310,28 @@ function addSprintYamlValidationHook(projectRoot, installationType) {
|
|
|
1012
1310
|
/**
|
|
1013
1311
|
* Fix function: Add Stop hook to settings.local.json
|
|
1014
1312
|
*/
|
|
1015
|
-
function addStopHook(projectRoot,
|
|
1313
|
+
function addStopHook(projectRoot, _installationType) {
|
|
1016
1314
|
const settingsPath = join(projectRoot, '.claude/settings.local.json');
|
|
1017
|
-
const
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1315
|
+
const requiredHooks = [
|
|
1316
|
+
{
|
|
1317
|
+
matcher: '',
|
|
1318
|
+
hooks: [
|
|
1319
|
+
{
|
|
1320
|
+
type: 'command',
|
|
1321
|
+
command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks reflector-check'
|
|
1322
|
+
}
|
|
1323
|
+
]
|
|
1324
|
+
},
|
|
1325
|
+
{
|
|
1326
|
+
matcher: '',
|
|
1327
|
+
hooks: [
|
|
1328
|
+
{
|
|
1329
|
+
type: 'command',
|
|
1330
|
+
command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks session-stop'
|
|
1331
|
+
}
|
|
1332
|
+
]
|
|
1333
|
+
}
|
|
1334
|
+
];
|
|
1027
1335
|
let settings = {};
|
|
1028
1336
|
if (pathExists(settingsPath)) {
|
|
1029
1337
|
try {
|
|
@@ -1039,11 +1347,11 @@ function addStopHook(projectRoot, installationType) {
|
|
|
1039
1347
|
}
|
|
1040
1348
|
const hooks = settings.hooks;
|
|
1041
1349
|
if (!hooks.Stop) {
|
|
1042
|
-
hooks.Stop =
|
|
1350
|
+
hooks.Stop = requiredHooks;
|
|
1043
1351
|
}
|
|
1044
1352
|
else if (Array.isArray(hooks.Stop)) {
|
|
1045
|
-
// Prepend the required
|
|
1046
|
-
hooks.Stop = [
|
|
1353
|
+
// Prepend the required hooks
|
|
1354
|
+
hooks.Stop = [...requiredHooks, ...hooks.Stop];
|
|
1047
1355
|
}
|
|
1048
1356
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
|
|
1049
1357
|
}
|
|
@@ -1060,15 +1368,14 @@ function getScriptBasePath(installationType) {
|
|
|
1060
1368
|
/**
|
|
1061
1369
|
* Fix function: Add SessionStart hooks to settings.local.json
|
|
1062
1370
|
*/
|
|
1063
|
-
function addSessionStartHooks(projectRoot,
|
|
1371
|
+
function addSessionStartHooks(projectRoot, _installationType) {
|
|
1064
1372
|
const settingsPath = join(projectRoot, '.claude/settings.local.json');
|
|
1065
|
-
const scriptBase = getScriptBasePath(installationType);
|
|
1066
1373
|
const requiredHooks = [
|
|
1067
1374
|
{
|
|
1068
1375
|
hooks: [
|
|
1069
1376
|
{
|
|
1070
1377
|
type: 'command',
|
|
1071
|
-
command:
|
|
1378
|
+
command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks session-start'
|
|
1072
1379
|
}
|
|
1073
1380
|
]
|
|
1074
1381
|
},
|
|
@@ -1117,10 +1424,9 @@ function addSessionStartHooks(projectRoot, installationType) {
|
|
|
1117
1424
|
* Create settings.local.json from template
|
|
1118
1425
|
* This is the critical fix for installations that are missing this file
|
|
1119
1426
|
*/
|
|
1120
|
-
function createSettingsLocalJson(projectRoot,
|
|
1427
|
+
function createSettingsLocalJson(projectRoot, _installationType) {
|
|
1121
1428
|
const settingsPath = join(projectRoot, '.claude/settings.local.json');
|
|
1122
|
-
|
|
1123
|
-
// Create full settings structure matching the template
|
|
1429
|
+
// Create full settings structure matching the template — uses pf hooks commands
|
|
1124
1430
|
const settings = {
|
|
1125
1431
|
permissions: {
|
|
1126
1432
|
allow: [
|
|
@@ -1163,7 +1469,7 @@ function createSettingsLocalJson(projectRoot, installationType) {
|
|
|
1163
1469
|
hooks: [
|
|
1164
1470
|
{
|
|
1165
1471
|
type: 'command',
|
|
1166
|
-
command:
|
|
1472
|
+
command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks session-start'
|
|
1167
1473
|
}
|
|
1168
1474
|
]
|
|
1169
1475
|
},
|
|
@@ -1185,33 +1491,42 @@ function createSettingsLocalJson(projectRoot, installationType) {
|
|
|
1185
1491
|
]
|
|
1186
1492
|
}
|
|
1187
1493
|
],
|
|
1188
|
-
|
|
1494
|
+
Stop: [
|
|
1189
1495
|
{
|
|
1190
1496
|
matcher: '',
|
|
1191
1497
|
hooks: [
|
|
1192
1498
|
{
|
|
1193
1499
|
type: 'command',
|
|
1194
|
-
command:
|
|
1500
|
+
command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks reflector-check'
|
|
1195
1501
|
}
|
|
1196
1502
|
]
|
|
1197
1503
|
},
|
|
1198
1504
|
{
|
|
1199
|
-
matcher: '
|
|
1505
|
+
matcher: '',
|
|
1200
1506
|
hooks: [
|
|
1201
1507
|
{
|
|
1202
1508
|
type: 'command',
|
|
1203
|
-
command:
|
|
1509
|
+
command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks session-stop'
|
|
1204
1510
|
}
|
|
1205
1511
|
]
|
|
1206
1512
|
}
|
|
1207
1513
|
],
|
|
1208
|
-
|
|
1514
|
+
PostToolUse: [
|
|
1209
1515
|
{
|
|
1210
1516
|
matcher: '',
|
|
1211
1517
|
hooks: [
|
|
1212
1518
|
{
|
|
1213
1519
|
type: 'command',
|
|
1214
|
-
command:
|
|
1520
|
+
command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks bell-mode'
|
|
1521
|
+
}
|
|
1522
|
+
]
|
|
1523
|
+
},
|
|
1524
|
+
{
|
|
1525
|
+
matcher: 'Edit|Write',
|
|
1526
|
+
hooks: [
|
|
1527
|
+
{
|
|
1528
|
+
type: 'command',
|
|
1529
|
+
command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks sprint-yaml'
|
|
1215
1530
|
}
|
|
1216
1531
|
]
|
|
1217
1532
|
}
|
|
@@ -1222,7 +1537,16 @@ function createSettingsLocalJson(projectRoot, installationType) {
|
|
|
1222
1537
|
hooks: [
|
|
1223
1538
|
{
|
|
1224
1539
|
type: 'command',
|
|
1225
|
-
command:
|
|
1540
|
+
command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks pre-edit-check'
|
|
1541
|
+
}
|
|
1542
|
+
]
|
|
1543
|
+
},
|
|
1544
|
+
{
|
|
1545
|
+
matcher: 'Write',
|
|
1546
|
+
hooks: [
|
|
1547
|
+
{
|
|
1548
|
+
type: 'command',
|
|
1549
|
+
command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks schema-validation'
|
|
1226
1550
|
}
|
|
1227
1551
|
]
|
|
1228
1552
|
},
|
|
@@ -1231,7 +1555,7 @@ function createSettingsLocalJson(projectRoot, installationType) {
|
|
|
1231
1555
|
hooks: [
|
|
1232
1556
|
{
|
|
1233
1557
|
type: 'command',
|
|
1234
|
-
command:
|
|
1558
|
+
command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks context-warning'
|
|
1235
1559
|
}
|
|
1236
1560
|
]
|
|
1237
1561
|
},
|
|
@@ -1240,7 +1564,15 @@ function createSettingsLocalJson(projectRoot, installationType) {
|
|
|
1240
1564
|
hooks: [
|
|
1241
1565
|
{
|
|
1242
1566
|
type: 'command',
|
|
1243
|
-
command:
|
|
1567
|
+
command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks context-breaker'
|
|
1568
|
+
}
|
|
1569
|
+
]
|
|
1570
|
+
},
|
|
1571
|
+
{
|
|
1572
|
+
hooks: [
|
|
1573
|
+
{
|
|
1574
|
+
type: 'command',
|
|
1575
|
+
command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks cyclist-pretooluse'
|
|
1244
1576
|
}
|
|
1245
1577
|
]
|
|
1246
1578
|
}
|
|
@@ -1248,7 +1580,7 @@ function createSettingsLocalJson(projectRoot, installationType) {
|
|
|
1248
1580
|
},
|
|
1249
1581
|
statusLine: {
|
|
1250
1582
|
type: 'command',
|
|
1251
|
-
command:
|
|
1583
|
+
command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks statusline'
|
|
1252
1584
|
}
|
|
1253
1585
|
};
|
|
1254
1586
|
ensureDirSync(join(projectRoot, '.claude'));
|
|
@@ -1809,8 +2141,8 @@ export function checkLegacyStatuslinePath(projectRoot) {
|
|
|
1809
2141
|
// or plain paths like .pennyfarthing/scripts/misc/statusline.sh
|
|
1810
2142
|
const pathMatch = command.match(/(?:\"\$CLAUDE_PROJECT_DIR\"\/)?([^\s"]+)/);
|
|
1811
2143
|
const currentPath = pathMatch ? pathMatch[1] : command;
|
|
1812
|
-
// Check if it
|
|
1813
|
-
if (currentPath.includes('misc/statusline.sh') || command.includes('misc/statusline.sh')) {
|
|
2144
|
+
// Check if it's the canonical pf hooks command or the legacy .sh path
|
|
2145
|
+
if (command.includes('pf.sh hooks statusline') || command === 'pf hooks statusline' || currentPath.includes('misc/statusline.sh') || command.includes('misc/statusline.sh')) {
|
|
1814
2146
|
return {
|
|
1815
2147
|
name: 'settings/statusline-path',
|
|
1816
2148
|
status: 'pass',
|
|
@@ -1831,7 +2163,7 @@ export function checkLegacyStatuslinePath(projectRoot) {
|
|
|
1831
2163
|
const updatedSettings = { ...settings };
|
|
1832
2164
|
updatedSettings.statusLine = {
|
|
1833
2165
|
type: 'command',
|
|
1834
|
-
command:
|
|
2166
|
+
command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks statusline'
|
|
1835
2167
|
};
|
|
1836
2168
|
writeFileSync(settingsPath, JSON.stringify(updatedSettings, null, 2));
|
|
1837
2169
|
}
|
|
@@ -1845,6 +2177,82 @@ export function checkLegacyStatuslinePath(projectRoot) {
|
|
|
1845
2177
|
detail: 'Configured'
|
|
1846
2178
|
};
|
|
1847
2179
|
}
|
|
2180
|
+
/**
|
|
2181
|
+
* Check if settings.local.json contains legacy .sh hook commands that should
|
|
2182
|
+
* be migrated to `pf hooks` commands. The .sh scripts still work (they're shims)
|
|
2183
|
+
* but `pf hooks` is the canonical path — faster, no shell indirection.
|
|
2184
|
+
*/
|
|
2185
|
+
function checkLegacyHookCommands(projectRoot) {
|
|
2186
|
+
const settingsPath = join(projectRoot, '.claude/settings.local.json');
|
|
2187
|
+
if (!pathExists(settingsPath)) {
|
|
2188
|
+
return { name: 'legacy/hook-commands', status: 'pass', detail: 'No settings file' };
|
|
2189
|
+
}
|
|
2190
|
+
let settings;
|
|
2191
|
+
try {
|
|
2192
|
+
settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
|
|
2193
|
+
}
|
|
2194
|
+
catch {
|
|
2195
|
+
return { name: 'legacy/hook-commands', status: 'warn', detail: 'Cannot parse settings.local.json' };
|
|
2196
|
+
}
|
|
2197
|
+
if (!settings.hooks) {
|
|
2198
|
+
return { name: 'legacy/hook-commands', status: 'pass' };
|
|
2199
|
+
}
|
|
2200
|
+
// Count how many hook commands still reference .sh scripts
|
|
2201
|
+
const hooks = settings.hooks;
|
|
2202
|
+
let legacyCount = 0;
|
|
2203
|
+
for (const hookType of ['SessionStart', 'SessionEnd', 'PreToolUse', 'PostToolUse', 'Stop']) {
|
|
2204
|
+
if (!Array.isArray(hooks[hookType]))
|
|
2205
|
+
continue;
|
|
2206
|
+
for (const entry of hooks[hookType]) {
|
|
2207
|
+
if (!entry.hooks)
|
|
2208
|
+
continue;
|
|
2209
|
+
for (const h of entry.hooks) {
|
|
2210
|
+
if (!h.command)
|
|
2211
|
+
continue;
|
|
2212
|
+
for (const shName of Object.keys(LEGACY_HOOK_MIGRATIONS)) {
|
|
2213
|
+
if (h.command.includes(shName)) {
|
|
2214
|
+
legacyCount++;
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
// Also check statusLine
|
|
2221
|
+
const statusLine = settings.statusLine;
|
|
2222
|
+
if (statusLine?.command && !statusLine.command.includes('pf.sh hooks statusline') && statusLine.command !== 'pf hooks statusline') {
|
|
2223
|
+
for (const shName of Object.keys(LEGACY_HOOK_MIGRATIONS)) {
|
|
2224
|
+
if (statusLine.command.includes(shName)) {
|
|
2225
|
+
legacyCount++;
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
if (legacyCount === 0) {
|
|
2230
|
+
return { name: 'legacy/hook-commands', status: 'pass' };
|
|
2231
|
+
}
|
|
2232
|
+
return {
|
|
2233
|
+
name: 'legacy/hook-commands',
|
|
2234
|
+
status: 'warn',
|
|
2235
|
+
detail: `${legacyCount} hook(s) still use .sh scripts — should use pf hooks commands`,
|
|
2236
|
+
fix: () => {
|
|
2237
|
+
// Migrate all hook arrays
|
|
2238
|
+
for (const hookType of ['SessionStart', 'SessionEnd', 'PreToolUse', 'PostToolUse', 'Stop']) {
|
|
2239
|
+
if (Array.isArray(hooks[hookType])) {
|
|
2240
|
+
migrateHookPaths(hooks[hookType]);
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
// Migrate statusLine
|
|
2244
|
+
if (statusLine?.command && !statusLine.command.includes('pf.sh hooks statusline') && statusLine.command !== 'pf hooks statusline') {
|
|
2245
|
+
for (const [shName, pfCommand] of Object.entries(LEGACY_HOOK_MIGRATIONS)) {
|
|
2246
|
+
if (statusLine.command.includes(shName)) {
|
|
2247
|
+
statusLine.command = pfCommand;
|
|
2248
|
+
break;
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
|
|
2253
|
+
}
|
|
2254
|
+
};
|
|
2255
|
+
}
|
|
1848
2256
|
/**
|
|
1849
2257
|
* Check file layout — validate files are at correct .pennyfarthing/ locations.
|
|
1850
2258
|
* Flags old .claude/ locations with migration instructions.
|