@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,181 @@
|
|
|
1
|
+
"""BikeRack launcher — core lifecycle functions.
|
|
2
|
+
|
|
3
|
+
Story 101-5: BikeRack launcher CLI (pf bikerack start/stop/status)
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import atexit
|
|
9
|
+
import os
|
|
10
|
+
import signal
|
|
11
|
+
import subprocess
|
|
12
|
+
import time
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import NoReturn
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def is_process_alive(pid: int) -> bool:
|
|
18
|
+
"""Check if a process with given PID is alive."""
|
|
19
|
+
try:
|
|
20
|
+
os.kill(pid, 0)
|
|
21
|
+
return True
|
|
22
|
+
except (ProcessLookupError, PermissionError, OSError):
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def cleanup_files(project_dir: Path) -> None:
|
|
27
|
+
"""Clean up .bikerack-port and .bikerack-pid files."""
|
|
28
|
+
for name in (".bikerack-port", ".bikerack-pid"):
|
|
29
|
+
try:
|
|
30
|
+
(project_dir / name).unlink()
|
|
31
|
+
except FileNotFoundError:
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def read_port_file(project_dir: Path) -> int | None:
|
|
36
|
+
"""Read port from .bikerack-port file. Returns None if not found."""
|
|
37
|
+
try:
|
|
38
|
+
return int((project_dir / ".bikerack-port").read_text().strip())
|
|
39
|
+
except (FileNotFoundError, ValueError):
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def read_pid_file(project_dir: Path) -> int | None:
|
|
44
|
+
"""Read PID from .bikerack-pid file. Returns None if not found."""
|
|
45
|
+
try:
|
|
46
|
+
return int((project_dir / ".bikerack-pid").read_text().strip())
|
|
47
|
+
except (FileNotFoundError, ValueError):
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def write_pid_file(project_dir: Path, pid: int) -> None:
|
|
52
|
+
"""Write .bikerack-pid file."""
|
|
53
|
+
(project_dir / ".bikerack-pid").write_text(str(pid))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def build_otel_env(port: int) -> dict[str, str]:
|
|
57
|
+
"""Build the 5 OTEL environment variables from discovered port (Rule 5)."""
|
|
58
|
+
return {
|
|
59
|
+
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
|
|
60
|
+
"OTEL_LOGS_EXPORTER": "otlp",
|
|
61
|
+
"OTEL_METRICS_EXPORTER": "otlp",
|
|
62
|
+
"OTEL_EXPORTER_OTLP_PROTOCOL": "http/json",
|
|
63
|
+
"OTEL_EXPORTER_OTLP_ENDPOINT": f"http://localhost:{port}",
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _find_framework_dir() -> Path:
|
|
68
|
+
"""Locate the pennyfarthing framework root from this package's location."""
|
|
69
|
+
# pennyfarthing_scripts/bikerack/launcher.py -> pennyfarthing/
|
|
70
|
+
return Path(__file__).resolve().parent.parent.parent
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def start_wheelhub(project_dir: Path) -> subprocess.Popen:
|
|
74
|
+
"""Start WheelHub server in background with IS_BIKERACK=1."""
|
|
75
|
+
framework_dir = _find_framework_dir()
|
|
76
|
+
bikerack_entry = framework_dir / "packages" / "cyclist" / "dist" / "bikerack.js"
|
|
77
|
+
|
|
78
|
+
env = os.environ.copy()
|
|
79
|
+
env["IS_BIKERACK"] = "1"
|
|
80
|
+
env["CYCLIST_PROJECT_DIR"] = str(project_dir)
|
|
81
|
+
|
|
82
|
+
return subprocess.Popen(
|
|
83
|
+
["node", str(bikerack_entry)],
|
|
84
|
+
env=env,
|
|
85
|
+
cwd=str(project_dir),
|
|
86
|
+
stdout=subprocess.DEVNULL,
|
|
87
|
+
stderr=subprocess.DEVNULL,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def poll_for_port_file(
|
|
92
|
+
project_dir: Path, timeout: float = 5.0, interval: float = 0.1
|
|
93
|
+
) -> int:
|
|
94
|
+
"""Poll for .bikerack-port file, return port number."""
|
|
95
|
+
port_file = project_dir / ".bikerack-port"
|
|
96
|
+
deadline = time.monotonic() + timeout
|
|
97
|
+
|
|
98
|
+
while True:
|
|
99
|
+
if port_file.exists():
|
|
100
|
+
return int(port_file.read_text().strip())
|
|
101
|
+
if time.monotonic() >= deadline:
|
|
102
|
+
raise TimeoutError(
|
|
103
|
+
f"Timed out waiting for {port_file} after {timeout}s"
|
|
104
|
+
)
|
|
105
|
+
time.sleep(interval)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def register_cleanup(project_dir: Path, pid: int) -> None:
|
|
109
|
+
"""Register atexit handler to kill WheelHub and clean up files (Rule 8)."""
|
|
110
|
+
|
|
111
|
+
def _cleanup(project_dir: Path, pid: int) -> None:
|
|
112
|
+
try:
|
|
113
|
+
os.kill(pid, signal.SIGTERM)
|
|
114
|
+
except (ProcessLookupError, OSError):
|
|
115
|
+
pass
|
|
116
|
+
cleanup_files(project_dir)
|
|
117
|
+
|
|
118
|
+
atexit.register(_cleanup, project_dir, pid)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def exec_claude(otel_env: dict[str, str], project_dir: Path | None = None) -> NoReturn:
|
|
122
|
+
"""Replace current process with Claude CLI using os.execvpe (CE-4)."""
|
|
123
|
+
env = os.environ.copy()
|
|
124
|
+
env.update(otel_env)
|
|
125
|
+
if project_dir:
|
|
126
|
+
os.chdir(project_dir)
|
|
127
|
+
os.execvpe("claude", ["claude"], env)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def is_already_running(project_dir: Path) -> tuple[bool, int | None, int | None]:
|
|
131
|
+
"""Check if BikeRack is already running.
|
|
132
|
+
|
|
133
|
+
Returns (is_running, pid_or_none, port_or_none).
|
|
134
|
+
Cleans up stale files if PID is dead.
|
|
135
|
+
"""
|
|
136
|
+
pid = read_pid_file(project_dir)
|
|
137
|
+
port = read_port_file(project_dir)
|
|
138
|
+
|
|
139
|
+
if pid is None or port is None:
|
|
140
|
+
return (False, None, None)
|
|
141
|
+
|
|
142
|
+
if is_process_alive(pid):
|
|
143
|
+
return (True, pid, port)
|
|
144
|
+
|
|
145
|
+
cleanup_files(project_dir)
|
|
146
|
+
return (False, None, None)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def stop_bikerack(project_dir: Path) -> dict:
|
|
150
|
+
"""Stop running BikeRack instance. Returns {success, pid, message}."""
|
|
151
|
+
pid = read_pid_file(project_dir)
|
|
152
|
+
|
|
153
|
+
if pid is None:
|
|
154
|
+
return {"success": False, "message": "BikeRack is not running"}
|
|
155
|
+
|
|
156
|
+
if not is_process_alive(pid):
|
|
157
|
+
cleanup_files(project_dir)
|
|
158
|
+
return {"success": False, "message": "BikeRack is not running (stale PID)"}
|
|
159
|
+
|
|
160
|
+
os.kill(pid, signal.SIGTERM)
|
|
161
|
+
cleanup_files(project_dir)
|
|
162
|
+
return {"success": True, "pid": pid, "message": f"Stopped BikeRack (PID {pid})"}
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def get_status(project_dir: Path) -> dict:
|
|
166
|
+
"""Get BikeRack running status. Returns {running, pid, port, dashboard}."""
|
|
167
|
+
pid = read_pid_file(project_dir)
|
|
168
|
+
port = read_port_file(project_dir)
|
|
169
|
+
|
|
170
|
+
if pid is None or port is None:
|
|
171
|
+
return {"running": False}
|
|
172
|
+
|
|
173
|
+
if not is_process_alive(pid):
|
|
174
|
+
return {"running": False}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
"running": True,
|
|
178
|
+
"pid": pid,
|
|
179
|
+
"port": port,
|
|
180
|
+
"dashboard": f"http://localhost:{port}/bikerack",
|
|
181
|
+
}
|
|
@@ -6,17 +6,17 @@ Analyzes existing codebases and generates AI-ready documentation.
|
|
|
6
6
|
|
|
7
7
|
from pennyfarthing_scripts.brownfield.discover import (
|
|
8
8
|
DepthLevel,
|
|
9
|
-
ProjectType,
|
|
10
9
|
DiscoveryResult,
|
|
10
|
+
ProjectType,
|
|
11
|
+
detect_architecture_patterns,
|
|
11
12
|
detect_project_type,
|
|
12
13
|
detect_tech_stack,
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
discover,
|
|
15
|
+
generate_ai_guidance_doc,
|
|
15
16
|
generate_project_overview,
|
|
16
|
-
generate_tech_stack_doc,
|
|
17
17
|
generate_source_tree_doc,
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
generate_tech_stack_doc,
|
|
19
|
+
scan_directory_structure,
|
|
20
20
|
)
|
|
21
21
|
|
|
22
22
|
__all__ = [
|
|
@@ -11,7 +11,6 @@ import re
|
|
|
11
11
|
from dataclasses import dataclass, field
|
|
12
12
|
from enum import Enum
|
|
13
13
|
from pathlib import Path
|
|
14
|
-
from typing import Literal
|
|
15
14
|
|
|
16
15
|
# Try to import tomllib (Python 3.11+) or fall back to tomli
|
|
17
16
|
try:
|
|
@@ -642,7 +641,7 @@ def generate_ai_guidance_doc(result: DiscoveryResult) -> str:
|
|
|
642
641
|
|
|
643
642
|
if result.tech_stack:
|
|
644
643
|
# Get unique tech names
|
|
645
|
-
tech_names = sorted(
|
|
644
|
+
tech_names = sorted({item.name.lower() for item in result.tech_stack})[:10]
|
|
646
645
|
lines.extend([
|
|
647
646
|
"## Key Technologies",
|
|
648
647
|
"",
|
|
@@ -31,35 +31,45 @@ def cli():
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
# Import and register sprint group (lazy registration preserves startup time)
|
|
34
|
-
from pennyfarthing_scripts.sprint.cli import sprint
|
|
34
|
+
from pennyfarthing_scripts.sprint.cli import sprint # noqa: E402
|
|
35
35
|
|
|
36
36
|
cli.add_command(sprint)
|
|
37
37
|
|
|
38
38
|
# Import and register hotspots group
|
|
39
|
-
from pennyfarthing_scripts.hotspots.cli import hotspots
|
|
39
|
+
from pennyfarthing_scripts.hotspots.cli import hotspots # noqa: E402
|
|
40
40
|
|
|
41
41
|
cli.add_command(hotspots)
|
|
42
42
|
|
|
43
43
|
# Import and register jira group
|
|
44
|
-
from pennyfarthing_scripts.jira.cli import jira
|
|
44
|
+
from pennyfarthing_scripts.jira.cli import jira # noqa: E402
|
|
45
45
|
|
|
46
46
|
cli.add_command(jira)
|
|
47
47
|
|
|
48
48
|
# Import and register deadcode group
|
|
49
|
-
from pennyfarthing_scripts.deadcode.cli import deadcode
|
|
49
|
+
from pennyfarthing_scripts.deadcode.cli import deadcode # noqa: E402
|
|
50
50
|
|
|
51
51
|
cli.add_command(deadcode)
|
|
52
52
|
|
|
53
53
|
# Import and register theme group
|
|
54
|
-
from pennyfarthing_scripts.theme.cli import theme
|
|
54
|
+
from pennyfarthing_scripts.theme.cli import theme # noqa: E402
|
|
55
55
|
|
|
56
56
|
cli.add_command(theme)
|
|
57
57
|
|
|
58
58
|
# Import and register healthscore group
|
|
59
|
-
from pennyfarthing_scripts.healthscore.cli import healthscore
|
|
59
|
+
from pennyfarthing_scripts.healthscore.cli import healthscore # noqa: E402
|
|
60
60
|
|
|
61
61
|
cli.add_command(healthscore)
|
|
62
62
|
|
|
63
|
+
# Import and register validate group
|
|
64
|
+
from pennyfarthing_scripts.validate.cli import validate # noqa: E402
|
|
65
|
+
|
|
66
|
+
cli.add_command(validate)
|
|
67
|
+
|
|
68
|
+
# Import and register bikerack group
|
|
69
|
+
from pennyfarthing_scripts.bikerack.cli import bikerack # noqa: E402
|
|
70
|
+
|
|
71
|
+
cli.add_command(bikerack)
|
|
72
|
+
|
|
63
73
|
|
|
64
74
|
@cli.group()
|
|
65
75
|
def agent():
|
|
@@ -2,18 +2,22 @@
|
|
|
2
2
|
Code marker analysis — TODO, FIXME, HACK, XXX detection with git blame.
|
|
3
3
|
|
|
4
4
|
Story 80-1: Python codemarkers module.
|
|
5
|
+
Story 80-2: @deprecated detection and caller cross-reference.
|
|
5
6
|
"""
|
|
6
7
|
|
|
8
|
+
from pennyfarthing_scripts.codemarkers.analyze import analyze_deprecations, analyze_repo
|
|
7
9
|
from pennyfarthing_scripts.codemarkers.models import (
|
|
8
10
|
CodeMarker,
|
|
9
11
|
CodeMarkersResult,
|
|
12
|
+
DeprecationMarker,
|
|
10
13
|
MarkerSummary,
|
|
11
14
|
)
|
|
12
|
-
from pennyfarthing_scripts.codemarkers.analyze import analyze_repo
|
|
13
15
|
|
|
14
16
|
__all__ = [
|
|
15
17
|
"CodeMarker",
|
|
16
18
|
"CodeMarkersResult",
|
|
19
|
+
"DeprecationMarker",
|
|
17
20
|
"MarkerSummary",
|
|
21
|
+
"analyze_deprecations",
|
|
18
22
|
"analyze_repo",
|
|
19
23
|
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -12,12 +12,13 @@ import fnmatch
|
|
|
12
12
|
import re
|
|
13
13
|
import time
|
|
14
14
|
from collections import defaultdict
|
|
15
|
-
from datetime import
|
|
15
|
+
from datetime import UTC, datetime
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
|
|
18
18
|
from pennyfarthing_scripts.codemarkers.models import (
|
|
19
19
|
CodeMarker,
|
|
20
20
|
CodeMarkersResult,
|
|
21
|
+
DeprecationMarker,
|
|
21
22
|
MarkerSummary,
|
|
22
23
|
)
|
|
23
24
|
|
|
@@ -286,7 +287,7 @@ async def analyze_repo(
|
|
|
286
287
|
if author_time > 0:
|
|
287
288
|
age_days = (now - author_time) / 86400
|
|
288
289
|
date_str = datetime.fromtimestamp(
|
|
289
|
-
author_time, tz=
|
|
290
|
+
author_time, tz=UTC
|
|
290
291
|
).isoformat()
|
|
291
292
|
else:
|
|
292
293
|
age_days = 0.0
|
|
@@ -324,3 +325,177 @@ async def analyze_repo(
|
|
|
324
325
|
markers=markers,
|
|
325
326
|
summary=summary,
|
|
326
327
|
)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
# =============================================================================
|
|
331
|
+
# @deprecated detection (Story 80-2)
|
|
332
|
+
# =============================================================================
|
|
333
|
+
|
|
334
|
+
# File extensions to scan for @deprecated JSDoc tags
|
|
335
|
+
_DEPRECATION_EXTENSIONS = frozenset({".ts", ".tsx", ".js"})
|
|
336
|
+
|
|
337
|
+
# Pattern to find @deprecated in JSDoc comments
|
|
338
|
+
_DEPRECATED_PATTERN = re.compile(r"@deprecated\b(.*)")
|
|
339
|
+
|
|
340
|
+
# Pattern to extract symbol name from export declarations
|
|
341
|
+
_SYMBOL_PATTERN = re.compile(
|
|
342
|
+
r"export\s+(?:default\s+)?(?:function|class|const|let|var|interface|type|enum)\s+(\w+)"
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _grep_deprecations(root: Path, excludes: list[str]) -> list[dict]:
|
|
347
|
+
"""Scan TypeScript/JS files for @deprecated JSDoc tags.
|
|
348
|
+
|
|
349
|
+
For each @deprecated tag found, extracts the symbol name from the
|
|
350
|
+
next export declaration line following the JSDoc block.
|
|
351
|
+
|
|
352
|
+
Args:
|
|
353
|
+
root: Directory to scan recursively
|
|
354
|
+
excludes: Glob patterns for files/dirs to skip
|
|
355
|
+
|
|
356
|
+
Returns:
|
|
357
|
+
List of dicts: {path, line, symbol, text}
|
|
358
|
+
"""
|
|
359
|
+
results: list[dict] = []
|
|
360
|
+
|
|
361
|
+
for file_path in sorted(root.rglob("*")):
|
|
362
|
+
if not file_path.is_file():
|
|
363
|
+
continue
|
|
364
|
+
|
|
365
|
+
if file_path.suffix.lower() not in _DEPRECATION_EXTENSIONS:
|
|
366
|
+
continue
|
|
367
|
+
|
|
368
|
+
rel_path = str(file_path.relative_to(root))
|
|
369
|
+
|
|
370
|
+
if _should_exclude(rel_path, excludes):
|
|
371
|
+
continue
|
|
372
|
+
|
|
373
|
+
try:
|
|
374
|
+
content = file_path.read_text(encoding="utf-8", errors="strict")
|
|
375
|
+
except (UnicodeDecodeError, OSError):
|
|
376
|
+
continue
|
|
377
|
+
|
|
378
|
+
lines = content.split("\n")
|
|
379
|
+
for line_num, line_text in enumerate(lines, start=1):
|
|
380
|
+
match = _DEPRECATED_PATTERN.search(line_text)
|
|
381
|
+
if not match:
|
|
382
|
+
continue
|
|
383
|
+
|
|
384
|
+
# Extract the @deprecated text
|
|
385
|
+
deprecated_text = line_text.strip()
|
|
386
|
+
|
|
387
|
+
# Look ahead for the symbol declaration
|
|
388
|
+
symbol = ""
|
|
389
|
+
for ahead in lines[line_num:]: # line_num is already 1-indexed, so lines[line_num:] starts after current
|
|
390
|
+
sym_match = _SYMBOL_PATTERN.search(ahead)
|
|
391
|
+
if sym_match:
|
|
392
|
+
symbol = sym_match.group(1)
|
|
393
|
+
break
|
|
394
|
+
# Stop looking if we hit another JSDoc or a blank line after the block closes
|
|
395
|
+
stripped = ahead.strip()
|
|
396
|
+
if stripped and not stripped.startswith("*") and not stripped.startswith("/") and not stripped == "":
|
|
397
|
+
break
|
|
398
|
+
|
|
399
|
+
if symbol:
|
|
400
|
+
results.append({
|
|
401
|
+
"path": rel_path,
|
|
402
|
+
"line": line_num,
|
|
403
|
+
"symbol": symbol,
|
|
404
|
+
"text": deprecated_text,
|
|
405
|
+
})
|
|
406
|
+
|
|
407
|
+
return results
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def _count_callers(
|
|
411
|
+
symbol: str, root: Path, defining_file: str
|
|
412
|
+
) -> tuple[int, list[str]]:
|
|
413
|
+
"""Count files that import/reference a deprecated symbol.
|
|
414
|
+
|
|
415
|
+
Greps all TypeScript/JS files for the symbol name, excluding
|
|
416
|
+
the file that defines it.
|
|
417
|
+
|
|
418
|
+
Args:
|
|
419
|
+
symbol: The deprecated symbol name to search for
|
|
420
|
+
root: Directory to scan
|
|
421
|
+
defining_file: Relative path of the file defining the symbol (excluded)
|
|
422
|
+
|
|
423
|
+
Returns:
|
|
424
|
+
(caller_count, list_of_caller_paths)
|
|
425
|
+
"""
|
|
426
|
+
callers: list[str] = []
|
|
427
|
+
|
|
428
|
+
for file_path in sorted(root.rglob("*")):
|
|
429
|
+
if not file_path.is_file():
|
|
430
|
+
continue
|
|
431
|
+
|
|
432
|
+
if file_path.suffix.lower() not in _DEPRECATION_EXTENSIONS:
|
|
433
|
+
continue
|
|
434
|
+
|
|
435
|
+
rel_path = str(file_path.relative_to(root))
|
|
436
|
+
|
|
437
|
+
if rel_path == defining_file:
|
|
438
|
+
continue
|
|
439
|
+
|
|
440
|
+
try:
|
|
441
|
+
content = file_path.read_text(encoding="utf-8", errors="strict")
|
|
442
|
+
except (UnicodeDecodeError, OSError):
|
|
443
|
+
continue
|
|
444
|
+
|
|
445
|
+
if symbol in content:
|
|
446
|
+
callers.append(rel_path)
|
|
447
|
+
|
|
448
|
+
return len(callers), callers
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
async def analyze_deprecations(
|
|
452
|
+
path: Path,
|
|
453
|
+
excludes: list[str] | None = None,
|
|
454
|
+
) -> dict:
|
|
455
|
+
"""Analyze a directory for @deprecated symbols and their callers.
|
|
456
|
+
|
|
457
|
+
Args:
|
|
458
|
+
path: Directory to scan
|
|
459
|
+
excludes: Additional file patterns to exclude
|
|
460
|
+
|
|
461
|
+
Returns:
|
|
462
|
+
Dict with success, deprecations (list of DeprecationMarker),
|
|
463
|
+
summary (total_deprecations, deprecations_with_callers), and
|
|
464
|
+
optionally error.
|
|
465
|
+
"""
|
|
466
|
+
resolved = Path(path).resolve()
|
|
467
|
+
|
|
468
|
+
if not resolved.exists():
|
|
469
|
+
return {
|
|
470
|
+
"success": False,
|
|
471
|
+
"error": f"Path not found: {resolved}",
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
all_excludes = DEFAULT_EXCLUDES + (excludes or [])
|
|
475
|
+
|
|
476
|
+
raw = _grep_deprecations(resolved, all_excludes)
|
|
477
|
+
|
|
478
|
+
markers: list[DeprecationMarker] = []
|
|
479
|
+
for item in raw:
|
|
480
|
+
count, caller_list = _count_callers(
|
|
481
|
+
item["symbol"], resolved, item["path"]
|
|
482
|
+
)
|
|
483
|
+
markers.append(DeprecationMarker(
|
|
484
|
+
path=item["path"],
|
|
485
|
+
line=item["line"],
|
|
486
|
+
symbol=item["symbol"],
|
|
487
|
+
text=item["text"],
|
|
488
|
+
caller_count=count,
|
|
489
|
+
callers=caller_list,
|
|
490
|
+
))
|
|
491
|
+
|
|
492
|
+
with_callers = sum(1 for m in markers if m.caller_count > 0)
|
|
493
|
+
|
|
494
|
+
return {
|
|
495
|
+
"success": True,
|
|
496
|
+
"deprecations": markers,
|
|
497
|
+
"summary": {
|
|
498
|
+
"total_deprecations": len(markers),
|
|
499
|
+
"deprecations_with_callers": with_callers,
|
|
500
|
+
},
|
|
501
|
+
}
|
|
@@ -127,3 +127,53 @@ def summary(repo, repo_path, days, top, fmt, output_file, exclude):
|
|
|
127
127
|
else:
|
|
128
128
|
from pennyfarthing_scripts.codemarkers.formatters import format_summary
|
|
129
129
|
format_summary(result, file=click.get_text_stream("stdout"))
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _run_deprecation_analysis(repo_path, exclude):
|
|
133
|
+
"""Run deprecation analysis and return result dict."""
|
|
134
|
+
from pennyfarthing_scripts.codemarkers.analyze import analyze_deprecations
|
|
135
|
+
|
|
136
|
+
excludes = list(exclude) if exclude else None
|
|
137
|
+
if repo_path:
|
|
138
|
+
p = Path(repo_path).resolve()
|
|
139
|
+
else:
|
|
140
|
+
from pennyfarthing_scripts.common.config import get_project_root
|
|
141
|
+
p = get_project_root()
|
|
142
|
+
|
|
143
|
+
return asyncio.run(analyze_deprecations(p, excludes))
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@codemarkers.command()
|
|
147
|
+
@_common_options
|
|
148
|
+
def deprecations(repo, repo_path, days, top, fmt, output_file, exclude):
|
|
149
|
+
"""Scan for @deprecated symbols and cross-reference callers."""
|
|
150
|
+
result = _run_deprecation_analysis(repo_path, exclude)
|
|
151
|
+
|
|
152
|
+
if fmt == "json":
|
|
153
|
+
import json
|
|
154
|
+
from dataclasses import asdict
|
|
155
|
+
|
|
156
|
+
output = {
|
|
157
|
+
"success": result["success"],
|
|
158
|
+
"deprecations": [asdict(m) for m in result.get("deprecations", [])],
|
|
159
|
+
"summary": result.get("summary", {}),
|
|
160
|
+
}
|
|
161
|
+
text = json.dumps(output, indent=2)
|
|
162
|
+
else:
|
|
163
|
+
lines = []
|
|
164
|
+
deps = result.get("deprecations", [])
|
|
165
|
+
summary = result.get("summary", {})
|
|
166
|
+
lines.append(f"Total deprecations: {summary.get('total_deprecations', 0)}")
|
|
167
|
+
lines.append(f"With active callers: {summary.get('deprecations_with_callers', 0)}")
|
|
168
|
+
lines.append("")
|
|
169
|
+
for m in deps[:top]:
|
|
170
|
+
callers_str = f" ({m.caller_count} callers)" if m.caller_count else ""
|
|
171
|
+
lines.append(f" {m.symbol} @ {m.path}:{m.line}{callers_str}")
|
|
172
|
+
lines.append(f" {m.text}")
|
|
173
|
+
text = "\n".join(lines)
|
|
174
|
+
|
|
175
|
+
if output_file:
|
|
176
|
+
Path(output_file).write_text(text)
|
|
177
|
+
click.echo(f"Output written to {output_file}", err=True)
|
|
178
|
+
else:
|
|
179
|
+
click.echo(text)
|
|
@@ -43,3 +43,18 @@ class CodeMarkersResult:
|
|
|
43
43
|
markers: list[CodeMarker] = field(default_factory=list)
|
|
44
44
|
summary: MarkerSummary | None = None
|
|
45
45
|
error: str | None = None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class DeprecationMarker:
|
|
50
|
+
"""A deprecated symbol with caller cross-reference data (Story 80-2).
|
|
51
|
+
|
|
52
|
+
Stub: Dev will implement full functionality.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
path: str
|
|
56
|
+
line: int
|
|
57
|
+
symbol: str
|
|
58
|
+
text: str
|
|
59
|
+
caller_count: int = 0
|
|
60
|
+
callers: list[str] = field(default_factory=list)
|
|
@@ -5,8 +5,16 @@ This package provides shared utilities used across all CLI tools:
|
|
|
5
5
|
- config: Project configuration loading
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from pennyfarthing_scripts.common.config import (
|
|
9
|
+
find_project_root,
|
|
10
|
+
get_project_root,
|
|
11
|
+
load_pennyfarthing_config,
|
|
12
|
+
load_yaml_config,
|
|
13
|
+
)
|
|
8
14
|
from pennyfarthing_scripts.common.output import (
|
|
9
15
|
Colors,
|
|
16
|
+
_colorize,
|
|
17
|
+
_supports_color,
|
|
10
18
|
bold,
|
|
11
19
|
debug,
|
|
12
20
|
dim,
|
|
@@ -16,15 +24,6 @@ from pennyfarthing_scripts.common.output import (
|
|
|
16
24
|
info,
|
|
17
25
|
success,
|
|
18
26
|
warn,
|
|
19
|
-
_colorize,
|
|
20
|
-
_supports_color,
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
from pennyfarthing_scripts.common.config import (
|
|
24
|
-
find_project_root,
|
|
25
|
-
get_project_root,
|
|
26
|
-
load_pennyfarthing_config,
|
|
27
|
-
load_yaml_config,
|
|
28
27
|
)
|
|
29
28
|
|
|
30
29
|
__all__ = [
|
|
Binary file
|
|
Binary file
|
|
@@ -5,8 +5,8 @@ Wraps eslint with complexity rules to extract per-file metrics:
|
|
|
5
5
|
cyclomatic complexity, function length, nesting depth, and line counts.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
from pennyfarthing_scripts.complexity.models import FileComplexity, ComplexityResult
|
|
9
8
|
from pennyfarthing_scripts.complexity.analyze import analyze_complexity
|
|
9
|
+
from pennyfarthing_scripts.complexity.models import ComplexityResult, FileComplexity
|
|
10
10
|
|
|
11
11
|
__all__ = [
|
|
12
12
|
"FileComplexity",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|