@pennyfarthing/core 10.1.0 → 10.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -24
- package/package.json +3 -1
- package/packages/core/dist/cli/commands/doctor-file-layout.test.js.map +1 -1
- package/packages/core/dist/cli/commands/doctor-legacy.test.js +24 -0
- package/packages/core/dist/cli/commands/doctor-legacy.test.js.map +1 -1
- package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/doctor.js +101 -15
- package/packages/core/dist/cli/commands/doctor.js.map +1 -1
- package/packages/core/dist/cli/commands/e2e-fresh-install.test.js +2 -2
- package/packages/core/dist/cli/commands/e2e-fresh-install.test.js.map +1 -1
- package/packages/core/dist/cli/commands/e2e-upgrade.test.js +2 -2
- package/packages/core/dist/cli/commands/e2e-upgrade.test.js.map +1 -1
- package/packages/core/dist/cli/commands/hooks-consolidation.test.js +2 -2
- package/packages/core/dist/cli/commands/hooks-consolidation.test.js.map +1 -1
- package/packages/core/dist/cli/commands/init-consolidation.test.js.map +1 -1
- package/packages/core/dist/cli/commands/theme.js +1 -1
- package/packages/core/dist/cli/commands/theme.js.map +1 -1
- package/packages/core/dist/cli/commands/uninstall.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/uninstall.js +24 -13
- package/packages/core/dist/cli/commands/uninstall.js.map +1 -1
- package/packages/core/dist/cli/commands/update-consolidation.test.js +0 -10
- package/packages/core/dist/cli/commands/update-consolidation.test.js.map +1 -1
- package/packages/core/dist/cli/commands/update.js.map +1 -1
- package/packages/core/dist/cli/ocean-profiles.test.js.map +1 -1
- package/packages/core/dist/cli/theme-maker.test.js +64 -115
- package/packages/core/dist/cli/theme-maker.test.js.map +1 -1
- package/packages/core/dist/cli/utils/themes.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/themes.js +3 -2
- package/packages/core/dist/cli/utils/themes.js.map +1 -1
- package/packages/core/dist/index.d.ts +1 -1
- package/packages/core/dist/index.d.ts.map +1 -1
- package/packages/core/dist/index.js +2 -2
- package/packages/core/dist/index.js.map +1 -1
- package/packages/core/dist/plugins/plugin-discovery.d.ts +116 -0
- package/packages/core/dist/plugins/plugin-discovery.d.ts.map +1 -0
- package/packages/core/dist/plugins/plugin-discovery.js +165 -0
- package/packages/core/dist/plugins/plugin-discovery.js.map +1 -0
- package/packages/core/dist/plugins/plugin-discovery.test.d.ts +22 -0
- package/packages/core/dist/plugins/plugin-discovery.test.d.ts.map +1 -0
- package/packages/core/dist/plugins/plugin-discovery.test.js +498 -0
- package/packages/core/dist/plugins/plugin-discovery.test.js.map +1 -0
- package/packages/core/dist/scripts/add-ocean-profiles.js +1 -1
- package/packages/core/dist/scripts/add-ocean-profiles.js.map +1 -1
- package/packages/core/dist/scripts/generate-all-spiders.js +2 -0
- package/packages/core/dist/scripts/generate-all-spiders.js.map +1 -1
- package/packages/core/dist/scripts/generate-report.d.ts.map +1 -1
- package/packages/core/dist/scripts/generate-report.js +2 -0
- package/packages/core/dist/scripts/generate-report.js.map +1 -1
- package/packages/core/dist/scripts/generate-spider-report.js.map +1 -1
- package/packages/core/dist/scripts/generate-spider.d.ts.map +1 -1
- package/packages/core/dist/scripts/generate-spider.js +2 -0
- package/packages/core/dist/scripts/generate-spider.js.map +1 -1
- package/packages/core/dist/scripts/validate-ocean-profiles.js +1 -1
- package/packages/core/dist/scripts/validate-ocean-profiles.js.map +1 -1
- package/packages/core/dist/workflow/file-watch.d.ts +82 -0
- package/packages/core/dist/workflow/file-watch.d.ts.map +1 -0
- package/packages/core/dist/workflow/file-watch.js +198 -0
- package/packages/core/dist/workflow/file-watch.js.map +1 -0
- package/packages/core/dist/workflow/file-watch.test.d.ts +21 -0
- package/packages/core/dist/workflow/file-watch.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/file-watch.test.js +469 -0
- package/packages/core/dist/workflow/file-watch.test.js.map +1 -0
- package/packages/core/dist/workflow/observation-writer.d.ts +79 -0
- package/packages/core/dist/workflow/observation-writer.d.ts.map +1 -0
- package/packages/core/dist/workflow/observation-writer.js +97 -0
- package/packages/core/dist/workflow/observation-writer.js.map +1 -0
- package/packages/core/dist/workflow/observation-writer.test.d.ts +18 -0
- package/packages/core/dist/workflow/observation-writer.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/observation-writer.test.js +424 -0
- package/packages/core/dist/workflow/observation-writer.test.js.map +1 -0
- package/packages/core/dist/workflow/output-path-normalizer.d.ts +47 -0
- package/packages/core/dist/workflow/output-path-normalizer.d.ts.map +1 -0
- package/packages/core/dist/workflow/output-path-normalizer.js +79 -0
- package/packages/core/dist/workflow/output-path-normalizer.js.map +1 -0
- package/packages/core/dist/workflow/output-path-normalizer.test.d.ts +16 -0
- package/packages/core/dist/workflow/output-path-normalizer.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/output-path-normalizer.test.js +157 -0
- package/packages/core/dist/workflow/output-path-normalizer.test.js.map +1 -0
- package/packages/core/dist/workflow/story-workflow-routing.test.js +4 -2
- package/packages/core/dist/workflow/story-workflow-routing.test.js.map +1 -1
- package/packages/core/dist/workflow/tandem-lifecycle.d.ts +117 -0
- package/packages/core/dist/workflow/tandem-lifecycle.d.ts.map +1 -0
- package/packages/core/dist/workflow/tandem-lifecycle.js +186 -0
- package/packages/core/dist/workflow/tandem-lifecycle.js.map +1 -0
- package/packages/core/dist/workflow/tandem-lifecycle.test.d.ts +16 -0
- package/packages/core/dist/workflow/tandem-lifecycle.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/tandem-lifecycle.test.js +531 -0
- package/packages/core/dist/workflow/tandem-lifecycle.test.js.map +1 -0
- package/packages/core/dist/workflow/tool-watch.d.ts +68 -0
- package/packages/core/dist/workflow/tool-watch.d.ts.map +1 -0
- package/packages/core/dist/workflow/tool-watch.js +166 -0
- package/packages/core/dist/workflow/tool-watch.js.map +1 -0
- package/packages/core/dist/workflow/tool-watch.test.d.ts +18 -0
- package/packages/core/dist/workflow/tool-watch.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/tool-watch.test.js +717 -0
- package/packages/core/dist/workflow/tool-watch.test.js.map +1 -0
- package/packages/core/dist/workflow/variable-resolver.js +1 -1
- package/packages/core/dist/workflow/variable-resolver.js.map +1 -1
- package/packages/core/dist/workflow/workflow-migration.test.js +8 -4
- package/packages/core/dist/workflow/workflow-migration.test.js.map +1 -1
- package/packages/core/dist/workflow/workflow-schema.d.ts +7 -0
- package/packages/core/dist/workflow/workflow-schema.d.ts.map +1 -1
- package/packages/core/dist/workflow/workflow-schema.js +44 -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 +192 -0
- package/packages/core/dist/workflow/workflow-schema.test.js.map +1 -1
- package/pennyfarthing-dist/agents/README.md +3 -1
- package/pennyfarthing-dist/agents/ba.md +165 -0
- package/pennyfarthing-dist/agents/handoff.md +18 -3
- package/pennyfarthing-dist/agents/sm-finish.md +1 -1
- package/pennyfarthing-dist/agents/sm-handoff.md +27 -4
- package/pennyfarthing-dist/agents/sm.md +11 -5
- package/pennyfarthing-dist/agents/tandem-backseat.md +119 -0
- package/pennyfarthing-dist/commands/ba.md +17 -0
- package/pennyfarthing-dist/commands/setup.md +4 -0
- package/pennyfarthing-dist/guides/agent-behavior.md +62 -6
- package/pennyfarthing-dist/guides/bikelane.md +3 -2
- package/pennyfarthing-dist/guides/scale-levels.md +4 -6
- package/pennyfarthing-dist/guides/tandem-protocol.md +158 -0
- package/pennyfarthing-dist/guides/workflow-schema.md +1 -1
- package/pennyfarthing-dist/personas/themes/a-team.yaml +30 -0
- package/pennyfarthing-dist/personas/themes/alice-in-wonderland.yaml +30 -0
- package/pennyfarthing-dist/personas/themes/battlestar-galactica.yaml +30 -0
- package/pennyfarthing-dist/personas/themes/blade-runner.yaml +30 -0
- package/pennyfarthing-dist/personas/themes/catch-22.yaml +30 -0
- package/pennyfarthing-dist/personas/themes/control.yaml +30 -0
- package/pennyfarthing-dist/personas/themes/cowboy-bebop.yaml +31 -0
- package/pennyfarthing-dist/personas/themes/discworld.yaml +32 -1
- package/pennyfarthing-dist/personas/themes/doctor-who.yaml +31 -0
- package/pennyfarthing-dist/personas/themes/dune.yaml +32 -0
- package/pennyfarthing-dist/personas/themes/fifth-element.yaml +327 -0
- package/pennyfarthing-dist/personas/themes/firefly.yaml +31 -0
- package/pennyfarthing-dist/personas/themes/game-of-thrones.yaml +30 -0
- package/pennyfarthing-dist/personas/themes/harry-potter.yaml +30 -0
- package/pennyfarthing-dist/personas/themes/hitchhikers-guide.yaml +30 -0
- package/pennyfarthing-dist/personas/themes/lord-of-the-rings.yaml +30 -0
- package/pennyfarthing-dist/personas/themes/mad-max.yaml +30 -0
- package/pennyfarthing-dist/personas/themes/mash.yaml +33 -0
- package/pennyfarthing-dist/personas/themes/princess-bride.yaml +34 -0
- package/pennyfarthing-dist/personas/themes/sandman.yaml +33 -0
- package/pennyfarthing-dist/personas/themes/star-trek-tng.yaml +34 -0
- package/pennyfarthing-dist/personas/themes/star-wars.yaml +33 -0
- package/pennyfarthing-dist/personas/themes/the-expanse.yaml +30 -0
- package/pennyfarthing-dist/personas/themes/the-matrix.yaml +30 -0
- package/pennyfarthing-dist/personas/themes/watchmen.yaml +30 -0
- package/pennyfarthing-dist/personas/themes/west-wing.yaml +30 -0
- package/pennyfarthing-dist/personas/themes/x-files.yaml +30 -0
- package/pennyfarthing-dist/scripts/README.md +1 -1
- package/pennyfarthing-dist/scripts/core/agent-session.sh +1 -1
- package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +131 -54
- package/pennyfarthing-dist/scripts/hooks/post-merge.sh +20 -10
- package/pennyfarthing-dist/scripts/misc/statusline.sh +50 -8
- package/pennyfarthing-dist/scripts/portraits/generate-portraits.py +2 -2
- package/pennyfarthing-dist/scripts/validation/validate-agent-schema.sh +1 -0
- package/pennyfarthing-dist/scripts/workflow/README.md +2 -2
- package/pennyfarthing-dist/scripts/workflow/finish-story.sh +10 -189
- package/pennyfarthing-dist/skills/skill-registry.schema.json +8 -0
- package/pennyfarthing-dist/skills/skill-registry.yaml +1 -1
- package/pennyfarthing-dist/skills/sprint/skill.md +25 -2
- package/pennyfarthing-dist/skills/theme/skill.md +1 -1
- package/pennyfarthing-dist/skills/workflow/skill.md +24 -1
- package/pennyfarthing-dist/workflows/architecture/workflow.yaml +65 -0
- package/pennyfarthing-dist/workflows/architecture.yaml +2 -2
- package/pennyfarthing-dist/workflows/bdd-tandem.yaml +70 -0
- package/pennyfarthing-dist/workflows/epics-and-stories/workflow.yaml +2 -2
- package/pennyfarthing-dist/workflows/implementation-readiness/workflow.yaml +2 -2
- package/pennyfarthing-dist/workflows/prd/workflow.yaml +2 -2
- package/pennyfarthing-dist/workflows/product-brief/workflow.yaml +2 -2
- package/pennyfarthing-dist/workflows/project-context/workflow.yaml +2 -2
- package/pennyfarthing-dist/workflows/quick-dev/workflow.yaml +2 -2
- package/pennyfarthing-dist/workflows/research/workflow.yaml +2 -2
- package/pennyfarthing-dist/workflows/retrospective/workflow.yaml +1 -1
- package/pennyfarthing-dist/workflows/sprint-planning/workflow.yaml +3 -3
- package/pennyfarthing-dist/workflows/tdd-tandem.yaml +61 -0
- package/pennyfarthing-dist/workflows/ux-design/workflow.yaml +2 -2
- package/pennyfarthing_scripts/__pycache__/cli.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__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bellmode_hook.py +202 -47
- package/pennyfarthing_scripts/bikerack/__init__.py +36 -0
- package/pennyfarthing_scripts/bikerack/__main__.py +5 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/cli.py +148 -0
- package/pennyfarthing_scripts/bikerack/launcher.py +181 -0
- package/pennyfarthing_scripts/brownfield/__init__.py +6 -6
- package/pennyfarthing_scripts/brownfield/__main__.py +1 -0
- package/pennyfarthing_scripts/brownfield/cli.py +0 -1
- package/pennyfarthing_scripts/brownfield/discover.py +1 -2
- package/pennyfarthing_scripts/cli.py +16 -6
- package/pennyfarthing_scripts/codemarkers/__init__.py +5 -1
- 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/codemarkers/analyze.py +177 -2
- package/pennyfarthing_scripts/codemarkers/cli.py +50 -0
- package/pennyfarthing_scripts/codemarkers/formatters.py +0 -1
- package/pennyfarthing_scripts/codemarkers/models.py +15 -0
- package/pennyfarthing_scripts/common/__init__.py +8 -9
- 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/config.py +1 -1
- package/pennyfarthing_scripts/complexity/__init__.py +1 -1
- 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/complexity/analyze.py +1 -1
- package/pennyfarthing_scripts/complexity/cli.py +5 -1
- package/pennyfarthing_scripts/complexity/formatters.py +1 -1
- package/pennyfarthing_scripts/context.py +14 -15
- 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/deadcode/analyze.py +3 -4
- package/pennyfarthing_scripts/deadcode/cli.py +2 -2
- package/pennyfarthing_scripts/dependencies/__init__.py +2 -2
- 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/dependencies/analyze.py +1 -1
- package/pennyfarthing_scripts/dependencies/cli.py +8 -4
- package/pennyfarthing_scripts/dependencies/formatters.py +1 -1
- package/pennyfarthing_scripts/git/__init__.py +5 -5
- package/pennyfarthing_scripts/git/create_branches.py +3 -2
- package/pennyfarthing_scripts/git/status_all.py +1 -1
- package/pennyfarthing_scripts/healthscore/__init__.py +2 -2
- package/pennyfarthing_scripts/healthscore/__main__.py +8 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/healthscore/analyze.py +452 -21
- package/pennyfarthing_scripts/healthscore/cli.py +5 -1
- package/pennyfarthing_scripts/healthscore/models.py +0 -1
- package/pennyfarthing_scripts/hooks.py +8 -11
- package/pennyfarthing_scripts/hotspots/__init__.py +6 -6
- package/pennyfarthing_scripts/hotspots/__pycache__/__init__.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__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hotspots/analyze.py +128 -14
- package/pennyfarthing_scripts/hotspots/cli.py +2 -2
- package/pennyfarthing_scripts/hotspots/models.py +0 -1
- package/pennyfarthing_scripts/jira/__init__.py +15 -17
- package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/create.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/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/jira/bidirectional.py +2 -3
- package/pennyfarthing_scripts/jira/claim.py +21 -0
- package/pennyfarthing_scripts/jira/cli.py +2 -2
- package/pennyfarthing_scripts/jira/client.py +4 -4
- package/pennyfarthing_scripts/jira/create.py +45 -1
- package/pennyfarthing_scripts/jira/epic.py +3 -2
- package/pennyfarthing_scripts/jira/reconcile.py +0 -1
- package/pennyfarthing_scripts/jira/story.py +2 -0
- package/pennyfarthing_scripts/jira/sync.py +1 -1
- package/pennyfarthing_scripts/migration/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/session.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/skill.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/step.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/validate.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/skill.py +0 -1
- package/pennyfarthing_scripts/migration/step.py +0 -1
- package/pennyfarthing_scripts/migration/validate.py +8 -5
- package/pennyfarthing_scripts/patch_mode.py +2 -2
- package/pennyfarthing_scripts/preflight/__init__.py +1 -1
- package/pennyfarthing_scripts/preflight/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/finish.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/finish.py +0 -1
- package/pennyfarthing_scripts/pretooluse_hook.py +6 -7
- package/pennyfarthing_scripts/prime/__init__.py +2 -0
- package/pennyfarthing_scripts/prime/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/loader.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/tiers.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/cli.py +18 -1
- package/pennyfarthing_scripts/prime/loader.py +72 -3
- package/pennyfarthing_scripts/prime/persona.py +4 -2
- package/pennyfarthing_scripts/prime/tiers.py +17 -4
- package/pennyfarthing_scripts/schema_validation_hook.py +2 -3
- package/pennyfarthing_scripts/sprint/__init__.py +10 -12
- package/pennyfarthing_scripts/sprint/__main__.py +2 -2
- package/pennyfarthing_scripts/sprint/__pycache__/__init__.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__/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/archive.py +0 -1
- package/pennyfarthing_scripts/sprint/archive_epic.py +1 -4
- package/pennyfarthing_scripts/sprint/cli.py +34 -28
- package/pennyfarthing_scripts/sprint/epic_add.py +8 -1
- package/pennyfarthing_scripts/sprint/import_epic.py +42 -18
- package/pennyfarthing_scripts/sprint/loader.py +6 -0
- package/pennyfarthing_scripts/sprint/status.py +1 -2
- package/pennyfarthing_scripts/sprint/story_add.py +2 -2
- package/pennyfarthing_scripts/sprint/story_finish.py +3 -5
- package/pennyfarthing_scripts/sprint/story_update.py +11 -3
- package/pennyfarthing_scripts/sprint/validate_cmd.py +0 -1
- package/pennyfarthing_scripts/sprint/validator.py +120 -6
- package/pennyfarthing_scripts/sprint/work.py +1 -4
- package/pennyfarthing_scripts/sprint/yaml_io.py +10 -2
- package/pennyfarthing_scripts/story/__init__.py +14 -16
- package/pennyfarthing_scripts/story/__pycache__/__init__.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/story/size.py +0 -1
- package/pennyfarthing_scripts/story/template.py +0 -1
- package/pennyfarthing_scripts/swebench.py +1 -2
- package/pennyfarthing_scripts/tests/__pycache__/conftest.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_epic_shard_validation.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_sprint_validator.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/conftest.py +1 -2
- package/pennyfarthing_scripts/tests/test_bikerack.py +785 -0
- package/pennyfarthing_scripts/tests/test_brownfield.py +10 -13
- package/pennyfarthing_scripts/tests/test_cli_modules.py +0 -4
- package/pennyfarthing_scripts/tests/test_codemarkers.py +13 -8
- package/pennyfarthing_scripts/tests/test_common.py +9 -4
- package/pennyfarthing_scripts/tests/test_epic_shard_validation.py +699 -0
- package/pennyfarthing_scripts/tests/test_git_utils.py +10 -13
- package/pennyfarthing_scripts/tests/test_healthscore.py +17 -25
- package/pennyfarthing_scripts/tests/test_jira_package.py +0 -3
- package/pennyfarthing_scripts/tests/test_package_structure.py +3 -16
- package/pennyfarthing_scripts/tests/test_patch_mode.py +7 -11
- package/pennyfarthing_scripts/tests/test_prime.py +39 -21
- package/pennyfarthing_scripts/tests/test_sprint_package.py +3 -8
- package/pennyfarthing_scripts/tests/test_sprint_validator.py +53 -5
- package/pennyfarthing_scripts/tests/test_story_add.py +3 -7
- package/pennyfarthing_scripts/tests/test_story_package.py +0 -3
- package/pennyfarthing_scripts/tests/test_story_update.py +5 -10
- package/pennyfarthing_scripts/tests/test_tiers.py +18 -17
- package/pennyfarthing_scripts/tests/test_token_counting.py +19 -13
- package/pennyfarthing_scripts/tests/test_topology_loader.py +620 -0
- package/pennyfarthing_scripts/tests/test_validate_cmd.py +2 -7
- package/pennyfarthing_scripts/tests/test_workflow_check.py +0 -2
- package/pennyfarthing_scripts/tests/test_yaml_io.py +0 -3
- 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/theme/cli.py +3 -2
- package/pennyfarthing_scripts/validate/__init__.py +21 -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/__init__.py +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/agent.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/schema.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/skill_command.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/sprint.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/agent.py +239 -0
- package/pennyfarthing_scripts/validate/adapters/schema.py +30 -0
- package/pennyfarthing_scripts/validate/adapters/skill_command.py +291 -0
- package/pennyfarthing_scripts/validate/adapters/sprint.py +69 -0
- package/pennyfarthing_scripts/validate/adapters/workflow.py +320 -0
- package/pennyfarthing_scripts/validate/cli.py +141 -0
- package/pennyfarthing_scripts/welcome_hook.py +2 -3
- package/pennyfarthing_scripts/workflow.py +3 -3
- package/scripts/README.md +3 -15
- package/pennyfarthing-dist/commands/benchmark-control.md +0 -69
- package/pennyfarthing-dist/commands/benchmark.md +0 -485
- package/pennyfarthing-dist/commands/job-fair.md +0 -102
- package/pennyfarthing-dist/commands/solo.md +0 -447
- package/pennyfarthing-dist/guides/benchmarks.md +0 -62
- package/pennyfarthing-dist/scripts/test/ensure-swebench-data.sh +0 -59
- package/pennyfarthing-dist/scripts/test/ground-truth-judge.py +0 -220
- package/pennyfarthing-dist/scripts/test/swebench-judge.py +0 -374
- package/pennyfarthing-dist/scripts/test/test-cache.sh +0 -165
- package/pennyfarthing-dist/scripts/test/test-setup.sh +0 -337
- package/pennyfarthing-dist/scripts/theme/compute-theme-tiers.sh +0 -13
- package/pennyfarthing-dist/scripts/theme/compute_theme_tiers.py +0 -402
- package/pennyfarthing-dist/scripts/theme/update-theme-tiers.sh +0 -97
- package/pennyfarthing-dist/skills/finalize-run/SKILL.md +0 -261
- package/pennyfarthing-dist/skills/judge/SKILL.md +0 -644
- package/pennyfarthing-dist/skills/persona-benchmark/SKILL.md +0 -187
- package/pennyfarthing-dist/workflows/dev-story/checklist.md +0 -80
- package/pennyfarthing-dist/workflows/dev-story/instructions.xml +0 -410
- package/pennyfarthing-dist/workflows/dev-story/workflow.yaml +0 -50
- package/pennyfarthing-dist/workflows/quick-spec/steps/step-01-understand.md +0 -201
- package/pennyfarthing-dist/workflows/quick-spec/steps/step-02-investigate.md +0 -156
- package/pennyfarthing-dist/workflows/quick-spec/steps/step-03-generate.md +0 -140
- package/pennyfarthing-dist/workflows/quick-spec/steps/step-04-review.md +0 -203
- package/pennyfarthing-dist/workflows/quick-spec/tech-spec-template.md +0 -74
- package/pennyfarthing-dist/workflows/quick-spec/workflow.yaml +0 -27
|
@@ -15,8 +15,8 @@ workflow:
|
|
|
15
15
|
# Variables available in step files
|
|
16
16
|
variables:
|
|
17
17
|
project_root: .
|
|
18
|
-
planning_artifacts:
|
|
19
|
-
output_file:
|
|
18
|
+
planning_artifacts: sprint/planning/
|
|
19
|
+
output_file: sprint/planning/generate-project-context.md
|
|
20
20
|
|
|
21
21
|
# Agent assignment (customize as needed)
|
|
22
22
|
agent: architect
|
|
@@ -15,8 +15,8 @@ workflow:
|
|
|
15
15
|
# Variables available in step files
|
|
16
16
|
variables:
|
|
17
17
|
project_root: .
|
|
18
|
-
planning_artifacts:
|
|
19
|
-
output_file:
|
|
18
|
+
planning_artifacts: sprint/planning/
|
|
19
|
+
output_file: sprint/planning/quick-dev.md
|
|
20
20
|
|
|
21
21
|
# Agent assignment (customize as needed)
|
|
22
22
|
agent: architect
|
|
@@ -23,8 +23,8 @@ workflow:
|
|
|
23
23
|
# Variables available in step files
|
|
24
24
|
variables:
|
|
25
25
|
project_root: .
|
|
26
|
-
planning_artifacts:
|
|
27
|
-
output_file:
|
|
26
|
+
planning_artifacts: sprint/planning/
|
|
27
|
+
output_file: sprint/planning/research.md
|
|
28
28
|
research_type: ""
|
|
29
29
|
research_topic: ""
|
|
30
30
|
research_goals: ""
|
|
@@ -38,7 +38,7 @@ workflow:
|
|
|
38
38
|
variables:
|
|
39
39
|
project_root: .
|
|
40
40
|
config_source: "./_bmad/bmm/config.yaml"
|
|
41
|
-
planning_artifacts:
|
|
41
|
+
planning_artifacts: sprint/planning/
|
|
42
42
|
implementation_artifacts: ./artifacts
|
|
43
43
|
sprint_status_file: ./artifacts/sprint-status.yaml
|
|
44
44
|
story_directory: ./artifacts/stories
|
|
@@ -15,12 +15,12 @@ workflow:
|
|
|
15
15
|
# Variables available in step files
|
|
16
16
|
variables:
|
|
17
17
|
project_root: .
|
|
18
|
-
planning_artifacts:
|
|
18
|
+
planning_artifacts: sprint/planning/
|
|
19
19
|
implementation_artifacts: ./artifacts
|
|
20
20
|
epics_location: ./artifacts
|
|
21
21
|
epics_pattern: "epic*.md"
|
|
22
|
-
status_file:
|
|
23
|
-
output_file:
|
|
22
|
+
status_file: sprint/planning/sprint-status.yaml
|
|
23
|
+
output_file: sprint/planning/sprint-status.yaml
|
|
24
24
|
|
|
25
25
|
# Agent assignment - SM handles sprint planning
|
|
26
26
|
agent: sm
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# TDD Tandem Workflow - TDD with Full Tandem Chain
|
|
2
|
+
# Extends standard TDD with tandem observers on every phase:
|
|
3
|
+
# - Architect backseats TEA during red phase
|
|
4
|
+
# - TEA backseats Dev during green phase
|
|
5
|
+
# - PM backseats Reviewer during review phase
|
|
6
|
+
#
|
|
7
|
+
# Flow: SM → TEA (+Architect) → Dev (+TEA) → Reviewer (+PM) → SM
|
|
8
|
+
|
|
9
|
+
workflow:
|
|
10
|
+
name: tdd-tandem
|
|
11
|
+
description: TDD with full tandem chain across all phases
|
|
12
|
+
version: "2.0.0"
|
|
13
|
+
|
|
14
|
+
phases:
|
|
15
|
+
- name: setup
|
|
16
|
+
agent: sm
|
|
17
|
+
output: [session_file, branches, story_context]
|
|
18
|
+
|
|
19
|
+
- name: red
|
|
20
|
+
agent: tea
|
|
21
|
+
input: [session_file, story_context]
|
|
22
|
+
output: [failing_tests]
|
|
23
|
+
gate:
|
|
24
|
+
type: tests_fail
|
|
25
|
+
condition: All acceptance criteria have test coverage
|
|
26
|
+
tandem:
|
|
27
|
+
partner: architect
|
|
28
|
+
scope: file-watch
|
|
29
|
+
|
|
30
|
+
- name: green
|
|
31
|
+
agent: dev
|
|
32
|
+
input: [failing_tests, story_context]
|
|
33
|
+
output: [implementation, passing_tests]
|
|
34
|
+
gate:
|
|
35
|
+
type: tests_pass
|
|
36
|
+
condition: All tests passing, no skipped tests
|
|
37
|
+
tandem:
|
|
38
|
+
partner: tea
|
|
39
|
+
scope: file-watch
|
|
40
|
+
|
|
41
|
+
- name: review
|
|
42
|
+
agent: reviewer
|
|
43
|
+
input: [implementation, passing_tests]
|
|
44
|
+
output: [approval]
|
|
45
|
+
gate:
|
|
46
|
+
type: approval
|
|
47
|
+
condition: Code review approved, no blocking issues
|
|
48
|
+
tandem:
|
|
49
|
+
partner: pm
|
|
50
|
+
scope: file-watch
|
|
51
|
+
|
|
52
|
+
- name: finish
|
|
53
|
+
agent: sm
|
|
54
|
+
input: [approval]
|
|
55
|
+
output: [archived_session, story_summary]
|
|
56
|
+
|
|
57
|
+
triggers:
|
|
58
|
+
tags: [tandem]
|
|
59
|
+
points:
|
|
60
|
+
min: 3
|
|
61
|
+
default: false
|
|
@@ -29,8 +29,8 @@ workflow:
|
|
|
29
29
|
# Variables available in step files
|
|
30
30
|
variables:
|
|
31
31
|
project_root: .
|
|
32
|
-
planning_artifacts:
|
|
33
|
-
output_file:
|
|
32
|
+
planning_artifacts: sprint/planning/
|
|
33
|
+
output_file: sprint/planning/ux-design-specification.md
|
|
34
34
|
|
|
35
35
|
# Agent assignment - UX Designer for visual design workflow
|
|
36
36
|
agent: ux-designer
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,30 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
Bell Mode
|
|
3
|
+
PostToolUse Hook — Bell Mode + Tandem Injection (Python)
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
the first queued message as additionalContext to be injected into
|
|
8
|
-
Claude's next API call.
|
|
5
|
+
Called by Claude Code after each tool execution. Handles two independent
|
|
6
|
+
injection systems:
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
1. Bell queue (Cyclist only) — injects queued user messages when Cyclist
|
|
9
|
+
is running and bell_mode is enabled. In CLI sessions this is a no-op.
|
|
10
|
+
2. Tandem observations (always active) — injects backseat agent observations
|
|
11
|
+
when tandem observation files exist. No configuration required.
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"hookSpecificOutput": {
|
|
17
|
-
"hookEventName": "PostToolUse",
|
|
18
|
-
"additionalContext": "User feedback: <message>"
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
Output when disabled or queue empty: (nothing - exit 0)
|
|
13
|
+
Bell queue takes precedence: if a queued message exists, tandem is
|
|
14
|
+
deferred to the next hook invocation.
|
|
23
15
|
|
|
24
16
|
Story: MSSCI-12409 - Hook consistency and WheelHub consolidation
|
|
25
17
|
"""
|
|
26
18
|
|
|
27
19
|
import json
|
|
20
|
+
import re
|
|
28
21
|
import sys
|
|
29
22
|
from pathlib import Path
|
|
30
23
|
|
|
@@ -32,12 +25,13 @@ from pathlib import Path
|
|
|
32
25
|
sys.path.insert(0, str(Path(__file__).parent))
|
|
33
26
|
|
|
34
27
|
from hooks import (
|
|
35
|
-
|
|
36
|
-
get_cyclist_port,
|
|
37
|
-
send_to_cyclist,
|
|
38
|
-
output_hook_response,
|
|
28
|
+
CYCLIST_PORT_FILE,
|
|
39
29
|
HookResponse,
|
|
30
|
+
find_project_root,
|
|
40
31
|
is_bell_mode_enabled,
|
|
32
|
+
output_hook_response,
|
|
33
|
+
read_port_file,
|
|
34
|
+
send_to_cyclist,
|
|
41
35
|
)
|
|
42
36
|
|
|
43
37
|
|
|
@@ -106,8 +100,170 @@ def notify_cyclist(project_root: Path, message_text: str) -> None:
|
|
|
106
100
|
pass
|
|
107
101
|
|
|
108
102
|
|
|
103
|
+
# =============================================================================
|
|
104
|
+
# Tandem Observation Injection (Story 95-7 / MSSCI-14672)
|
|
105
|
+
# =============================================================================
|
|
106
|
+
# Stubs for tandem observation injection. Dev will implement.
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def read_tandem_observations(project_root: Path) -> list[Path]:
|
|
110
|
+
"""Find tandem observation files in .session/ directory.
|
|
111
|
+
|
|
112
|
+
Looks for files matching .session/*-tandem-*.md pattern.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
project_root: Project root directory
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
List of Path objects for tandem observation files
|
|
119
|
+
"""
|
|
120
|
+
session_dir = project_root / ".session"
|
|
121
|
+
if not session_dir.is_dir():
|
|
122
|
+
return []
|
|
123
|
+
return sorted(session_dir.glob("*-tandem-*.md"))
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def get_latest_observation(file_content: str) -> dict | None:
|
|
127
|
+
"""Extract the latest observation entry from a tandem observation file.
|
|
128
|
+
|
|
129
|
+
Parses the markdown format to extract the most recent ## [HH:MM] Observation
|
|
130
|
+
block, including the observer persona name from the file header.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
file_content: Full text content of the tandem observation file
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Dict with 'persona' and 'text' keys, or None if no observations found
|
|
137
|
+
"""
|
|
138
|
+
# Extract persona from header: **Observer:** agent (Persona Name)
|
|
139
|
+
persona_match = re.search(r"\*\*Observer:\*\*\s*\w+\s*\(([^)]+)\)", file_content)
|
|
140
|
+
persona = persona_match.group(1) if persona_match else "Unknown"
|
|
141
|
+
|
|
142
|
+
# Split on observation headers: ## [HH:MM] Observation
|
|
143
|
+
entries = re.split(r"## \[\d{1,2}:\d{2}\] Observation\n", file_content)
|
|
144
|
+
if len(entries) < 2:
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
# Last entry is the most recent observation
|
|
148
|
+
last_entry = entries[-1].strip()
|
|
149
|
+
# Remove trailing --- separator
|
|
150
|
+
last_entry = re.sub(r"\n---\s*$", "", last_entry).strip()
|
|
151
|
+
# Remove the **Trigger:** line
|
|
152
|
+
lines = last_entry.split("\n")
|
|
153
|
+
text_lines = [line for line in lines if not line.startswith("**Trigger:**")]
|
|
154
|
+
text = "\n".join(text_lines).strip()
|
|
155
|
+
|
|
156
|
+
if not text:
|
|
157
|
+
return None
|
|
158
|
+
|
|
159
|
+
return {"persona": persona, "text": text}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def format_tandem_message(persona_name: str, observation_text: str) -> str:
|
|
163
|
+
"""Format a tandem observation as a bell mode injection message.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
persona_name: The backseat agent's persona name
|
|
167
|
+
observation_text: The observation summary text
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Formatted string: [Tandem] {persona_name}: {observation_text}
|
|
171
|
+
"""
|
|
172
|
+
return f"[Tandem] {persona_name}: {observation_text}"
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def get_tandem_mtime(project_root: Path, agent: str) -> float:
|
|
176
|
+
"""Read the last-checked mtime for a tandem agent's observation file.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
project_root: Project root directory
|
|
180
|
+
agent: Agent name (e.g. 'reviewer', 'tea')
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
Last-checked mtime as float, or 0.0 if no sidecar exists
|
|
184
|
+
"""
|
|
185
|
+
sidecar = project_root / ".session" / f".tandem-mtime-{agent}"
|
|
186
|
+
if not sidecar.exists():
|
|
187
|
+
return 0.0
|
|
188
|
+
try:
|
|
189
|
+
return float(sidecar.read_text().strip())
|
|
190
|
+
except (ValueError, OSError):
|
|
191
|
+
return 0.0
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def save_tandem_mtime(project_root: Path, agent: str, mtime: float) -> None:
|
|
195
|
+
"""Save the mtime for a tandem agent's observation file.
|
|
196
|
+
|
|
197
|
+
Writes to .session/.tandem-mtime-{agent} sidecar file.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
project_root: Project root directory
|
|
201
|
+
agent: Agent name (e.g. 'reviewer', 'tea')
|
|
202
|
+
mtime: The mtime value to save
|
|
203
|
+
"""
|
|
204
|
+
sidecar = project_root / ".session" / f".tandem-mtime-{agent}"
|
|
205
|
+
try:
|
|
206
|
+
sidecar.write_text(str(mtime))
|
|
207
|
+
except OSError:
|
|
208
|
+
pass
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def check_tandem_files(project_root: Path) -> list[dict]:
|
|
212
|
+
"""Check for new tandem observations and return injection messages.
|
|
213
|
+
|
|
214
|
+
Main entry point for tandem injection in the PostToolUse hook.
|
|
215
|
+
Always active — no configuration required. The presence of tandem
|
|
216
|
+
observation files in .session/ is the only signal needed.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
project_root: Project root directory
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
List of dicts with 'agent', 'message' keys for each new observation
|
|
223
|
+
"""
|
|
224
|
+
tandem_files = read_tandem_observations(project_root)
|
|
225
|
+
if not tandem_files:
|
|
226
|
+
return []
|
|
227
|
+
|
|
228
|
+
results = []
|
|
229
|
+
for obs_file in tandem_files:
|
|
230
|
+
# Extract agent name from filename: *-tandem-{agent}.md
|
|
231
|
+
agent_match = re.search(r"-tandem-(\w+)\.md$", obs_file.name)
|
|
232
|
+
if not agent_match:
|
|
233
|
+
continue
|
|
234
|
+
agent = agent_match.group(1)
|
|
235
|
+
|
|
236
|
+
# Compare mtime
|
|
237
|
+
try:
|
|
238
|
+
file_mtime = obs_file.stat().st_mtime
|
|
239
|
+
except OSError:
|
|
240
|
+
continue
|
|
241
|
+
saved_mtime = get_tandem_mtime(project_root, agent)
|
|
242
|
+
if file_mtime == saved_mtime:
|
|
243
|
+
continue
|
|
244
|
+
|
|
245
|
+
# Read and parse
|
|
246
|
+
try:
|
|
247
|
+
content = obs_file.read_text()
|
|
248
|
+
except OSError:
|
|
249
|
+
continue
|
|
250
|
+
obs = get_latest_observation(content)
|
|
251
|
+
if not obs:
|
|
252
|
+
# Update mtime even for unparseable files to avoid re-checking
|
|
253
|
+
save_tandem_mtime(project_root, agent, file_mtime)
|
|
254
|
+
continue
|
|
255
|
+
|
|
256
|
+
message = format_tandem_message(obs["persona"], obs["text"])
|
|
257
|
+
results.append({"agent": agent, "message": message})
|
|
258
|
+
|
|
259
|
+
# Update mtime sidecar
|
|
260
|
+
save_tandem_mtime(project_root, agent, file_mtime)
|
|
261
|
+
|
|
262
|
+
return results
|
|
263
|
+
|
|
264
|
+
|
|
109
265
|
def main() -> None:
|
|
110
|
-
"""Main entry point for PostToolUse
|
|
266
|
+
"""Main entry point for PostToolUse hook."""
|
|
111
267
|
try:
|
|
112
268
|
# Read and discard stdin (required by hook protocol)
|
|
113
269
|
sys.stdin.read()
|
|
@@ -117,35 +273,34 @@ def main() -> None:
|
|
|
117
273
|
if not project_root:
|
|
118
274
|
sys.exit(0)
|
|
119
275
|
|
|
120
|
-
#
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
276
|
+
# --- Bell queue (Cyclist only, requires bell_mode: true) ---
|
|
277
|
+
# Takes precedence over tandem when active.
|
|
278
|
+
is_cyclist = read_port_file(CYCLIST_PORT_FILE, project_root) is not None
|
|
279
|
+
if is_cyclist and is_bell_mode_enabled(project_root):
|
|
280
|
+
queue = read_bell_queue(project_root)
|
|
281
|
+
if queue:
|
|
282
|
+
first_message = queue[0]
|
|
283
|
+
message_text = first_message.get("text", "")
|
|
284
|
+
if message_text:
|
|
285
|
+
output_hook_response(HookResponse(
|
|
286
|
+
event_name="PostToolUse",
|
|
287
|
+
additional_context=f"User feedback: {message_text}",
|
|
288
|
+
))
|
|
289
|
+
dequeue_message(project_root)
|
|
290
|
+
notify_cyclist(project_root, message_text)
|
|
291
|
+
sys.exit(0)
|
|
292
|
+
|
|
293
|
+
# --- Tandem observations (always active, no config required) ---
|
|
294
|
+
tandem_results = check_tandem_files(project_root)
|
|
295
|
+
if tandem_results:
|
|
296
|
+
output_hook_response(HookResponse(
|
|
297
|
+
event_name="PostToolUse",
|
|
298
|
+
additional_context=tandem_results[0]["message"],
|
|
299
|
+
))
|
|
144
300
|
|
|
145
301
|
sys.exit(0)
|
|
146
302
|
|
|
147
303
|
except Exception as e:
|
|
148
|
-
# On error, exit silently
|
|
149
304
|
print(f"[bellmode-hook] Error: {e}", file=sys.stderr)
|
|
150
305
|
sys.exit(0)
|
|
151
306
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""BikeRack Mode — Decoupled WheelHub launcher for CLI-first developers.
|
|
2
|
+
|
|
3
|
+
Story 101-5: BikeRack launcher CLI (pf bikerack start/stop/status)
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from pennyfarthing_scripts.bikerack.launcher import (
|
|
7
|
+
build_otel_env,
|
|
8
|
+
cleanup_files,
|
|
9
|
+
exec_claude,
|
|
10
|
+
get_status,
|
|
11
|
+
is_already_running,
|
|
12
|
+
is_process_alive,
|
|
13
|
+
poll_for_port_file,
|
|
14
|
+
read_pid_file,
|
|
15
|
+
read_port_file,
|
|
16
|
+
register_cleanup,
|
|
17
|
+
start_wheelhub,
|
|
18
|
+
stop_bikerack,
|
|
19
|
+
write_pid_file,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"build_otel_env",
|
|
24
|
+
"cleanup_files",
|
|
25
|
+
"exec_claude",
|
|
26
|
+
"get_status",
|
|
27
|
+
"is_already_running",
|
|
28
|
+
"is_process_alive",
|
|
29
|
+
"poll_for_port_file",
|
|
30
|
+
"read_pid_file",
|
|
31
|
+
"read_port_file",
|
|
32
|
+
"register_cleanup",
|
|
33
|
+
"start_wheelhub",
|
|
34
|
+
"stop_bikerack",
|
|
35
|
+
"write_pid_file",
|
|
36
|
+
]
|
|
Binary file
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""BikeRack CLI — Click-based CLI for bikerack operations.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
pf bikerack [COMMAND]
|
|
5
|
+
|
|
6
|
+
Commands:
|
|
7
|
+
start Start BikeRack mode (default)
|
|
8
|
+
stop Stop running BikeRack instance
|
|
9
|
+
status Show running state
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
import click
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@click.group(invoke_without_command=True)
|
|
20
|
+
@click.pass_context
|
|
21
|
+
def bikerack(ctx):
|
|
22
|
+
"""BikeRack Mode — Decoupled WheelHub dashboard launcher.
|
|
23
|
+
|
|
24
|
+
\b
|
|
25
|
+
Commands:
|
|
26
|
+
start - Start WheelHub + Claude CLI (default)
|
|
27
|
+
stop - Stop running BikeRack instance
|
|
28
|
+
status - Show running state (PID, port, uptime)
|
|
29
|
+
"""
|
|
30
|
+
if ctx.invoked_subcommand is None:
|
|
31
|
+
ctx.invoke(start)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@bikerack.command()
|
|
35
|
+
@click.option(
|
|
36
|
+
"--project-dir",
|
|
37
|
+
type=click.Path(exists=True, file_okay=False, resolve_path=True),
|
|
38
|
+
default=None,
|
|
39
|
+
help="Project directory (where .pennyfarthing/ lives). Falls back to CYCLIST_PROJECT_DIR env var, then cwd.",
|
|
40
|
+
)
|
|
41
|
+
def start(project_dir):
|
|
42
|
+
"""Start BikeRack mode.
|
|
43
|
+
|
|
44
|
+
Starts WheelHub in background, waits for readiness,
|
|
45
|
+
sets OTEL env vars, and execs Claude CLI.
|
|
46
|
+
"""
|
|
47
|
+
from pennyfarthing_scripts.bikerack.launcher import (
|
|
48
|
+
build_otel_env,
|
|
49
|
+
exec_claude,
|
|
50
|
+
is_already_running,
|
|
51
|
+
poll_for_port_file,
|
|
52
|
+
register_cleanup,
|
|
53
|
+
start_wheelhub,
|
|
54
|
+
write_pid_file,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
if project_dir:
|
|
58
|
+
project_dir = Path(project_dir)
|
|
59
|
+
elif os.environ.get("CYCLIST_PROJECT_DIR"):
|
|
60
|
+
project_dir = Path(os.environ["CYCLIST_PROJECT_DIR"])
|
|
61
|
+
else:
|
|
62
|
+
project_dir = Path.cwd()
|
|
63
|
+
|
|
64
|
+
running, pid, port = is_already_running(project_dir)
|
|
65
|
+
if running:
|
|
66
|
+
click.echo(
|
|
67
|
+
f"Error: BikeRack is already running (PID {pid}, port {port})",
|
|
68
|
+
err=True,
|
|
69
|
+
)
|
|
70
|
+
click.echo("Use 'pf bikerack stop' to stop it.", err=True)
|
|
71
|
+
sys.exit(2)
|
|
72
|
+
|
|
73
|
+
click.echo("Starting BikeRack mode...")
|
|
74
|
+
try:
|
|
75
|
+
proc = start_wheelhub(project_dir)
|
|
76
|
+
write_pid_file(project_dir, proc.pid)
|
|
77
|
+
|
|
78
|
+
port = poll_for_port_file(project_dir)
|
|
79
|
+
click.echo(f"WheelHub listening on http://localhost:{port}")
|
|
80
|
+
|
|
81
|
+
otel_env = build_otel_env(port)
|
|
82
|
+
click.echo("Setting OTEL environment variables...")
|
|
83
|
+
|
|
84
|
+
register_cleanup(project_dir, proc.pid)
|
|
85
|
+
|
|
86
|
+
click.echo(f"Dashboard: http://localhost:{port}/bikerack")
|
|
87
|
+
click.echo("Starting Claude CLI...")
|
|
88
|
+
exec_claude(otel_env, project_dir)
|
|
89
|
+
except TimeoutError as e:
|
|
90
|
+
click.echo(f"Error: {e}", err=True)
|
|
91
|
+
sys.exit(1)
|
|
92
|
+
except Exception as e:
|
|
93
|
+
click.echo(f"Error: {e}", err=True)
|
|
94
|
+
sys.exit(1)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@bikerack.command()
|
|
98
|
+
@click.option(
|
|
99
|
+
"--project-dir",
|
|
100
|
+
type=click.Path(exists=True, file_okay=False, resolve_path=True),
|
|
101
|
+
default=None,
|
|
102
|
+
help="Project directory. Falls back to CYCLIST_PROJECT_DIR env var, then cwd.",
|
|
103
|
+
)
|
|
104
|
+
def stop(project_dir):
|
|
105
|
+
"""Stop running BikeRack instance."""
|
|
106
|
+
from pennyfarthing_scripts.bikerack.launcher import stop_bikerack
|
|
107
|
+
|
|
108
|
+
if project_dir:
|
|
109
|
+
project_dir = Path(project_dir)
|
|
110
|
+
elif os.environ.get("CYCLIST_PROJECT_DIR"):
|
|
111
|
+
project_dir = Path(os.environ["CYCLIST_PROJECT_DIR"])
|
|
112
|
+
else:
|
|
113
|
+
project_dir = Path.cwd()
|
|
114
|
+
result = stop_bikerack(project_dir)
|
|
115
|
+
|
|
116
|
+
if result["success"]:
|
|
117
|
+
click.echo(result["message"])
|
|
118
|
+
else:
|
|
119
|
+
click.echo(result["message"], err=True)
|
|
120
|
+
sys.exit(1)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@bikerack.command()
|
|
124
|
+
@click.option(
|
|
125
|
+
"--project-dir",
|
|
126
|
+
type=click.Path(exists=True, file_okay=False, resolve_path=True),
|
|
127
|
+
default=None,
|
|
128
|
+
help="Project directory. Falls back to CYCLIST_PROJECT_DIR env var, then cwd.",
|
|
129
|
+
)
|
|
130
|
+
def status(project_dir):
|
|
131
|
+
"""Show BikeRack running state."""
|
|
132
|
+
from pennyfarthing_scripts.bikerack.launcher import get_status
|
|
133
|
+
|
|
134
|
+
if project_dir:
|
|
135
|
+
project_dir = Path(project_dir)
|
|
136
|
+
elif os.environ.get("CYCLIST_PROJECT_DIR"):
|
|
137
|
+
project_dir = Path(os.environ["CYCLIST_PROJECT_DIR"])
|
|
138
|
+
else:
|
|
139
|
+
project_dir = Path.cwd()
|
|
140
|
+
result = get_status(project_dir)
|
|
141
|
+
|
|
142
|
+
if result["running"]:
|
|
143
|
+
click.echo("BikeRack is running")
|
|
144
|
+
click.echo(f" PID: {result['pid']}")
|
|
145
|
+
click.echo(f" Port: {result['port']}")
|
|
146
|
+
click.echo(f" Dashboard: {result['dashboard']}")
|
|
147
|
+
else:
|
|
148
|
+
click.echo("BikeRack is not running")
|