@pennyfarthing/core 11.2.0 → 11.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- 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 +381 -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 +11 -0
- package/packages/core/dist/cli/utils/settings.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/settings.js +65 -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/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/js/react/react.js +14 -14
- package/packages/core/dist/scripts/benchmark-integration.d.ts +182 -0
- package/packages/core/dist/scripts/benchmark-integration.d.ts.map +1 -0
- package/packages/core/dist/scripts/benchmark-integration.js +691 -0
- package/packages/core/dist/scripts/benchmark-integration.js.map +1 -0
- package/packages/core/dist/scripts/job-fair-aggregator.d.ts +150 -0
- package/packages/core/dist/scripts/job-fair-aggregator.d.ts.map +1 -0
- package/packages/core/dist/scripts/job-fair-aggregator.js +547 -0
- package/packages/core/dist/scripts/job-fair-aggregator.js.map +1 -0
- 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/server.d.ts +0 -3
- package/packages/core/dist/server/server.d.ts.map +1 -1
- package/packages/core/dist/server/server.js +3 -37
- 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/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/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/pennyfarthing-dist/agents/dev.md +6 -10
- package/pennyfarthing-dist/agents/reviewer.md +8 -2
- package/pennyfarthing-dist/agents/sm-finish.md +18 -1
- package/pennyfarthing-dist/commands/pf-git.md +4 -2
- 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 +23 -19
- package/pennyfarthing-dist/guides/bell-mode.md +1 -1
- package/pennyfarthing-dist/guides/hooks.md +28 -28
- package/pennyfarthing-dist/guides/reflector.md +1 -1
- package/pennyfarthing-dist/guides/tandem-protocol.md +3 -3
- package/pennyfarthing-dist/scripts/core/check-context.sh +2 -0
- package/pennyfarthing-dist/scripts/core/phase-check-start.sh +5 -87
- package/pennyfarthing-dist/scripts/hooks/README.md +5 -5
- package/pennyfarthing-dist/scripts/hooks/__pycache__/question_reflector_check.cpython-314.pyc +0 -0
- 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/pre-commit.sh +27 -33
- 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 +4 -93
- package/pennyfarthing-dist/scripts/misc/README.md +1 -1
- package/pennyfarthing-dist/scripts/misc/statusline.sh +4 -301
- package/pennyfarthing-dist/templates/settings.local.json.template +19 -10
- package/pennyfarthing-dist/workflows/tdd.yaml +11 -2
- package/pennyfarthing_scripts/CLAUDE.md +19 -10
- package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/bellmode_hook.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/config.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/context.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/hooks.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_bidirectional_sync.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_epic_creation.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_sync.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_sync_story.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/output.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/patch_mode.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/pretooluse_hook.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/schema_validation_hook.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/session_start_hook.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/sprint.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/workflow.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/__pycache__/focus.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bellmode_hook.py +12 -296
- package/pennyfarthing_scripts/bikerack/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/audit_log_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/background_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/changed_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/cli.cpython-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 +119 -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/portrait_resolver.py +139 -0
- package/pennyfarthing_scripts/bikerack/sprint_panel.py +373 -142
- package/pennyfarthing_scripts/bikerack/story_detail_data.py +244 -0
- package/pennyfarthing_scripts/bikerack/story_detail_screen.py +176 -0
- package/pennyfarthing_scripts/bikerack/tui.py +293 -61
- package/pennyfarthing_scripts/brownfield/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/discover.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/cli.py +5 -0
- package/pennyfarthing_scripts/codemarkers/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/codemarkers/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/codemarkers/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/codemarkers/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/codemarkers/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/codemarkers/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/output.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/themes.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/pr_config.py +38 -0
- package/pennyfarthing_scripts/complexity/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/complexity/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/complexity/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/complexity/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/complexity/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/complexity/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/consultation/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/consultation/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/consultation/__pycache__/dialogue_manager.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/deadcode/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/deadcode/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/deadcode/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/deadcode/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/deadcode/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/deadcode/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/dependencies/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/dependencies/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/dependencies/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/dependencies/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/dependencies/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/dependencies/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/epic/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/epic/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git_group/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git_group/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/complete_phase.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/gate_file.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/gate_runner.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/marker.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/cli.py +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/healthscore/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__init__.py +437 -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 +215 -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 +78 -0
- package/pennyfarthing_scripts/hooks/reflector_check.py +271 -0
- package/pennyfarthing_scripts/hooks/schema_validation.py +203 -0
- package/pennyfarthing_scripts/hooks/session_start.py +296 -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 +420 -0
- package/pennyfarthing_scripts/hooks.py +27 -432
- package/pennyfarthing_scripts/hotspots/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hotspots/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hotspots/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hotspots/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hotspots/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hotspots/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/compat.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/create.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/mappings.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/operations.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/reconcile.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/story.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/launch/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/launch/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/session.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/skill.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/step.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/validate.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/finish.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/pretooluse_hook.py +3 -185
- package/pennyfarthing_scripts/prime/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/loader.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/session.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/tiers.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/version_sentinel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/workflow.py +2 -1
- package/pennyfarthing_scripts/schema_validation_hook.py +3 -298
- package/pennyfarthing_scripts/session/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/session/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/session_start_hook.py +4 -186
- package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/archive_epic.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/epic_update.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/import_epic.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/status.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/story_add.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/validate_cmd.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/yaml_io.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/story_update.py +19 -0
- package/pennyfarthing_scripts/story/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/create.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/size.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/template.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_108_1_gate_migration.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_archive_epic.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_bc.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_bikerack.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_brownfield.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_cli_modules.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_cli_normalization.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_codemarkers.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_common.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_confidence_sm_evaluation.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_confidence_sm_gate.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_dialogue_manager.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_epic_shard_validation.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_git_utils.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_handoff_cli.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_handoff_e2e.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_healthscore.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_jira_package.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_package_structure.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_patch_mode.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_prime.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_sprint_package.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_sprint_panel.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_story_add.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_story_package.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_story_update.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_tiers.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_token_counting.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_topology_loader.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_tui_focus.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_tui_panel_persistence.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_validate_cmd.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_version_sentinel.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_workflow_cli.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_yaml_io.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/test_sprint_panel.py +344 -265
- package/pennyfarthing_scripts/theme/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/theme/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/agent.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/schema.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/skill_command.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/sprint.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/tandem_awareness.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/workflow.cpython-314.pyc +0 -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/cli.py +7 -6
- package/pennyfarthing_scripts/workflow/team_lifecycle.py +257 -0
- package/packages/core/dist/scripts/theme-detail.test.d.ts +0 -10
- package/packages/core/dist/scripts/theme-detail.test.js +0 -199
- package/pennyfarthing_scripts/bikerack/__pycache__/portrait.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/gate/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/gate/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/gate/__pycache__/validate.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/hooks_installer.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/repos.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/worktree.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/heatmap.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_108_2_remove_handoff_fallback.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_gate_file_resolution.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_gate_runner.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_resolve_gate_file_field.cpython-314-pytest-9.0.2.pyc +0 -0
|
@@ -1,192 +1,10 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
1
|
"""
|
|
3
|
-
|
|
2
|
+
Backward-compatibility shim — pretooluse hook moved to hooks/cyclist_pretooluse.py.
|
|
4
3
|
|
|
5
|
-
This
|
|
6
|
-
It communicates with WheelHub (Cyclist's central coordination server)
|
|
7
|
-
via HTTP to get approval decisions.
|
|
8
|
-
|
|
9
|
-
Flow:
|
|
10
|
-
1. Claude Code calls this script with tool info via stdin (JSON)
|
|
11
|
-
2. Script reads port from .wheelhub-port in project directory
|
|
12
|
-
3. Script sends request to WheelHub's /api/hook-request endpoint
|
|
13
|
-
4. WheelHub shows approval modal, user decides
|
|
14
|
-
5. Script receives response, outputs JSON decision to stdout
|
|
15
|
-
6. Claude Code proceeds or blocks based on decision
|
|
16
|
-
|
|
17
|
-
Per ADR-0004: All communication converges through WheelHub.
|
|
18
|
-
|
|
19
|
-
Story: MSSCI-12409 - Hook consistency and WheelHub consolidation
|
|
20
|
-
|
|
21
|
-
Usage:
|
|
22
|
-
Install in ~/.claude/settings.json or project .claude/settings.json:
|
|
23
|
-
{
|
|
24
|
-
"hooks": {
|
|
25
|
-
"PreToolUse": [{
|
|
26
|
-
"matcher": "Bash",
|
|
27
|
-
"hooks": [{
|
|
28
|
-
"type": "command",
|
|
29
|
-
"command": "python3 /path/to/pretooluse_hook.py"
|
|
30
|
-
}]
|
|
31
|
-
}]
|
|
32
|
-
}
|
|
33
|
-
}
|
|
4
|
+
This file will be removed in a future version.
|
|
34
5
|
"""
|
|
35
6
|
|
|
36
|
-
import
|
|
37
|
-
from pathlib import Path
|
|
38
|
-
|
|
39
|
-
# Add parent directory to path for imports
|
|
40
|
-
sys.path.insert(0, str(Path(__file__).parent))
|
|
41
|
-
|
|
42
|
-
from hooks import (
|
|
43
|
-
HookResponse,
|
|
44
|
-
find_project_root,
|
|
45
|
-
get_context_state,
|
|
46
|
-
is_cyclist_running,
|
|
47
|
-
load_settings,
|
|
48
|
-
output_hook_response,
|
|
49
|
-
read_stdin_json,
|
|
50
|
-
send_to_cyclist,
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def _resolve_agent(session_id: str | None, project_root: Path | None) -> str | None:
|
|
55
|
-
"""Resolve agent name from session file.
|
|
56
|
-
|
|
57
|
-
Looks up .session/agents/{session_id} to find the active agent name.
|
|
58
|
-
Falls back to the most recently modified agent file if session_id
|
|
59
|
-
doesn't match.
|
|
60
|
-
|
|
61
|
-
Args:
|
|
62
|
-
session_id: Claude Code session ID
|
|
63
|
-
project_root: Project root directory
|
|
64
|
-
|
|
65
|
-
Returns:
|
|
66
|
-
Agent name string, or None if not found
|
|
67
|
-
"""
|
|
68
|
-
if not project_root:
|
|
69
|
-
return None
|
|
70
|
-
|
|
71
|
-
agents_dir = project_root / ".session" / "agents"
|
|
72
|
-
if not agents_dir.is_dir():
|
|
73
|
-
return None
|
|
74
|
-
|
|
75
|
-
# Try exact session_id match first
|
|
76
|
-
if session_id:
|
|
77
|
-
agent_file = agents_dir / session_id
|
|
78
|
-
if agent_file.is_file():
|
|
79
|
-
try:
|
|
80
|
-
return agent_file.read_text().strip() or None
|
|
81
|
-
except OSError:
|
|
82
|
-
pass
|
|
83
|
-
|
|
84
|
-
# Fallback: most recently modified agent file
|
|
85
|
-
try:
|
|
86
|
-
agent_files = sorted(
|
|
87
|
-
(f for f in agents_dir.iterdir() if f.is_file()),
|
|
88
|
-
key=lambda f: f.stat().st_mtime,
|
|
89
|
-
reverse=True,
|
|
90
|
-
)
|
|
91
|
-
if agent_files:
|
|
92
|
-
return agent_files[0].read_text().strip() or None
|
|
93
|
-
except OSError:
|
|
94
|
-
pass
|
|
95
|
-
|
|
96
|
-
return None
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
def main() -> None:
|
|
100
|
-
"""Main entry point for PreToolUse hook."""
|
|
101
|
-
try:
|
|
102
|
-
# Read tool data from Claude Code
|
|
103
|
-
tool_data = read_stdin_json()
|
|
104
|
-
|
|
105
|
-
# Extract relevant fields
|
|
106
|
-
tool_name = tool_data.get("tool_name", "")
|
|
107
|
-
tool_id = tool_data.get("tool_use_id", "")
|
|
108
|
-
tool_input = tool_data.get("tool_input", {})
|
|
109
|
-
session_id = tool_data.get("session_id")
|
|
110
|
-
|
|
111
|
-
# Find project root
|
|
112
|
-
project_root = find_project_root()
|
|
113
|
-
|
|
114
|
-
# Resolve agent name from session file (MSSCI-14392)
|
|
115
|
-
agent_name = _resolve_agent(session_id, project_root)
|
|
116
|
-
|
|
117
|
-
# Check if Cyclist is running
|
|
118
|
-
if not is_cyclist_running(project_root):
|
|
119
|
-
# No Cyclist - pass through to Claude Code's built-in permissions
|
|
120
|
-
# Using "allow" so the hook doesn't override Claude Code's own
|
|
121
|
-
# permission system (settings.json allow lists still apply)
|
|
122
|
-
sys.exit(0)
|
|
123
|
-
|
|
124
|
-
# Load settings to check for auto-approval mode
|
|
125
|
-
settings = load_settings(project_root)
|
|
126
|
-
if settings.permission_mode == "accept":
|
|
127
|
-
# Auto-accept mode - approve everything
|
|
128
|
-
output_hook_response(HookResponse(
|
|
129
|
-
event_name="PreToolUse",
|
|
130
|
-
decision="allow",
|
|
131
|
-
reason="Auto-accept mode enabled",
|
|
132
|
-
))
|
|
133
|
-
sys.exit(0)
|
|
134
|
-
|
|
135
|
-
# Get context state for inclusion in request
|
|
136
|
-
context = get_context_state(project_root)
|
|
137
|
-
|
|
138
|
-
# Build request data
|
|
139
|
-
request_data = {
|
|
140
|
-
"toolName": tool_name,
|
|
141
|
-
"toolId": tool_id,
|
|
142
|
-
"input": tool_input,
|
|
143
|
-
"sessionId": session_id,
|
|
144
|
-
"context": {
|
|
145
|
-
"percentage": context.percentage,
|
|
146
|
-
"isHigh": context.is_high,
|
|
147
|
-
"isCritical": context.is_critical,
|
|
148
|
-
},
|
|
149
|
-
}
|
|
150
|
-
# Include agent identity if resolved (MSSCI-14392)
|
|
151
|
-
if agent_name:
|
|
152
|
-
request_data["agent"] = agent_name
|
|
153
|
-
|
|
154
|
-
# Send approval request to WheelHub with context info
|
|
155
|
-
response = send_to_cyclist(
|
|
156
|
-
endpoint="/api/hook-request",
|
|
157
|
-
data=request_data,
|
|
158
|
-
project_root=project_root,
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
if response is None:
|
|
162
|
-
# Connection failed - defer to Claude Code
|
|
163
|
-
output_hook_response(HookResponse(
|
|
164
|
-
event_name="PreToolUse",
|
|
165
|
-
decision="ask",
|
|
166
|
-
reason="Could not connect to WheelHub",
|
|
167
|
-
))
|
|
168
|
-
sys.exit(0)
|
|
169
|
-
|
|
170
|
-
# Extract decision from response
|
|
171
|
-
decision = response.get("decision", "ask")
|
|
172
|
-
reason = response.get("reason", "")
|
|
173
|
-
data = response.get("data")
|
|
174
|
-
|
|
175
|
-
# Output decision
|
|
176
|
-
output_hook_response(HookResponse(
|
|
177
|
-
event_name="PreToolUse",
|
|
178
|
-
decision=decision,
|
|
179
|
-
reason=reason,
|
|
180
|
-
updated_input=data,
|
|
181
|
-
))
|
|
182
|
-
sys.exit(0)
|
|
183
|
-
|
|
184
|
-
except Exception as e:
|
|
185
|
-
# On error, output to stderr and exit with code 0 (allow)
|
|
186
|
-
# We don't want hook failures to block the user
|
|
187
|
-
print(f"[pretooluse-hook] Error: {e}", file=sys.stderr)
|
|
188
|
-
sys.exit(0)
|
|
189
|
-
|
|
7
|
+
from pennyfarthing_scripts.hooks.cyclist_pretooluse import main # noqa: F401
|
|
190
8
|
|
|
191
9
|
if __name__ == "__main__":
|
|
192
10
|
main()
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -104,8 +104,9 @@ def parse_session_header(session_path: Path) -> dict[str, Any]:
|
|
|
104
104
|
|
|
105
105
|
if key == "workflow":
|
|
106
106
|
result["workflow"] = value.lower()
|
|
107
|
-
elif key
|
|
107
|
+
elif key in ("current phase", "phase"):
|
|
108
108
|
# Extract phase name, handling "(APPROVED)" suffix
|
|
109
|
+
# Matches both "**Current Phase:**" and "**Phase:**"
|
|
109
110
|
phase_match = re.match(r"(\w+)(?:\s*\(([^)]+)\))?", value)
|
|
110
111
|
if phase_match:
|
|
111
112
|
result["phase"] = phase_match.group(1).lower()
|
|
@@ -1,305 +1,10 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
1
|
"""
|
|
3
|
-
|
|
2
|
+
Backward-compatibility shim — schema validation hook moved to hooks/schema_validation.py.
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
Blocks the tool call if validation fails, providing immediate feedback.
|
|
7
|
-
|
|
8
|
-
Usage in settings.json:
|
|
9
|
-
{
|
|
10
|
-
"hooks": {
|
|
11
|
-
"PreToolUse": [{
|
|
12
|
-
"matcher": "Write|Edit",
|
|
13
|
-
"hooks": [{
|
|
14
|
-
"type": "command",
|
|
15
|
-
"command": "python3 -m pennyfarthing_scripts.schema_validation_hook"
|
|
16
|
-
}]
|
|
17
|
-
}]
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
Story: XML Schema Migration Tools
|
|
4
|
+
This file will be removed in a future version.
|
|
22
5
|
"""
|
|
23
6
|
|
|
24
|
-
import
|
|
25
|
-
import sys
|
|
26
|
-
from pathlib import Path
|
|
27
|
-
|
|
28
|
-
# Add parent directory to path for imports
|
|
29
|
-
sys.path.insert(0, str(Path(__file__).parent))
|
|
30
|
-
|
|
31
|
-
from hooks import (
|
|
32
|
-
HookResponse,
|
|
33
|
-
output_hook_response,
|
|
34
|
-
read_stdin_json,
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
# =============================================================================
|
|
38
|
-
# File Type Detection
|
|
39
|
-
# =============================================================================
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def is_session_file(file_path: str) -> bool:
|
|
43
|
-
"""Check if path is a session file."""
|
|
44
|
-
return file_path.endswith("-session.md") and ".session/" in file_path
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def is_skill_file(file_path: str) -> bool:
|
|
48
|
-
"""Check if path is a skill SKILL.md file."""
|
|
49
|
-
return file_path.endswith("/SKILL.md") and "/skills/" in file_path
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def is_step_file(file_path: str) -> bool:
|
|
53
|
-
"""Check if path is a workflow step file."""
|
|
54
|
-
path = Path(file_path)
|
|
55
|
-
return (
|
|
56
|
-
path.name.startswith("step-")
|
|
57
|
-
and path.name.endswith(".md")
|
|
58
|
-
and "/workflows/" in file_path
|
|
59
|
-
and "/steps/" in file_path
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def get_file_type(file_path: str) -> str | None:
|
|
64
|
-
"""Detect file type from path."""
|
|
65
|
-
if is_session_file(file_path):
|
|
66
|
-
return "session"
|
|
67
|
-
elif is_skill_file(file_path):
|
|
68
|
-
return "skill"
|
|
69
|
-
elif is_step_file(file_path):
|
|
70
|
-
return "step"
|
|
71
|
-
return None
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
# =============================================================================
|
|
75
|
-
# Content-Based Validation (inline to avoid import issues)
|
|
76
|
-
# =============================================================================
|
|
77
|
-
|
|
78
|
-
# Skill required tags
|
|
79
|
-
SKILL_REQUIRED_TAGS = ["run", "output"]
|
|
80
|
-
|
|
81
|
-
# Workflow step required tags
|
|
82
|
-
STEP_REQUIRED_TAGS = ["purpose", "instructions", "output"]
|
|
83
|
-
|
|
84
|
-
# Step meta required fields
|
|
85
|
-
STEP_META_FIELDS = ["step", "workflow", "agent", "next"]
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def _has_tag(content: str, tag: str) -> bool:
|
|
89
|
-
"""Check if content contains a specific XML tag."""
|
|
90
|
-
return f"<{tag}>" in content or f"<{tag} " in content
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
def validate_session_content(content: str) -> list[str]:
|
|
94
|
-
"""Validate session file content.
|
|
95
|
-
|
|
96
|
-
Args:
|
|
97
|
-
content: File content to validate
|
|
98
|
-
|
|
99
|
-
Returns:
|
|
100
|
-
List of error messages (empty if valid)
|
|
101
|
-
"""
|
|
102
|
-
errors = []
|
|
103
|
-
|
|
104
|
-
# Check for XML format indicator
|
|
105
|
-
if "<session" not in content:
|
|
106
|
-
# Old markdown format - warn but don't block during migration
|
|
107
|
-
return []
|
|
108
|
-
|
|
109
|
-
# Validate <session> root with attributes
|
|
110
|
-
if not re.search(r'<session\s+story="[^"]+"', content):
|
|
111
|
-
errors.append("Missing story attribute on <session>")
|
|
112
|
-
|
|
113
|
-
if not re.search(r'<session[^>]+workflow="[^"]+"', content):
|
|
114
|
-
errors.append("Missing workflow attribute on <session>")
|
|
115
|
-
|
|
116
|
-
# Validate <meta> section
|
|
117
|
-
if not _has_tag(content, "meta"):
|
|
118
|
-
errors.append("Missing <meta> section")
|
|
119
|
-
else:
|
|
120
|
-
if "<jira>" not in content:
|
|
121
|
-
errors.append("Missing <jira> in <meta>")
|
|
122
|
-
if "<started>" not in content:
|
|
123
|
-
errors.append("Missing <started> in <meta>")
|
|
124
|
-
|
|
125
|
-
# Validate <status> element
|
|
126
|
-
if not _has_tag(content, "status"):
|
|
127
|
-
errors.append("Missing <status> element")
|
|
128
|
-
else:
|
|
129
|
-
if 'phase="' not in content:
|
|
130
|
-
errors.append("Missing phase attribute on <status>")
|
|
131
|
-
|
|
132
|
-
return errors
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
def validate_skill_content(content: str) -> list[str]:
|
|
136
|
-
"""Validate skill file content.
|
|
137
|
-
|
|
138
|
-
Args:
|
|
139
|
-
content: File content to validate
|
|
140
|
-
|
|
141
|
-
Returns:
|
|
142
|
-
List of error messages (empty if valid)
|
|
143
|
-
"""
|
|
144
|
-
errors = []
|
|
145
|
-
|
|
146
|
-
# Check YAML frontmatter
|
|
147
|
-
if not content.startswith("---\n"):
|
|
148
|
-
errors.append("Missing YAML frontmatter")
|
|
149
|
-
else:
|
|
150
|
-
parts = content.split("---", 2)
|
|
151
|
-
if len(parts) >= 2:
|
|
152
|
-
frontmatter = parts[1]
|
|
153
|
-
if "name:" not in frontmatter:
|
|
154
|
-
errors.append("Missing 'name' in frontmatter")
|
|
155
|
-
if "description:" not in frontmatter:
|
|
156
|
-
errors.append("Missing 'description' in frontmatter")
|
|
157
|
-
|
|
158
|
-
# Check required tags
|
|
159
|
-
for tag in SKILL_REQUIRED_TAGS:
|
|
160
|
-
if not _has_tag(content, tag):
|
|
161
|
-
errors.append(f"Missing <{tag}> tag (required)")
|
|
162
|
-
|
|
163
|
-
return errors
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
def validate_step_content(content: str) -> list[str]:
|
|
167
|
-
"""Validate workflow step file content.
|
|
168
|
-
|
|
169
|
-
Args:
|
|
170
|
-
content: File content to validate
|
|
171
|
-
|
|
172
|
-
Returns:
|
|
173
|
-
List of error messages (empty if valid)
|
|
174
|
-
"""
|
|
175
|
-
errors = []
|
|
176
|
-
|
|
177
|
-
# Check required tags
|
|
178
|
-
for tag in STEP_REQUIRED_TAGS:
|
|
179
|
-
if not _has_tag(content, tag):
|
|
180
|
-
errors.append(f"Missing <{tag}> tag")
|
|
181
|
-
|
|
182
|
-
# Check step-meta fields if tag exists
|
|
183
|
-
if _has_tag(content, "step-meta"):
|
|
184
|
-
meta_match = re.search(r"<step-meta>(.+?)</step-meta>", content, re.DOTALL)
|
|
185
|
-
if meta_match:
|
|
186
|
-
meta_content = meta_match.group(1)
|
|
187
|
-
for field_name in STEP_META_FIELDS:
|
|
188
|
-
if f"{field_name}:" not in meta_content:
|
|
189
|
-
errors.append(f"Missing '{field_name}' in step-meta")
|
|
190
|
-
|
|
191
|
-
return errors
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
def validate_content(file_path: str, content: str) -> list[str]:
|
|
195
|
-
"""Validate content based on file type.
|
|
196
|
-
|
|
197
|
-
Args:
|
|
198
|
-
file_path: Path to file being written
|
|
199
|
-
content: Content being written
|
|
200
|
-
|
|
201
|
-
Returns:
|
|
202
|
-
List of error messages (empty if valid)
|
|
203
|
-
"""
|
|
204
|
-
file_type = get_file_type(file_path)
|
|
205
|
-
|
|
206
|
-
if file_type == "session":
|
|
207
|
-
return validate_session_content(content)
|
|
208
|
-
elif file_type == "skill":
|
|
209
|
-
return validate_skill_content(content)
|
|
210
|
-
elif file_type == "step":
|
|
211
|
-
return validate_step_content(content)
|
|
212
|
-
|
|
213
|
-
return []
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
# =============================================================================
|
|
217
|
-
# Hook Entry Point
|
|
218
|
-
# =============================================================================
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
def main() -> None:
|
|
222
|
-
"""Main entry point for schema validation hook."""
|
|
223
|
-
try:
|
|
224
|
-
# Read tool data from Claude Code
|
|
225
|
-
tool_data = read_stdin_json()
|
|
226
|
-
|
|
227
|
-
tool_name = tool_data.get("tool_name", "")
|
|
228
|
-
tool_input = tool_data.get("tool_input", {})
|
|
229
|
-
|
|
230
|
-
# Only process Write and Edit tools
|
|
231
|
-
if tool_name not in ("Write", "Edit"):
|
|
232
|
-
output_hook_response(HookResponse(
|
|
233
|
-
event_name="PreToolUse",
|
|
234
|
-
decision="allow",
|
|
235
|
-
reason="Not a Write/Edit operation",
|
|
236
|
-
))
|
|
237
|
-
sys.exit(0)
|
|
238
|
-
|
|
239
|
-
# Get file path
|
|
240
|
-
file_path = tool_input.get("file_path", "")
|
|
241
|
-
if not file_path:
|
|
242
|
-
output_hook_response(HookResponse(
|
|
243
|
-
event_name="PreToolUse",
|
|
244
|
-
decision="allow",
|
|
245
|
-
reason="No file path",
|
|
246
|
-
))
|
|
247
|
-
sys.exit(0)
|
|
248
|
-
|
|
249
|
-
# Check if this is a file we care about
|
|
250
|
-
file_type = get_file_type(file_path)
|
|
251
|
-
if not file_type:
|
|
252
|
-
output_hook_response(HookResponse(
|
|
253
|
-
event_name="PreToolUse",
|
|
254
|
-
decision="allow",
|
|
255
|
-
reason="Not a session/skill/step file",
|
|
256
|
-
))
|
|
257
|
-
sys.exit(0)
|
|
258
|
-
|
|
259
|
-
# Get content to validate
|
|
260
|
-
# For Write: use 'content'
|
|
261
|
-
# For Edit: we need to simulate the edit result
|
|
262
|
-
if tool_name == "Write":
|
|
263
|
-
content = tool_input.get("content", "")
|
|
264
|
-
else:
|
|
265
|
-
# Edit operation - we can't easily validate without reading the file
|
|
266
|
-
# For now, allow Edits and validate on Write only
|
|
267
|
-
output_hook_response(HookResponse(
|
|
268
|
-
event_name="PreToolUse",
|
|
269
|
-
decision="allow",
|
|
270
|
-
reason="Edit operations validated post-hoc",
|
|
271
|
-
))
|
|
272
|
-
sys.exit(0)
|
|
273
|
-
|
|
274
|
-
# Validate content
|
|
275
|
-
errors = validate_content(file_path, content)
|
|
276
|
-
|
|
277
|
-
if errors:
|
|
278
|
-
# Block the write
|
|
279
|
-
error_msg = f"Schema validation failed for {file_type} file:\n"
|
|
280
|
-
error_msg += "\n".join(f" - {e}" for e in errors)
|
|
281
|
-
error_msg += f"\n\nFile: {file_path}"
|
|
282
|
-
|
|
283
|
-
output_hook_response(HookResponse(
|
|
284
|
-
event_name="PreToolUse",
|
|
285
|
-
decision="deny",
|
|
286
|
-
reason=error_msg,
|
|
287
|
-
))
|
|
288
|
-
sys.exit(0)
|
|
289
|
-
|
|
290
|
-
# Validation passed
|
|
291
|
-
output_hook_response(HookResponse(
|
|
292
|
-
event_name="PreToolUse",
|
|
293
|
-
decision="allow",
|
|
294
|
-
reason=f"Schema validation passed for {file_type} file",
|
|
295
|
-
))
|
|
296
|
-
sys.exit(0)
|
|
297
|
-
|
|
298
|
-
except Exception as e:
|
|
299
|
-
# On error, allow to avoid blocking legitimate work
|
|
300
|
-
print(f"[schema-validation-hook] Error: {e}", file=sys.stderr)
|
|
301
|
-
sys.exit(0)
|
|
302
|
-
|
|
7
|
+
from pennyfarthing_scripts.hooks.schema_validation import main # noqa: F401
|
|
303
8
|
|
|
304
9
|
if __name__ == "__main__":
|
|
305
10
|
main()
|
|
Binary file
|
|
Binary file
|