@pennyfarthing/core 10.1.0 → 10.2.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 +13 -18
- 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 +1 -1
- package/packages/core/dist/cli/commands/e2e-fresh-install.test.js.map +1 -1
- package/packages/core/dist/cli/commands/e2e-upgrade.test.js +1 -1
- 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/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/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/generate-spider-report.js.map +1 -1
- package/packages/core/dist/workflow/context-watch.d.ts +80 -0
- package/packages/core/dist/workflow/context-watch.d.ts.map +1 -0
- package/packages/core/dist/workflow/context-watch.js +235 -0
- package/packages/core/dist/workflow/context-watch.js.map +1 -0
- package/packages/core/dist/workflow/context-watch.test.d.ts +1 -0
- package/packages/core/dist/workflow/context-watch.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/context-watch.test.js +746 -0
- package/packages/core/dist/workflow/context-watch.test.js.map +1 -0
- 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/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 +718 -0
- package/packages/core/dist/workflow/tool-watch.test.js.map +1 -0
- 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/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/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/personas/themes/discworld.yaml +1 -1
- package/pennyfarthing-dist/personas/themes/fifth-element.yaml +295 -0
- package/pennyfarthing-dist/scripts/README.md +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/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/workflow/skill.md +24 -1
- package/pennyfarthing-dist/workflows/architecture/workflow.yaml +65 -0
- package/pennyfarthing-dist/workflows/bdd-tandem.yaml +70 -0
- package/pennyfarthing-dist/workflows/tdd-tandem.yaml +61 -0
- package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/bellmode_hook.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/config.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/hooks.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_bidirectional_sync.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_epic_creation.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_sync.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_sync_story.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/output.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/patch_mode.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/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/brownfield/__init__.py +6 -6
- package/pennyfarthing_scripts/brownfield/__main__.py +1 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/discover.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/cli.py +0 -1
- package/pennyfarthing_scripts/brownfield/discover.py +1 -2
- package/pennyfarthing_scripts/cli.py +11 -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/__pycache__/output.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/themes.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/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/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 +451 -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__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hotspots/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hotspots/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hotspots/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hotspots/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/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__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/create.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/operations.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/reconcile.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/story.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/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__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/finish.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/finish.py +0 -1
- package/pennyfarthing_scripts/pretooluse_hook.py +6 -7
- 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__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/session.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/tiers.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/cli.py +5 -1
- package/pennyfarthing_scripts/prime/loader.py +2 -3
- package/pennyfarthing_scripts/prime/persona.py +2 -1
- package/pennyfarthing_scripts/prime/tiers.py +4 -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__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/archive_epic.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/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__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/create.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/size.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/template.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/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__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_brownfield.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_cli_modules.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_codemarkers.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_common.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_git_utils.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_healthscore.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_jira_package.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_package_structure.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_patch_mode.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_prime.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_sprint_package.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_story_add.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_story_package.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_story_update.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_tiers.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_token_counting.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_validate_cmd.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_yaml_io.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/conftest.py +1 -2
- 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_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 +292 -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/hooks/__pycache__/question_reflector_check.cpython-314.pyc +0 -0
- 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
- package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/pretooluse_hook.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/sprint.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/workflow.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/compat.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/mappings.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_workflow_cli.cpython-314-pytest-9.0.2.pyc +0 -0
|
@@ -312,8 +312,8 @@ def story_add_command(
|
|
|
312
312
|
raise click.ClickException("POINTS is required")
|
|
313
313
|
try:
|
|
314
314
|
init_points = int(title)
|
|
315
|
-
except ValueError:
|
|
316
|
-
raise click.ClickException(f"POINTS must be an integer, got '{title}'")
|
|
315
|
+
except ValueError as err:
|
|
316
|
+
raise click.ClickException(f"POINTS must be an integer, got '{title}'") from err
|
|
317
317
|
|
|
318
318
|
result = add_initiative_story(
|
|
319
319
|
initiative_slug=initiative,
|
|
@@ -7,7 +7,7 @@ Steps:
|
|
|
7
7
|
1. Archive session file to sprint/archive/{jira-key}-session.md
|
|
8
8
|
2. Squash merge PR via gh (handle already-merged)
|
|
9
9
|
3. Transition Jira to Done
|
|
10
|
-
4. Update sprint YAML (status: done, completed date
|
|
10
|
+
4. Update sprint YAML (status: done, completed date)
|
|
11
11
|
5. Archive completed epics
|
|
12
12
|
6. Git cleanup (checkout develop, pull, delete local branch)
|
|
13
13
|
7. Remove session file
|
|
@@ -16,6 +16,7 @@ Steps:
|
|
|
16
16
|
import re
|
|
17
17
|
import shutil
|
|
18
18
|
import subprocess
|
|
19
|
+
import sys
|
|
19
20
|
from datetime import date
|
|
20
21
|
from pathlib import Path
|
|
21
22
|
from typing import Any
|
|
@@ -23,7 +24,6 @@ from typing import Any
|
|
|
23
24
|
from pennyfarthing_scripts.sprint.loader import find_epic, find_story
|
|
24
25
|
from pennyfarthing_scripts.sprint.yaml_io import read_sprint, write_sprint
|
|
25
26
|
|
|
26
|
-
|
|
27
27
|
SESSION_FIELD_RE = re.compile(r"\*\*(\w[\w\s]*):\*\*\s*(.*)")
|
|
28
28
|
|
|
29
29
|
|
|
@@ -175,8 +175,6 @@ def finish_story(
|
|
|
175
175
|
if story:
|
|
176
176
|
story["status"] = "done"
|
|
177
177
|
story["completed"] = today
|
|
178
|
-
if "assigned_to" in story:
|
|
179
|
-
del story["assigned_to"]
|
|
180
178
|
write_sprint(sprint_path, data)
|
|
181
179
|
steps.append({"step": 4, "action": "yaml_update", "status": "done", "completed": today})
|
|
182
180
|
else:
|
|
@@ -186,7 +184,7 @@ def finish_story(
|
|
|
186
184
|
|
|
187
185
|
# --- Step 5: Archive completed epics ---
|
|
188
186
|
result = _run(
|
|
189
|
-
[
|
|
187
|
+
[sys.executable, "-m", "pennyfarthing_scripts.cli", "sprint", "epic", "archive"],
|
|
190
188
|
cwd=str(project_root),
|
|
191
189
|
)
|
|
192
190
|
steps.append({"step": 5, "action": "archive_epics", "ran": True})
|
|
@@ -7,6 +7,7 @@ This module provides:
|
|
|
7
7
|
- story_update_command (Click command for CLI registration)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
+
import subprocess
|
|
10
11
|
from datetime import date
|
|
11
12
|
from pathlib import Path
|
|
12
13
|
from typing import Any
|
|
@@ -95,9 +96,6 @@ def update_story(
|
|
|
95
96
|
|
|
96
97
|
# Auto-cleanup rules
|
|
97
98
|
if status == "done":
|
|
98
|
-
# Remove assigned_to
|
|
99
|
-
if "assigned_to" in story:
|
|
100
|
-
del story["assigned_to"]
|
|
101
99
|
# Auto-set completed if not explicitly provided
|
|
102
100
|
if completed_date is None and "completed" not in story:
|
|
103
101
|
story["completed"] = date.today().isoformat()
|
|
@@ -108,6 +106,16 @@ def update_story(
|
|
|
108
106
|
# Auto-set started if not already present
|
|
109
107
|
if "started" not in story:
|
|
110
108
|
story["started"] = date.today().isoformat()
|
|
109
|
+
# Auto-set assignee from current Jira user if not already assigned
|
|
110
|
+
if "assigned_to" not in story and assigned_to is None:
|
|
111
|
+
try:
|
|
112
|
+
result = subprocess.run(
|
|
113
|
+
["jira", "me"], capture_output=True, text=True
|
|
114
|
+
)
|
|
115
|
+
if result.returncode == 0 and result.stdout.strip():
|
|
116
|
+
story["assigned_to"] = result.stdout.strip()
|
|
117
|
+
except Exception:
|
|
118
|
+
pass
|
|
111
119
|
|
|
112
120
|
# Validate after mutation
|
|
113
121
|
result = validate_full_sprint(data)
|
|
@@ -78,6 +78,10 @@ REQUIRED_STORY_FIELDS = {"id", "title", "status", "points"}
|
|
|
78
78
|
# Required fields for epic
|
|
79
79
|
REQUIRED_EPIC_FIELDS = {"id", "title"}
|
|
80
80
|
|
|
81
|
+
# Required fields for epic shard files (write-time validation, ADR-0022)
|
|
82
|
+
REQUIRED_EPIC_SHARD_FIELDS = {"id", "title", "status", "stories"}
|
|
83
|
+
REQUIRED_SHARD_STORY_FIELDS = {"id", "title", "points", "status"}
|
|
84
|
+
|
|
81
85
|
# Required fields for future.yaml initiative
|
|
82
86
|
REQUIRED_INITIATIVE_FIELDS = {"name", "status"}
|
|
83
87
|
|
|
@@ -235,6 +239,15 @@ def validate_epic(epic: dict[str, Any], all_story_ids: set[str], epic_index: int
|
|
|
235
239
|
f"{base_path}.{field_name}",
|
|
236
240
|
)
|
|
237
241
|
|
|
242
|
+
# Reject non-string IDs (YAML parses bare integers like `id: 87` as int,
|
|
243
|
+
# which crashes Cyclist's sprint-data.ts — epicId.match() fails on non-strings)
|
|
244
|
+
if "id" in epic and not isinstance(epic["id"], str):
|
|
245
|
+
result.add_error(
|
|
246
|
+
f"Epic ID must be a string, got {type(epic['id']).__name__} ({epic['id']!r}). "
|
|
247
|
+
"Quote it in YAML (e.g., id: \"87\" not id: 87)",
|
|
248
|
+
f"{base_path}.id",
|
|
249
|
+
)
|
|
250
|
+
|
|
238
251
|
# Validate stories if present
|
|
239
252
|
if "stories" in epic:
|
|
240
253
|
seen_in_epic: set[str] = set()
|
|
@@ -262,6 +275,95 @@ def validate_epic(epic: dict[str, Any], all_story_ids: set[str], epic_index: int
|
|
|
262
275
|
return result
|
|
263
276
|
|
|
264
277
|
|
|
278
|
+
def validate_epic_shard(epic: dict[str, Any]) -> ValidationResult:
|
|
279
|
+
"""Validate an epic shard dict before writing to disk.
|
|
280
|
+
|
|
281
|
+
Enforces stricter requirements than validate_epic() since shards
|
|
282
|
+
are standalone files that must be self-contained.
|
|
283
|
+
|
|
284
|
+
Validates:
|
|
285
|
+
- Required fields present (id, title, status, stories)
|
|
286
|
+
- stories is a list
|
|
287
|
+
- Each story has required fields (id, title, points, status)
|
|
288
|
+
- No duplicate story IDs within the epic
|
|
289
|
+
- jira key follows MSSCI-NNNNN pattern if present
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
epic: Epic shard dict to validate
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
ValidationResult with any errors found
|
|
296
|
+
"""
|
|
297
|
+
result = ValidationResult(valid=True)
|
|
298
|
+
|
|
299
|
+
# Check required shard fields
|
|
300
|
+
for field_name in REQUIRED_EPIC_SHARD_FIELDS:
|
|
301
|
+
if field_name not in epic:
|
|
302
|
+
result.add_error(
|
|
303
|
+
f"Missing required field: {field_name}",
|
|
304
|
+
f"epic.{field_name}",
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
# Reject non-string IDs (YAML parses bare integers like `id: 87` as int,
|
|
308
|
+
# which crashes Cyclist's sprint-data.ts checkEpicContext — epicId.match() fails)
|
|
309
|
+
if "id" in epic and not isinstance(epic["id"], str):
|
|
310
|
+
result.add_error(
|
|
311
|
+
f"Epic ID must be a string, got {type(epic['id']).__name__} ({epic['id']!r}). "
|
|
312
|
+
"Quote it in YAML (e.g., id: \"87\" not id: 87)",
|
|
313
|
+
"epic.id",
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
# Reject epic- prefix in ID (ADR-0022: reference prefix should not be baked into value)
|
|
317
|
+
if "id" in epic:
|
|
318
|
+
epic_id_val = str(epic["id"])
|
|
319
|
+
if epic_id_val.startswith("epic-"):
|
|
320
|
+
result.add_error(
|
|
321
|
+
f"Epic ID '{epic_id_val}' starts with 'epic-' prefix. "
|
|
322
|
+
"Use the numeric ID (e.g., '94' not 'epic-94')",
|
|
323
|
+
"epic.id",
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
# Validate jira key format if present
|
|
327
|
+
if "jira" in epic:
|
|
328
|
+
jira_key = str(epic["jira"])
|
|
329
|
+
if not JIRA_KEY_PATTERN.match(jira_key):
|
|
330
|
+
result.add_error(
|
|
331
|
+
f"Invalid Jira key format '{jira_key}'. Expected MSSCI-NNNNN",
|
|
332
|
+
"epic.jira",
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
# Validate stories field
|
|
336
|
+
if "stories" in epic:
|
|
337
|
+
stories = epic["stories"]
|
|
338
|
+
if not isinstance(stories, list):
|
|
339
|
+
result.add_error(
|
|
340
|
+
"'stories' must be a list",
|
|
341
|
+
"epic.stories",
|
|
342
|
+
)
|
|
343
|
+
else:
|
|
344
|
+
seen_ids: set[str] = set()
|
|
345
|
+
epic_id = epic.get("id", "epic")
|
|
346
|
+
for idx, story in enumerate(stories):
|
|
347
|
+
story_id = story.get("id")
|
|
348
|
+
if story_id:
|
|
349
|
+
if story_id in seen_ids:
|
|
350
|
+
result.add_error(
|
|
351
|
+
f"Duplicate story ID '{story_id}' within epic",
|
|
352
|
+
f"epic.stories[{idx}].id",
|
|
353
|
+
)
|
|
354
|
+
seen_ids.add(story_id)
|
|
355
|
+
|
|
356
|
+
# Check required story fields
|
|
357
|
+
for field_name in REQUIRED_SHARD_STORY_FIELDS:
|
|
358
|
+
if field_name not in story:
|
|
359
|
+
result.add_error(
|
|
360
|
+
f"Missing required field: {field_name}",
|
|
361
|
+
f"{epic_id}.stories[{idx}].{field_name}",
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
return result
|
|
365
|
+
|
|
366
|
+
|
|
265
367
|
def validate_full_sprint(data: dict[str, Any]) -> ValidationResult:
|
|
266
368
|
"""Validate complete sprint YAML including all epics and stories.
|
|
267
369
|
|
|
@@ -356,7 +458,7 @@ def validate_future(data: dict[str, Any]) -> ValidationResult:
|
|
|
356
458
|
if isinstance(initiative, str):
|
|
357
459
|
continue
|
|
358
460
|
if not isinstance(initiative, dict):
|
|
359
|
-
result.add_error(
|
|
461
|
+
result.add_error("Initiative must be a mapping", f"future.initiatives[{i}]")
|
|
360
462
|
continue
|
|
361
463
|
|
|
362
464
|
base_path = f"future.initiatives[{i}]"
|
|
@@ -433,13 +535,15 @@ def validate_future(data: dict[str, Any]) -> ValidationResult:
|
|
|
433
535
|
return result
|
|
434
536
|
|
|
435
537
|
|
|
436
|
-
def validate_sprint_file(file_path: Path) -> ValidationResult:
|
|
538
|
+
def validate_sprint_file(file_path: Path, *, strict: bool = False) -> ValidationResult:
|
|
437
539
|
"""Validate a sprint YAML file from disk.
|
|
438
540
|
|
|
439
|
-
Loads the file and validates its contents.
|
|
541
|
+
Loads the file and validates its contents. In strict mode, loader
|
|
542
|
+
warnings (e.g., unresolvable shard refs) are promoted to errors.
|
|
440
543
|
|
|
441
544
|
Args:
|
|
442
545
|
file_path: Path to sprint YAML file
|
|
546
|
+
strict: If True, treat loader warnings as validation errors
|
|
443
547
|
|
|
444
548
|
Returns:
|
|
445
549
|
ValidationResult with any errors (including load errors)
|
|
@@ -509,12 +613,22 @@ def validate_sprint_file(file_path: Path) -> ValidationResult:
|
|
|
509
613
|
)
|
|
510
614
|
return result
|
|
511
615
|
|
|
512
|
-
# Merge sharded epic files if present
|
|
616
|
+
# Merge sharded epic files if present, capturing warnings in strict mode
|
|
513
617
|
from pennyfarthing_scripts.sprint.loader import _merge_epic_shards
|
|
514
|
-
|
|
618
|
+
if strict:
|
|
619
|
+
import warnings as _warnings
|
|
620
|
+
with _warnings.catch_warnings(record=True) as caught:
|
|
621
|
+
_warnings.simplefilter("always")
|
|
622
|
+
data = _merge_epic_shards(data, file_path.parent)
|
|
623
|
+
for w in caught:
|
|
624
|
+
result.add_error(str(w.message), str(file_path))
|
|
625
|
+
else:
|
|
626
|
+
data = _merge_epic_shards(data, file_path.parent)
|
|
515
627
|
|
|
516
628
|
# Validate loaded data
|
|
517
|
-
|
|
629
|
+
full_result = validate_full_sprint(data)
|
|
630
|
+
result.merge(full_result)
|
|
631
|
+
return result
|
|
518
632
|
|
|
519
633
|
|
|
520
634
|
def format_validation_errors(result: ValidationResult) -> str:
|
|
@@ -7,11 +7,8 @@ Provides functions for starting and managing work on stories.
|
|
|
7
7
|
from typing import Any
|
|
8
8
|
|
|
9
9
|
from pennyfarthing_scripts.sprint.loader import (
|
|
10
|
-
find_epic,
|
|
11
|
-
find_story,
|
|
12
10
|
get_stories_by_status,
|
|
13
11
|
get_story_by_id,
|
|
14
|
-
load_sprint,
|
|
15
12
|
)
|
|
16
13
|
|
|
17
14
|
|
|
@@ -220,7 +217,7 @@ def main(args: list[str] | None = None) -> int:
|
|
|
220
217
|
print(f"Story: {story.get('id')}")
|
|
221
218
|
print(f"Title: {story.get('title')}")
|
|
222
219
|
print(f"Points: {story.get('points')}")
|
|
223
|
-
print(
|
|
220
|
+
print("Status: Available")
|
|
224
221
|
return 0
|
|
225
222
|
else:
|
|
226
223
|
print(f"Not available: {result.get('error') or result.get('reason')}", file=sys.stderr)
|
|
@@ -272,7 +272,9 @@ def canonical_dump(data: Any) -> str:
|
|
|
272
272
|
def _get_epic_ref(epic: Mapping) -> str:
|
|
273
273
|
"""Get the canonical reference ID for an epic shard file.
|
|
274
274
|
|
|
275
|
-
|
|
275
|
+
Priority: Jira key > numeric ID extracted from epic-N > raw ID.
|
|
276
|
+
Strips 'epic-' prefix from IDs to prevent double-prefix filenames
|
|
277
|
+
(e.g., epic-epic-94.yaml). See ADR-0022.
|
|
276
278
|
"""
|
|
277
279
|
jira = epic.get("jira")
|
|
278
280
|
epic_id = str(epic.get("id", ""))
|
|
@@ -281,7 +283,13 @@ def _get_epic_ref(epic: Mapping) -> str:
|
|
|
281
283
|
return str(jira)
|
|
282
284
|
if JIRA_PATTERN.match(epic_id):
|
|
283
285
|
return epic_id
|
|
284
|
-
|
|
286
|
+
|
|
287
|
+
# Strip epic- prefix to prevent double-prefix filenames
|
|
288
|
+
# e.g., "epic-94" -> "94" so file becomes "epic-94.yaml" not "epic-epic-94.yaml"
|
|
289
|
+
stripped = epic_id
|
|
290
|
+
while stripped.startswith("epic-"):
|
|
291
|
+
stripped = stripped[5:]
|
|
292
|
+
return stripped or epic_id
|
|
285
293
|
|
|
286
294
|
|
|
287
295
|
def _write_yaml_file(path: Path, data: Any) -> None:
|
|
@@ -15,6 +15,20 @@ Usage:
|
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
# Re-export common functions
|
|
18
|
+
# Import submodules
|
|
19
|
+
# CLI entry point - import module, not function, so "from story import cli" gets the module
|
|
20
|
+
from pennyfarthing_scripts.story import (
|
|
21
|
+
cli,
|
|
22
|
+
create,
|
|
23
|
+
size,
|
|
24
|
+
template,
|
|
25
|
+
)
|
|
26
|
+
from pennyfarthing_scripts.story.cli import main
|
|
27
|
+
from pennyfarthing_scripts.story.create import (
|
|
28
|
+
create_story,
|
|
29
|
+
generate_story_yaml,
|
|
30
|
+
validate_points,
|
|
31
|
+
)
|
|
18
32
|
from pennyfarthing_scripts.story.size import (
|
|
19
33
|
SIZING_GUIDELINES,
|
|
20
34
|
format_size_info,
|
|
@@ -26,22 +40,6 @@ from pennyfarthing_scripts.story.template import (
|
|
|
26
40
|
get_all_templates,
|
|
27
41
|
get_template,
|
|
28
42
|
)
|
|
29
|
-
from pennyfarthing_scripts.story.create import (
|
|
30
|
-
create_story,
|
|
31
|
-
generate_story_yaml,
|
|
32
|
-
validate_points,
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
# Import submodules
|
|
36
|
-
from pennyfarthing_scripts.story import (
|
|
37
|
-
create,
|
|
38
|
-
size,
|
|
39
|
-
template,
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
# CLI entry point - import module, not function, so "from story import cli" gets the module
|
|
43
|
-
from pennyfarthing_scripts.story import cli
|
|
44
|
-
from pennyfarthing_scripts.story.cli import main
|
|
45
43
|
|
|
46
44
|
__all__ = [
|
|
47
45
|
# Size
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -19,7 +19,6 @@ from dataclasses import dataclass, field
|
|
|
19
19
|
from pathlib import Path
|
|
20
20
|
from typing import Any
|
|
21
21
|
|
|
22
|
-
|
|
23
22
|
# Default cache location for SWE-bench data
|
|
24
23
|
DEFAULT_CACHE_PATH = "/tmp/swebench_all.json"
|
|
25
24
|
|
|
@@ -108,7 +107,7 @@ def load_swebench_data(cache_path: str | Path = DEFAULT_CACHE_PATH) -> list[dict
|
|
|
108
107
|
FileNotFoundError: If cache file doesn't exist
|
|
109
108
|
json.JSONDecodeError: If cache file is invalid JSON
|
|
110
109
|
"""
|
|
111
|
-
with open(cache_path
|
|
110
|
+
with open(cache_path) as f:
|
|
112
111
|
return json.load(f)
|
|
113
112
|
|
|
114
113
|
|
|
Binary file
|
|
Binary file
|
package/pennyfarthing_scripts/tests/__pycache__/test_brownfield.cpython-314-pytest-9.0.2.pyc
CHANGED
|
Binary file
|
package/pennyfarthing_scripts/tests/__pycache__/test_cli_modules.cpython-314-pytest-9.0.2.pyc
CHANGED
|
Binary file
|
package/pennyfarthing_scripts/tests/__pycache__/test_codemarkers.cpython-314-pytest-9.0.2.pyc
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/pennyfarthing_scripts/tests/__pycache__/test_healthscore.cpython-314-pytest-9.0.2.pyc
CHANGED
|
Binary file
|
package/pennyfarthing_scripts/tests/__pycache__/test_jira_package.cpython-314-pytest-9.0.2.pyc
CHANGED
|
Binary file
|
package/pennyfarthing_scripts/tests/__pycache__/test_package_structure.cpython-314-pytest-9.0.2.pyc
CHANGED
|
Binary file
|
package/pennyfarthing_scripts/tests/__pycache__/test_patch_mode.cpython-314-pytest-9.0.2.pyc
CHANGED
|
Binary file
|
|
Binary file
|
package/pennyfarthing_scripts/tests/__pycache__/test_sprint_package.cpython-314-pytest-9.0.2.pyc
CHANGED
|
Binary file
|
package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc
CHANGED
|
Binary file
|
|
Binary file
|
package/pennyfarthing_scripts/tests/__pycache__/test_story_package.cpython-314-pytest-9.0.2.pyc
CHANGED
|
Binary file
|
package/pennyfarthing_scripts/tests/__pycache__/test_story_update.cpython-314-pytest-9.0.2.pyc
CHANGED
|
Binary file
|
|
Binary file
|
package/pennyfarthing_scripts/tests/__pycache__/test_token_counting.cpython-314-pytest-9.0.2.pyc
CHANGED
|
Binary file
|
package/pennyfarthing_scripts/tests/__pycache__/test_validate_cmd.cpython-314-pytest-9.0.2.pyc
CHANGED
|
Binary file
|
package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -12,37 +12,34 @@ Tests verify:
|
|
|
12
12
|
7. CLI integration
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
import asyncio
|
|
16
15
|
import subprocess
|
|
17
16
|
import sys
|
|
18
17
|
import tempfile
|
|
18
|
+
from collections.abc import Generator
|
|
19
19
|
from pathlib import Path
|
|
20
|
-
from typing import Generator
|
|
21
|
-
from unittest.mock import MagicMock, patch
|
|
22
20
|
|
|
23
21
|
import pytest
|
|
24
22
|
|
|
25
23
|
from pennyfarthing_scripts.brownfield import (
|
|
26
24
|
DepthLevel,
|
|
27
|
-
ProjectType,
|
|
28
25
|
DiscoveryResult,
|
|
26
|
+
ProjectType,
|
|
27
|
+
detect_architecture_patterns,
|
|
29
28
|
detect_project_type,
|
|
30
29
|
detect_tech_stack,
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
discover,
|
|
31
|
+
generate_ai_guidance_doc,
|
|
33
32
|
generate_project_overview,
|
|
34
|
-
generate_tech_stack_doc,
|
|
35
33
|
generate_source_tree_doc,
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
generate_tech_stack_doc,
|
|
35
|
+
scan_directory_structure,
|
|
38
36
|
)
|
|
39
37
|
from pennyfarthing_scripts.brownfield.discover import (
|
|
40
|
-
TechStackItem,
|
|
41
|
-
DirectoryNode,
|
|
42
38
|
ArchitecturePattern,
|
|
39
|
+
DirectoryNode,
|
|
40
|
+
TechStackItem,
|
|
43
41
|
)
|
|
44
42
|
|
|
45
|
-
|
|
46
43
|
# =============================================================================
|
|
47
44
|
# FIXTURES
|
|
48
45
|
# =============================================================================
|
|
@@ -623,7 +620,7 @@ class TestDiscover:
|
|
|
623
620
|
output_dir = temp_project_dir / "output"
|
|
624
621
|
output_dir.mkdir()
|
|
625
622
|
|
|
626
|
-
|
|
623
|
+
await discover(node_project, output_dir=output_dir, depth=DepthLevel.DEEP)
|
|
627
624
|
|
|
628
625
|
expected_files = [
|
|
629
626
|
"project-overview.md",
|
|
@@ -9,16 +9,13 @@ Tests cover: models, analyze engine, CLI, formatters.
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
-
import asyncio
|
|
13
12
|
import json
|
|
14
13
|
from dataclasses import asdict
|
|
15
14
|
from pathlib import Path
|
|
16
|
-
from
|
|
17
|
-
from unittest.mock import AsyncMock, MagicMock, patch
|
|
15
|
+
from unittest.mock import AsyncMock, patch
|
|
18
16
|
|
|
19
17
|
import pytest
|
|
20
18
|
|
|
21
|
-
|
|
22
19
|
# ---------------------------------------------------------------------------
|
|
23
20
|
# Models
|
|
24
21
|
# ---------------------------------------------------------------------------
|
|
@@ -123,7 +120,9 @@ class TestCodeMarkersResultModel:
|
|
|
123
120
|
def test_json_serializable(self) -> None:
|
|
124
121
|
"""Full result serializes to JSON via asdict."""
|
|
125
122
|
from pennyfarthing_scripts.codemarkers.models import (
|
|
126
|
-
CodeMarker,
|
|
123
|
+
CodeMarker,
|
|
124
|
+
CodeMarkersResult,
|
|
125
|
+
MarkerSummary,
|
|
127
126
|
)
|
|
128
127
|
|
|
129
128
|
r = CodeMarkersResult(
|
|
@@ -534,7 +533,7 @@ class TestTableFormatter:
|
|
|
534
533
|
]
|
|
535
534
|
out = format_marker_table(markers, top_n=5)
|
|
536
535
|
# Header + separator + 5 data rows = 7 lines
|
|
537
|
-
lines = [
|
|
536
|
+
lines = [line for line in out.strip().split("\n") if line.strip()]
|
|
538
537
|
assert len(lines) <= 7
|
|
539
538
|
|
|
540
539
|
|
|
@@ -544,7 +543,9 @@ class TestJsonExport:
|
|
|
544
543
|
def test_valid_json(self) -> None:
|
|
545
544
|
from pennyfarthing_scripts.codemarkers.formatters import export_json
|
|
546
545
|
from pennyfarthing_scripts.codemarkers.models import (
|
|
547
|
-
CodeMarker,
|
|
546
|
+
CodeMarker,
|
|
547
|
+
CodeMarkersResult,
|
|
548
|
+
MarkerSummary,
|
|
548
549
|
)
|
|
549
550
|
|
|
550
551
|
r = CodeMarkersResult(
|
|
@@ -587,6 +588,7 @@ class TestCLI:
|
|
|
587
588
|
def test_cli_help(self) -> None:
|
|
588
589
|
"""codemarkers group shows help."""
|
|
589
590
|
from click.testing import CliRunner
|
|
591
|
+
|
|
590
592
|
from pennyfarthing_scripts.codemarkers.cli import codemarkers
|
|
591
593
|
|
|
592
594
|
runner = CliRunner()
|
|
@@ -597,6 +599,7 @@ class TestCLI:
|
|
|
597
599
|
def test_analyze_command_exists(self) -> None:
|
|
598
600
|
"""analyze subcommand is registered."""
|
|
599
601
|
from click.testing import CliRunner
|
|
602
|
+
|
|
600
603
|
from pennyfarthing_scripts.codemarkers.cli import codemarkers
|
|
601
604
|
|
|
602
605
|
runner = CliRunner()
|
|
@@ -608,6 +611,7 @@ class TestCLI:
|
|
|
608
611
|
def test_stale_command_exists(self) -> None:
|
|
609
612
|
"""stale subcommand is registered."""
|
|
610
613
|
from click.testing import CliRunner
|
|
614
|
+
|
|
611
615
|
from pennyfarthing_scripts.codemarkers.cli import codemarkers
|
|
612
616
|
|
|
613
617
|
runner = CliRunner()
|
|
@@ -617,6 +621,7 @@ class TestCLI:
|
|
|
617
621
|
def test_summary_command_exists(self) -> None:
|
|
618
622
|
"""summary subcommand is registered."""
|
|
619
623
|
from click.testing import CliRunner
|
|
624
|
+
|
|
620
625
|
from pennyfarthing_scripts.codemarkers.cli import codemarkers
|
|
621
626
|
|
|
622
627
|
runner = CliRunner()
|
|
@@ -626,6 +631,7 @@ class TestCLI:
|
|
|
626
631
|
def test_json_format_option(self) -> None:
|
|
627
632
|
"""--format json produces JSON output."""
|
|
628
633
|
from click.testing import CliRunner
|
|
634
|
+
|
|
629
635
|
from pennyfarthing_scripts.codemarkers.cli import codemarkers
|
|
630
636
|
|
|
631
637
|
runner = CliRunner()
|
|
@@ -672,7 +678,6 @@ class TestModuleExports:
|
|
|
672
678
|
from pennyfarthing_scripts.codemarkers import (
|
|
673
679
|
CodeMarker,
|
|
674
680
|
CodeMarkersResult,
|
|
675
|
-
MarkerSummary,
|
|
676
681
|
)
|
|
677
682
|
assert CodeMarker is not None
|
|
678
683
|
assert CodeMarkersResult is not None
|