@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
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
"""Workflow YAML schema validator adapter.
|
|
2
|
+
|
|
3
|
+
Validates workflow definition files in pennyfarthing-dist/workflows/.
|
|
4
|
+
Checks common fields, variant-specific structure (phased/stepped/procedural),
|
|
5
|
+
and cross-references agent names against agent definitions.
|
|
6
|
+
|
|
7
|
+
Story: MSSCI-14709 (91-11)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
import yaml
|
|
15
|
+
|
|
16
|
+
from pennyfarthing_scripts.validate import ValidateReport
|
|
17
|
+
|
|
18
|
+
# Known workflow types
|
|
19
|
+
VALID_TYPES = {"phased", "stepped", "procedural"}
|
|
20
|
+
|
|
21
|
+
# Known gate types for phased workflows
|
|
22
|
+
VALID_GATE_TYPES = {
|
|
23
|
+
"tests_pass",
|
|
24
|
+
"tests_fail",
|
|
25
|
+
"approval",
|
|
26
|
+
"manual",
|
|
27
|
+
"validation",
|
|
28
|
+
"design_review",
|
|
29
|
+
"quality_pass",
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def discover_workflow_files(workflows_dir: Path) -> list[Path]:
|
|
34
|
+
"""Discover all workflow YAML files.
|
|
35
|
+
|
|
36
|
+
Finds root-level *.yaml files and subdirectory workflow.yaml files.
|
|
37
|
+
Excludes non-workflow YAML (e.g., templates).
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Sorted list of Path objects for workflow YAML files.
|
|
41
|
+
"""
|
|
42
|
+
files: list[Path] = []
|
|
43
|
+
|
|
44
|
+
if not workflows_dir.is_dir():
|
|
45
|
+
return files
|
|
46
|
+
|
|
47
|
+
# Root-level *.yaml files
|
|
48
|
+
for f in sorted(workflows_dir.glob("*.yaml")):
|
|
49
|
+
files.append(f)
|
|
50
|
+
|
|
51
|
+
# Subdirectory workflow.yaml files
|
|
52
|
+
for f in sorted(workflows_dir.glob("*/workflow.yaml")):
|
|
53
|
+
files.append(f)
|
|
54
|
+
|
|
55
|
+
return files
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _get_agent_stems(agents_dir: Path) -> set[str]:
|
|
59
|
+
"""Get set of agent file stems from agents directory."""
|
|
60
|
+
if not agents_dir.is_dir():
|
|
61
|
+
return set()
|
|
62
|
+
return {f.stem for f in agents_dir.glob("*.md") if f.name != "README.md"}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _check_agent_ref(
|
|
66
|
+
agent_name: str, agent_stems: set[str], context: str
|
|
67
|
+
) -> list[str]:
|
|
68
|
+
"""Check if an agent reference exists. Returns warnings for unknown agents."""
|
|
69
|
+
warnings: list[str] = []
|
|
70
|
+
if agent_name and agent_name not in agent_stems:
|
|
71
|
+
warnings.append(
|
|
72
|
+
f"Agent '{agent_name}' referenced in {context} not found in agents/"
|
|
73
|
+
)
|
|
74
|
+
return warnings
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def validate_common(data: dict, path: Path) -> tuple[list[str], list[str]]:
|
|
78
|
+
"""Validate common fields present in all workflow types.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
(errors, warnings) — two lists of message strings.
|
|
82
|
+
"""
|
|
83
|
+
errors: list[str] = []
|
|
84
|
+
warnings: list[str] = []
|
|
85
|
+
|
|
86
|
+
if "name" not in data:
|
|
87
|
+
errors.append("Missing required field: name")
|
|
88
|
+
|
|
89
|
+
wtype = data.get("type")
|
|
90
|
+
if wtype is not None and wtype not in VALID_TYPES:
|
|
91
|
+
errors.append(
|
|
92
|
+
f"Invalid type '{wtype}' (must be one of: {', '.join(sorted(VALID_TYPES))})"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
if "description" not in data:
|
|
96
|
+
warnings.append("Missing recommended field: description")
|
|
97
|
+
|
|
98
|
+
return errors, warnings
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def validate_phased(
|
|
102
|
+
data: dict, path: Path, agents_dir: Path
|
|
103
|
+
) -> tuple[list[str], list[str]]:
|
|
104
|
+
"""Validate phased workflow structure.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
(errors, warnings) — two lists of message strings.
|
|
108
|
+
"""
|
|
109
|
+
errors: list[str] = []
|
|
110
|
+
warnings: list[str] = []
|
|
111
|
+
agent_stems = _get_agent_stems(agents_dir)
|
|
112
|
+
|
|
113
|
+
phases = data.get("phases")
|
|
114
|
+
|
|
115
|
+
if phases is None:
|
|
116
|
+
errors.append("Missing required field: phases")
|
|
117
|
+
return errors, warnings
|
|
118
|
+
|
|
119
|
+
if not isinstance(phases, list):
|
|
120
|
+
errors.append("Field 'phases' must be a list")
|
|
121
|
+
return errors, warnings
|
|
122
|
+
|
|
123
|
+
if len(phases) == 0:
|
|
124
|
+
errors.append("Field 'phases' must not be empty")
|
|
125
|
+
return errors, warnings
|
|
126
|
+
|
|
127
|
+
seen_names: set[str] = set()
|
|
128
|
+
|
|
129
|
+
for i, phase in enumerate(phases):
|
|
130
|
+
if not isinstance(phase, dict):
|
|
131
|
+
errors.append(f"Phase {i} must be a mapping")
|
|
132
|
+
continue
|
|
133
|
+
|
|
134
|
+
phase_name = phase.get("name")
|
|
135
|
+
if not phase_name:
|
|
136
|
+
errors.append(f"Phase {i} missing required field: name")
|
|
137
|
+
else:
|
|
138
|
+
if phase_name in seen_names:
|
|
139
|
+
warnings.append(f"Duplicate phase name: '{phase_name}'")
|
|
140
|
+
seen_names.add(phase_name)
|
|
141
|
+
|
|
142
|
+
agent = phase.get("agent")
|
|
143
|
+
if not agent:
|
|
144
|
+
errors.append(
|
|
145
|
+
f"Phase {i}{' (' + phase_name + ')' if phase_name else ''} "
|
|
146
|
+
f"missing required field: agent"
|
|
147
|
+
)
|
|
148
|
+
else:
|
|
149
|
+
warnings.extend(
|
|
150
|
+
_check_agent_ref(agent, agent_stems, f"phase '{phase_name or i}'")
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
# Gate validation
|
|
154
|
+
gate = phase.get("gate")
|
|
155
|
+
if gate and isinstance(gate, dict):
|
|
156
|
+
gate_type = gate.get("type")
|
|
157
|
+
if gate_type is None:
|
|
158
|
+
errors.append(
|
|
159
|
+
f"Phase '{phase_name or i}' gate missing required field: type"
|
|
160
|
+
)
|
|
161
|
+
elif gate_type not in VALID_GATE_TYPES:
|
|
162
|
+
warnings.append(
|
|
163
|
+
f"Phase '{phase_name or i}' has unknown gate type: "
|
|
164
|
+
f"'{gate_type}'"
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
return errors, warnings
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def validate_stepped(
|
|
171
|
+
data: dict, path: Path, agents_dir: Path
|
|
172
|
+
) -> tuple[list[str], list[str]]:
|
|
173
|
+
"""Validate stepped workflow structure.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
(errors, warnings) — two lists of message strings.
|
|
177
|
+
"""
|
|
178
|
+
errors: list[str] = []
|
|
179
|
+
warnings: list[str] = []
|
|
180
|
+
agent_stems = _get_agent_stems(agents_dir)
|
|
181
|
+
|
|
182
|
+
agent = data.get("agent")
|
|
183
|
+
if not agent:
|
|
184
|
+
errors.append("Missing required field: agent")
|
|
185
|
+
else:
|
|
186
|
+
warnings.extend(_check_agent_ref(agent, agent_stems, "workflow"))
|
|
187
|
+
|
|
188
|
+
steps = data.get("steps")
|
|
189
|
+
if steps is None:
|
|
190
|
+
errors.append("Missing required field: steps")
|
|
191
|
+
return errors, warnings
|
|
192
|
+
|
|
193
|
+
if not isinstance(steps, dict):
|
|
194
|
+
errors.append("Field 'steps' must be a mapping")
|
|
195
|
+
return errors, warnings
|
|
196
|
+
|
|
197
|
+
if "path" not in steps:
|
|
198
|
+
errors.append("Missing required field: steps.path")
|
|
199
|
+
|
|
200
|
+
if "pattern" not in steps:
|
|
201
|
+
errors.append("Missing required field: steps.pattern")
|
|
202
|
+
|
|
203
|
+
return errors, warnings
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def validate_procedural(
|
|
207
|
+
data: dict, path: Path, agents_dir: Path
|
|
208
|
+
) -> tuple[list[str], list[str]]:
|
|
209
|
+
"""Validate procedural workflow structure.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
(errors, warnings) — two lists of message strings.
|
|
213
|
+
"""
|
|
214
|
+
errors: list[str] = []
|
|
215
|
+
warnings: list[str] = []
|
|
216
|
+
agent_stems = _get_agent_stems(agents_dir)
|
|
217
|
+
|
|
218
|
+
agent = data.get("agent")
|
|
219
|
+
if not agent:
|
|
220
|
+
errors.append("Missing required field: agent")
|
|
221
|
+
else:
|
|
222
|
+
warnings.extend(_check_agent_ref(agent, agent_stems, "workflow"))
|
|
223
|
+
|
|
224
|
+
if "instructions" not in data and "checklist" not in data:
|
|
225
|
+
warnings.append(
|
|
226
|
+
"Missing recommended field: instructions or checklist"
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
return errors, warnings
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def run(root: Path, *, fix: bool = False, strict: bool = False) -> ValidateReport:
|
|
233
|
+
"""Validate all workflow definition files."""
|
|
234
|
+
report = ValidateReport(validator="workflow")
|
|
235
|
+
workflows_dir = root / "pennyfarthing-dist" / "workflows"
|
|
236
|
+
|
|
237
|
+
if not workflows_dir.is_dir():
|
|
238
|
+
report.details.append("[ERROR] workflows directory not found")
|
|
239
|
+
report.errors += 1
|
|
240
|
+
return report
|
|
241
|
+
|
|
242
|
+
agents_dir = root / "pennyfarthing-dist" / "agents"
|
|
243
|
+
files = discover_workflow_files(workflows_dir)
|
|
244
|
+
|
|
245
|
+
for path in files:
|
|
246
|
+
try:
|
|
247
|
+
content = path.read_text()
|
|
248
|
+
raw = yaml.safe_load(content)
|
|
249
|
+
except yaml.YAMLError:
|
|
250
|
+
report.errors += 1
|
|
251
|
+
report.details.append(f"[ERROR] {path.name}: YAML parse error")
|
|
252
|
+
continue
|
|
253
|
+
|
|
254
|
+
if not isinstance(raw, dict) or "workflow" not in raw:
|
|
255
|
+
report.errors += 1
|
|
256
|
+
report.details.append(
|
|
257
|
+
f"[ERROR] {path.name}: Missing 'workflow' top-level key"
|
|
258
|
+
)
|
|
259
|
+
continue
|
|
260
|
+
|
|
261
|
+
data = raw["workflow"]
|
|
262
|
+
|
|
263
|
+
if not isinstance(data, dict):
|
|
264
|
+
report.errors += 1
|
|
265
|
+
report.details.append(
|
|
266
|
+
f"[ERROR] {path.name}: 'workflow' must be a mapping"
|
|
267
|
+
)
|
|
268
|
+
continue
|
|
269
|
+
|
|
270
|
+
file_errors: list[str] = []
|
|
271
|
+
file_warnings: list[str] = []
|
|
272
|
+
|
|
273
|
+
# Common validation
|
|
274
|
+
common_errors, common_warnings = validate_common(data, path)
|
|
275
|
+
file_errors.extend(common_errors)
|
|
276
|
+
file_warnings.extend(common_warnings)
|
|
277
|
+
|
|
278
|
+
# Variant-specific validation
|
|
279
|
+
wtype = data.get("type", "phased")
|
|
280
|
+
|
|
281
|
+
if wtype == "phased":
|
|
282
|
+
variant_errors, variant_warnings = validate_phased(
|
|
283
|
+
data, path, agents_dir
|
|
284
|
+
)
|
|
285
|
+
elif wtype == "stepped":
|
|
286
|
+
variant_errors, variant_warnings = validate_stepped(
|
|
287
|
+
data, path, agents_dir
|
|
288
|
+
)
|
|
289
|
+
elif wtype == "procedural":
|
|
290
|
+
variant_errors, variant_warnings = validate_procedural(
|
|
291
|
+
data, path, agents_dir
|
|
292
|
+
)
|
|
293
|
+
else:
|
|
294
|
+
variant_errors, variant_warnings = [], []
|
|
295
|
+
|
|
296
|
+
file_errors.extend(variant_errors)
|
|
297
|
+
file_warnings.extend(variant_warnings)
|
|
298
|
+
|
|
299
|
+
# Determine display name (use parent/name for subdirectory workflows)
|
|
300
|
+
if path.name == "workflow.yaml":
|
|
301
|
+
display = f"{path.parent.name}/{path.name}"
|
|
302
|
+
else:
|
|
303
|
+
display = path.name
|
|
304
|
+
|
|
305
|
+
for e in file_errors:
|
|
306
|
+
report.errors += 1
|
|
307
|
+
report.details.append(f"[ERROR] {display}: {e}")
|
|
308
|
+
|
|
309
|
+
for w in file_warnings:
|
|
310
|
+
if strict:
|
|
311
|
+
report.errors += 1
|
|
312
|
+
report.details.append(f"[ERROR] {display}: {w}")
|
|
313
|
+
else:
|
|
314
|
+
report.warnings += 1
|
|
315
|
+
report.details.append(f"[WARN] {display}: {w}")
|
|
316
|
+
|
|
317
|
+
if not file_errors:
|
|
318
|
+
report.passed += 1
|
|
319
|
+
|
|
320
|
+
return report
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""Validate CLI — top-level validation command.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
pf validate # Run all validators
|
|
5
|
+
pf validate sprint # Sprint YAML only
|
|
6
|
+
pf validate schema # XML schema only
|
|
7
|
+
pf validate --fix # Auto-fix where supported
|
|
8
|
+
pf validate --strict # Treat warnings as errors
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import click
|
|
14
|
+
|
|
15
|
+
from pennyfarthing_scripts.common.config import get_project_root
|
|
16
|
+
from pennyfarthing_scripts.common.output import error, header, info, success, warn
|
|
17
|
+
from pennyfarthing_scripts.validate import ValidateReport
|
|
18
|
+
|
|
19
|
+
VALIDATORS = {
|
|
20
|
+
"sprint": "pennyfarthing_scripts.validate.adapters.sprint",
|
|
21
|
+
"schema": "pennyfarthing_scripts.validate.adapters.schema",
|
|
22
|
+
"agent": "pennyfarthing_scripts.validate.adapters.agent",
|
|
23
|
+
"workflow": "pennyfarthing_scripts.validate.adapters.workflow",
|
|
24
|
+
"skill-command": "pennyfarthing_scripts.validate.adapters.skill_command",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _run_validator(name: str, *, fix: bool, strict: bool) -> ValidateReport:
|
|
29
|
+
"""Run a single validator by name."""
|
|
30
|
+
import importlib
|
|
31
|
+
|
|
32
|
+
mod = importlib.import_module(VALIDATORS[name])
|
|
33
|
+
root = get_project_root()
|
|
34
|
+
return mod.run(root, fix=fix, strict=strict)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _print_reports(reports: list[ValidateReport]) -> None:
|
|
38
|
+
"""Print validation reports with color output."""
|
|
39
|
+
for report in reports:
|
|
40
|
+
header(f"Validator: {report.validator}", char="-", width=50)
|
|
41
|
+
|
|
42
|
+
if report.details:
|
|
43
|
+
for line in report.details:
|
|
44
|
+
if "[ERROR]" in line or "[SYNTAX]" in line or "[SCHEMA]" in line:
|
|
45
|
+
error(line)
|
|
46
|
+
elif "[WARN]" in line or "[FORMAT]" in line:
|
|
47
|
+
warn(line)
|
|
48
|
+
else:
|
|
49
|
+
click.echo(f" {line}", err=True)
|
|
50
|
+
|
|
51
|
+
parts = [f"{report.passed} passed"]
|
|
52
|
+
if report.warnings:
|
|
53
|
+
parts.append(f"{report.warnings} warnings")
|
|
54
|
+
if report.errors:
|
|
55
|
+
parts.append(f"{report.errors} errors")
|
|
56
|
+
if report.fixed:
|
|
57
|
+
parts.append("(fixed)")
|
|
58
|
+
|
|
59
|
+
summary_line = ", ".join(parts)
|
|
60
|
+
if report.success:
|
|
61
|
+
success(f"{report.validator}: {summary_line}")
|
|
62
|
+
else:
|
|
63
|
+
error(f"{report.validator}: {summary_line}")
|
|
64
|
+
|
|
65
|
+
click.echo("", err=True)
|
|
66
|
+
total_errors = sum(r.errors for r in reports)
|
|
67
|
+
total_warnings = sum(r.warnings for r in reports)
|
|
68
|
+
total_passed = sum(r.passed for r in reports)
|
|
69
|
+
|
|
70
|
+
info(f"Total: {total_passed} passed, {total_warnings} warnings, {total_errors} errors")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@click.group(invoke_without_command=True)
|
|
74
|
+
@click.option("--fix", is_flag=True, help="Auto-fix format issues where supported")
|
|
75
|
+
@click.option("--strict", is_flag=True, help="Treat warnings as errors")
|
|
76
|
+
@click.pass_context
|
|
77
|
+
def validate(ctx, fix: bool, strict: bool):
|
|
78
|
+
"""Run project validators.
|
|
79
|
+
|
|
80
|
+
\b
|
|
81
|
+
With no subcommand, runs ALL validators.
|
|
82
|
+
Specify a validator name to run only that one.
|
|
83
|
+
|
|
84
|
+
\b
|
|
85
|
+
Validators:
|
|
86
|
+
sprint - Sprint YAML (epics, initiatives, future, current-sprint)
|
|
87
|
+
schema - XML schema (sessions, skills, workflow steps)
|
|
88
|
+
agent - Agent definitions (required sections, model values, subagent refs)
|
|
89
|
+
workflow - Workflow definitions (phased/stepped/procedural structure)
|
|
90
|
+
"""
|
|
91
|
+
ctx.ensure_object(dict)
|
|
92
|
+
ctx.obj["fix"] = fix
|
|
93
|
+
ctx.obj["strict"] = strict
|
|
94
|
+
|
|
95
|
+
if ctx.invoked_subcommand is None:
|
|
96
|
+
reports = []
|
|
97
|
+
for name in VALIDATORS:
|
|
98
|
+
reports.append(_run_validator(name, fix=fix, strict=strict))
|
|
99
|
+
_print_reports(reports)
|
|
100
|
+
if any(not r.success for r in reports):
|
|
101
|
+
raise SystemExit(1)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@validate.command("sprint")
|
|
105
|
+
@click.pass_context
|
|
106
|
+
def validate_sprint(ctx):
|
|
107
|
+
"""Validate sprint YAML files (auto-discovers all files in sprint/)."""
|
|
108
|
+
report = _run_validator("sprint", fix=ctx.obj["fix"], strict=ctx.obj["strict"])
|
|
109
|
+
_print_reports([report])
|
|
110
|
+
if not report.success:
|
|
111
|
+
raise SystemExit(1)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@validate.command("schema")
|
|
115
|
+
@click.pass_context
|
|
116
|
+
def validate_schema(ctx):
|
|
117
|
+
"""Validate XML schema files (sessions, skills, workflow steps)."""
|
|
118
|
+
report = _run_validator("schema", fix=ctx.obj["fix"], strict=ctx.obj["strict"])
|
|
119
|
+
_print_reports([report])
|
|
120
|
+
if not report.success:
|
|
121
|
+
raise SystemExit(1)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@validate.command("agent")
|
|
125
|
+
@click.pass_context
|
|
126
|
+
def validate_agent(ctx):
|
|
127
|
+
"""Validate agent definition files (required sections, model values, refs)."""
|
|
128
|
+
report = _run_validator("agent", fix=ctx.obj["fix"], strict=ctx.obj["strict"])
|
|
129
|
+
_print_reports([report])
|
|
130
|
+
if not report.success:
|
|
131
|
+
raise SystemExit(1)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@validate.command("workflow")
|
|
135
|
+
@click.pass_context
|
|
136
|
+
def validate_workflow(ctx):
|
|
137
|
+
"""Validate workflow definitions (phased/stepped/procedural structure)."""
|
|
138
|
+
report = _run_validator("workflow", fix=ctx.obj["fix"], strict=ctx.obj["strict"])
|
|
139
|
+
_print_reports([report])
|
|
140
|
+
if not report.success:
|
|
141
|
+
raise SystemExit(1)
|
|
@@ -22,12 +22,11 @@ sys.path.insert(0, str(Path(__file__).parent))
|
|
|
22
22
|
|
|
23
23
|
from hooks import (
|
|
24
24
|
find_project_root,
|
|
25
|
-
send_to_cyclist,
|
|
26
|
-
load_settings,
|
|
27
25
|
is_cyclist_running,
|
|
26
|
+
load_settings,
|
|
27
|
+
send_to_cyclist,
|
|
28
28
|
)
|
|
29
29
|
|
|
30
|
-
|
|
31
30
|
# Once-per-session guard
|
|
32
31
|
_welcome_shown_file: Path | None = None
|
|
33
32
|
|
|
@@ -12,8 +12,8 @@ Story: MSSCI-12416 - Define Scale Levels
|
|
|
12
12
|
Epic: MSSCI-12415 - Scale Adaptation and Brownfield Support
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
from typing import Any
|
|
16
15
|
import re
|
|
16
|
+
from typing import Any
|
|
17
17
|
|
|
18
18
|
# Scale level definitions with keywords, thresholds, and metadata
|
|
19
19
|
SCALE_LEVELS: dict[int, dict[str, Any]] = {
|
|
@@ -32,7 +32,7 @@ SCALE_LEVELS: dict[int, dict[str, Any]] = {
|
|
|
32
32
|
"keywords": ["simple", "basic", "small", "add", "minor"],
|
|
33
33
|
"stories_min": 1,
|
|
34
34
|
"stories_max": 10,
|
|
35
|
-
"workflow": "
|
|
35
|
+
"workflow": "prd",
|
|
36
36
|
"artifacts": ["tech-spec"],
|
|
37
37
|
},
|
|
38
38
|
2: {
|
|
@@ -124,7 +124,7 @@ def get_workflow_for_scale_level(level: int) -> str:
|
|
|
124
124
|
level: Scale level 0-4
|
|
125
125
|
|
|
126
126
|
Returns:
|
|
127
|
-
Workflow name (trivial
|
|
127
|
+
Workflow name (trivial or prd)
|
|
128
128
|
"""
|
|
129
129
|
if level not in SCALE_LEVELS:
|
|
130
130
|
return "prd" # Safe default for unknown levels
|
package/scripts/README.md
CHANGED
|
@@ -7,21 +7,15 @@
|
|
|
7
7
|
| Script | Purpose |
|
|
8
8
|
|--------|---------|
|
|
9
9
|
| `deploy.sh` | Release Pennyfarthing (version bump, tag, push, GitHub release) |
|
|
10
|
-
| `benchmark-runner.{sh,js}` | Run persona benchmarks |
|
|
11
|
-
| `job-fair-*.sh` | Job Fair character evaluations |
|
|
12
|
-
| `aggregate-benchmark-stats.{sh,js}` | Aggregate benchmark results |
|
|
13
|
-
| `solo-runner.sh` | Run single agent on a scenario |
|
|
14
|
-
| `parallel-benchmark.sh` | Parallel benchmark execution |
|
|
15
|
-
| `consolidate-job-fair.sh` | Consolidate Job Fair results |
|
|
16
|
-
| `convert-jobfair-to-benchmarks.sh` | Format conversion |
|
|
17
|
-
| `generate-leaderboard.sh` | Build leaderboard from benchmarks |
|
|
18
|
-
| `regenerate-summaries.sh` | Regenerate benchmark summaries |
|
|
19
10
|
| `cyclist-debug.mjs` | Debug Cyclist connection |
|
|
20
11
|
| `handoff-cli.{sh,js}` | Test handoff flow |
|
|
21
12
|
| `verify-visual-mapping.js` | Verify theme visual mappings |
|
|
22
13
|
| `migrate-assets-to-slug.sh` | One-time migration script |
|
|
23
14
|
| `resize-portraits.sh` | Resize portrait images |
|
|
24
15
|
| `resolve-portrait.mjs` | Portrait resolution logic |
|
|
16
|
+
| `validate-refs.js` | Validate internal references |
|
|
17
|
+
|
|
18
|
+
> Benchmark scripts have been moved to `packages/benchmark/`.
|
|
25
19
|
|
|
26
20
|
## Usage
|
|
27
21
|
|
|
@@ -31,12 +25,6 @@ Run from pennyfarthing repo root:
|
|
|
31
25
|
# Release a new version
|
|
32
26
|
./scripts/deploy.sh --dry-run patch
|
|
33
27
|
./scripts/deploy.sh patch
|
|
34
|
-
|
|
35
|
-
# Run benchmarks
|
|
36
|
-
./scripts/benchmark-runner.sh --theme mash --agent sm
|
|
37
|
-
|
|
38
|
-
# Job Fair
|
|
39
|
-
./scripts/job-fair-runner.sh mash
|
|
40
28
|
```
|
|
41
29
|
|
|
42
30
|
## Where Should My Script Go?
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Create control baseline for a scenario (shortcut for /benchmark control <agent>)
|
|
3
|
-
argument-hint: <agent> [--scenario <name>] [--runs N]
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Benchmark Control
|
|
7
|
-
|
|
8
|
-
<purpose>
|
|
9
|
-
Shortcut to run `/benchmark` with the `control` theme. Creates or extends a control baseline for comparing other personas against.
|
|
10
|
-
|
|
11
|
-
This is equivalent to running:
|
|
12
|
-
```
|
|
13
|
-
/benchmark control <agent> [--scenario <name>] [--runs N]
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
Default: 10 runs for statistically meaningful baseline data.
|
|
17
|
-
</purpose>
|
|
18
|
-
|
|
19
|
-
<critical-integrity-requirements>
|
|
20
|
-
**See `/benchmark` for full integrity requirements.**
|
|
21
|
-
|
|
22
|
-
Baselines are saved to `internal/results/baselines/{scenario}/{agent}/` with:
|
|
23
|
-
- Individual runs in `runs/*.json` with proof-of-work
|
|
24
|
-
- Summary statistics in `summary.yaml` (mean, std_dev, CI)
|
|
25
|
-
- Timestamp validation (runs must take 30+ seconds each)
|
|
26
|
-
|
|
27
|
-
Control theme runs must include all proof fields. NO FABRICATION.
|
|
28
|
-
</critical-integrity-requirements>
|
|
29
|
-
|
|
30
|
-
<usage>
|
|
31
|
-
```
|
|
32
|
-
# Pick scenario interactively
|
|
33
|
-
/benchmark-control sm
|
|
34
|
-
/benchmark-control reviewer
|
|
35
|
-
|
|
36
|
-
# Specify scenario directly
|
|
37
|
-
/benchmark-control reviewer --scenario order-service
|
|
38
|
-
/benchmark-control dev --scenario tdd-shopping-cart --runs 15
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
**Arguments:**
|
|
42
|
-
- `agent` - The agent role (e.g., `sm`, `dev`, `reviewer`, `architect`)
|
|
43
|
-
- `--scenario` - (Optional) Scenario name. If omitted, shows matching scenarios.
|
|
44
|
-
- `--runs N` - Number of runs (default: 10 for baselines, max: 20)
|
|
45
|
-
</usage>
|
|
46
|
-
|
|
47
|
-
<on-invoke>
|
|
48
|
-
The user invoked this command with: $ARGUMENTS
|
|
49
|
-
|
|
50
|
-
**This is a shortcut.** Translate the arguments and invoke `/benchmark`:
|
|
51
|
-
|
|
52
|
-
1. Prepend `control` as the theme
|
|
53
|
-
2. Pass through all other arguments
|
|
54
|
-
|
|
55
|
-
**Examples:**
|
|
56
|
-
- `/benchmark-control sm` → `/benchmark control sm` (runs control:sm)
|
|
57
|
-
- `/benchmark-control reviewer --scenario order-service` → `/benchmark control reviewer --scenario order-service` (runs control:reviewer)
|
|
58
|
-
- `/benchmark-control dev --runs 15` → `/benchmark control dev --runs 15` (runs control:dev)
|
|
59
|
-
|
|
60
|
-
**Default runs override:** If `--runs` is not specified, default to 10 (instead of 4) since baselines need more data.
|
|
61
|
-
|
|
62
|
-
Now execute the equivalent `/benchmark` command with the translated arguments.
|
|
63
|
-
</on-invoke>
|
|
64
|
-
|
|
65
|
-
<reference>
|
|
66
|
-
- Main command: `.claude/project/commands/benchmark.md`
|
|
67
|
-
- Baselines location: `internal/results/baselines/{scenario}/{role}/`
|
|
68
|
-
- Results README: `internal/results/README.md`
|
|
69
|
-
</reference>
|