@pennyfarthing/core 10.0.3 → 10.1.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 +9 -7
- package/package.json +7 -1
- package/packages/core/dist/cli/commands/cyclist.d.ts +5 -1
- package/packages/core/dist/cli/commands/cyclist.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/cyclist.js +4 -4
- package/packages/core/dist/cli/commands/cyclist.js.map +1 -1
- package/packages/core/dist/cli/commands/cyclist.test.js +2 -2
- package/packages/core/dist/cli/commands/cyclist.test.js.map +1 -1
- package/packages/core/dist/cli/commands/doctor-legacy.test.js +17 -16
- 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 +251 -4
- package/packages/core/dist/cli/commands/doctor.js.map +1 -1
- package/packages/core/dist/cli/commands/init.d.ts +7 -0
- package/packages/core/dist/cli/commands/init.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/init.js +43 -7
- package/packages/core/dist/cli/commands/init.js.map +1 -1
- package/packages/core/dist/cli/commands/update.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/update.js +26 -0
- package/packages/core/dist/cli/commands/update.js.map +1 -1
- package/packages/core/dist/cli/index.js +1 -1
- package/packages/core/dist/cli/index.js.map +1 -1
- package/packages/core/dist/cli/ocean-profiles.test.js +1 -1
- package/packages/core/dist/cli/ocean-profiles.test.js.map +1 -1
- package/packages/core/dist/cli/utils/files.d.ts +10 -0
- package/packages/core/dist/cli/utils/files.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/files.js +35 -0
- package/packages/core/dist/cli/utils/files.js.map +1 -1
- package/packages/core/dist/cli/utils/python.d.ts +22 -0
- package/packages/core/dist/cli/utils/python.d.ts.map +1 -0
- package/packages/core/dist/cli/utils/python.js +102 -0
- package/packages/core/dist/cli/utils/python.js.map +1 -0
- package/packages/core/dist/cli/utils/settings.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/settings.js +10 -0
- package/packages/core/dist/cli/utils/settings.js.map +1 -1
- package/packages/core/dist/scripts/generate-report.d.ts.map +1 -1
- package/packages/core/dist/scripts/generate-report.js +11 -7
- package/packages/core/dist/scripts/generate-report.js.map +1 -1
- package/packages/core/dist/scripts/generate-spider-report.d.ts.map +1 -1
- package/packages/core/dist/scripts/generate-spider-report.js +12 -8
- 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 +6 -4
- package/packages/core/dist/scripts/generate-spider.js.map +1 -1
- package/packages/core/dist/scripts/generate-spider.test.js +2 -2
- package/packages/core/dist/scripts/generate-spider.test.js.map +1 -1
- package/pennyfarthing-dist/agents/README.md +1 -3
- package/pennyfarthing-dist/agents/architect.md +0 -6
- package/pennyfarthing-dist/agents/devops.md +0 -6
- package/pennyfarthing-dist/agents/orchestrator.md +0 -6
- package/pennyfarthing-dist/agents/pm.md +1 -7
- package/pennyfarthing-dist/agents/sm-finish.md +1 -1
- package/pennyfarthing-dist/agents/sm-setup.md +2 -2
- package/pennyfarthing-dist/agents/sm.md +4 -11
- package/pennyfarthing-dist/commands/architect.md +11 -3
- package/pennyfarthing-dist/commands/close-epic.md +24 -131
- package/pennyfarthing-dist/commands/create-theme.md +14 -24
- package/pennyfarthing-dist/commands/dev.md +11 -3
- package/pennyfarthing-dist/commands/devops.md +11 -3
- package/pennyfarthing-dist/commands/health-check.md +1 -3
- package/pennyfarthing-dist/commands/help.md +8 -12
- package/pennyfarthing-dist/commands/list-themes.md +14 -16
- package/pennyfarthing-dist/commands/orchestrator.md +11 -3
- package/pennyfarthing-dist/commands/parallel-work.md +1 -3
- package/pennyfarthing-dist/commands/pm.md +11 -3
- package/pennyfarthing-dist/commands/prime.md +6 -6
- package/pennyfarthing-dist/commands/repo-status.md +2 -2
- package/pennyfarthing-dist/commands/reviewer.md +11 -3
- package/pennyfarthing-dist/commands/run-ci.md +1 -1
- package/pennyfarthing-dist/commands/set-theme.md +14 -51
- package/pennyfarthing-dist/commands/setup.md +1 -1
- package/pennyfarthing-dist/commands/show-theme.md +14 -16
- package/pennyfarthing-dist/commands/sm.md +11 -3
- package/pennyfarthing-dist/commands/sprint.md +8 -8
- package/pennyfarthing-dist/commands/tea.md +11 -3
- package/pennyfarthing-dist/commands/tech-writer.md +11 -3
- package/pennyfarthing-dist/commands/theme-maker.md +14 -671
- package/pennyfarthing-dist/commands/theme.md +95 -0
- package/pennyfarthing-dist/commands/ux-designer.md +11 -3
- package/pennyfarthing-dist/commands/work.md +3 -5
- package/pennyfarthing-dist/guides/agent-coordination.md +11 -13
- package/pennyfarthing-dist/guides/agent-template-tactical.md +2 -3
- package/pennyfarthing-dist/guides/command-tag-taxonomy.md +212 -0
- package/pennyfarthing-dist/guides/hooks.md +5 -5
- package/pennyfarthing-dist/guides/patterns/fan-out-fan-in-pattern.md +3 -3
- package/pennyfarthing-dist/guides/patterns/helper-delegation-pattern.md +9 -59
- package/pennyfarthing-dist/guides/patterns/tdd-flow-pattern.md +4 -5
- package/pennyfarthing-dist/guides/prime.md +2 -2
- package/pennyfarthing-dist/guides/skill-schema.md +25 -26
- package/pennyfarthing-dist/guides/xml-tags.md +2 -2
- package/pennyfarthing-dist/scripts/README.md +2 -2
- package/pennyfarthing-dist/scripts/core/agent-session.sh +6 -2
- package/pennyfarthing-dist/scripts/core/prime.sh +8 -10
- package/pennyfarthing-dist/scripts/git/git-status-all.sh +1 -1
- package/pennyfarthing-dist/scripts/git/install-git-hooks.sh +8 -6
- package/pennyfarthing-dist/scripts/git/worktree-manager.sh +3 -3
- package/pennyfarthing-dist/scripts/hooks/post-merge.sh +14 -12
- package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +4 -3
- package/pennyfarthing-dist/scripts/hooks/pre-push.sh +11 -5
- package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +1 -1
- package/pennyfarthing-dist/scripts/misc/README.md +1 -1
- package/pennyfarthing-dist/scripts/misc/repo-utils.sh +3 -3
- package/pennyfarthing-dist/scripts/misc/validate-subagent-frontmatter.sh +1 -2
- package/pennyfarthing-dist/scripts/sprint/README.md +32 -17
- package/pennyfarthing-dist/scripts/story/README.md +1 -1
- package/pennyfarthing-dist/scripts/test/test-setup.sh +1 -1
- package/pennyfarthing-dist/scripts/tests/handoff-phase-update.test.sh +5 -5
- package/pennyfarthing-dist/scripts/tests/test-drift-detection.sh +3 -79
- package/pennyfarthing-dist/scripts/theme/README.md +1 -1
- package/pennyfarthing-dist/scripts/validation/validate-agent-schema.sh +0 -1
- package/pennyfarthing-dist/scripts/workflow/finish-story.sh +62 -17
- package/pennyfarthing-dist/skills/dev-patterns/SKILL.md +2 -2
- package/pennyfarthing-dist/skills/skill-registry.yaml +41 -28
- package/pennyfarthing-dist/skills/sprint/skill.md +386 -68
- package/pennyfarthing-dist/skills/story/skill.md +14 -206
- package/pennyfarthing-dist/skills/theme/skill.md +290 -75
- package/pennyfarthing-dist/skills/theme-creation/SKILL.md +23 -166
- package/pennyfarthing-dist/skills/workflow/skill.md +4 -4
- package/pennyfarthing-dist/templates/agent-scopes.yaml.template +0 -11
- package/pennyfarthing-dist/templates/auto-load-sm.sh.template +14 -0
- package/pennyfarthing-dist/templates/settings.local.json.template +9 -0
- package/pennyfarthing-dist/workflows/2party-tdd.yaml +399 -0
- package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-05-import-to-future.md +42 -25
- package/pennyfarthing-dist/workflows/git-cleanup.yaml +1 -1
- package/pennyfarthing-dist/workflows/project-setup/steps/step-10-complete.md +1 -1
- package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/hooks.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/cli.py +15 -0
- package/pennyfarthing_scripts/codemarkers/__init__.py +19 -0
- package/pennyfarthing_scripts/codemarkers/__main__.py +6 -0
- package/pennyfarthing_scripts/codemarkers/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/codemarkers/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/codemarkers/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/codemarkers/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/codemarkers/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/codemarkers/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/codemarkers/analyze.py +326 -0
- package/pennyfarthing_scripts/codemarkers/cli.py +129 -0
- package/pennyfarthing_scripts/codemarkers/formatters.py +89 -0
- package/pennyfarthing_scripts/codemarkers/models.py +45 -0
- package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/themes.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/config.py +2 -1
- package/pennyfarthing_scripts/complexity/__init__.py +15 -0
- package/pennyfarthing_scripts/complexity/__main__.py +6 -0
- package/pennyfarthing_scripts/complexity/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/complexity/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/complexity/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/complexity/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/complexity/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/complexity/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/complexity/analyze.py +207 -0
- package/pennyfarthing_scripts/complexity/cli.py +78 -0
- package/pennyfarthing_scripts/complexity/formatters.py +64 -0
- package/pennyfarthing_scripts/complexity/models.py +32 -0
- package/pennyfarthing_scripts/deadcode/__init__.py +6 -0
- package/pennyfarthing_scripts/deadcode/__main__.py +6 -0
- package/pennyfarthing_scripts/deadcode/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/deadcode/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/deadcode/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/deadcode/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/deadcode/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/deadcode/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/deadcode/analyze.py +323 -0
- package/pennyfarthing_scripts/deadcode/cli.py +163 -0
- package/pennyfarthing_scripts/deadcode/formatters.py +106 -0
- package/pennyfarthing_scripts/deadcode/models.py +54 -0
- package/pennyfarthing_scripts/dependencies/__init__.py +20 -0
- package/pennyfarthing_scripts/dependencies/__main__.py +5 -0
- package/pennyfarthing_scripts/dependencies/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/dependencies/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/dependencies/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/dependencies/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/dependencies/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/dependencies/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/dependencies/analyze.py +155 -0
- package/pennyfarthing_scripts/dependencies/cli.py +72 -0
- package/pennyfarthing_scripts/dependencies/formatters.py +63 -0
- package/pennyfarthing_scripts/dependencies/models.py +39 -0
- package/pennyfarthing_scripts/healthscore/__init__.py +21 -0
- package/pennyfarthing_scripts/healthscore/__main__.py +6 -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 +161 -0
- package/pennyfarthing_scripts/healthscore/cli.py +76 -0
- package/pennyfarthing_scripts/healthscore/formatters.py +46 -0
- package/pennyfarthing_scripts/healthscore/models.py +44 -0
- 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 +28 -1
- package/pennyfarthing_scripts/hotspots/cli.py +11 -9
- 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__/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__/operations.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/reconcile.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/bidirectional.py +42 -15
- package/pennyfarthing_scripts/jira/cli.py +78 -1
- package/pennyfarthing_scripts/jira/client.py +28 -0
- package/pennyfarthing_scripts/prime/__pycache__/cli.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__/tiers.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/workflow.py +5 -3
- 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__/loader.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 +63 -6
- package/pennyfarthing_scripts/sprint/archive_epic.py +198 -85
- package/pennyfarthing_scripts/sprint/cli.py +1565 -65
- package/pennyfarthing_scripts/sprint/epic_add.py +173 -0
- package/pennyfarthing_scripts/sprint/loader.py +46 -2
- package/pennyfarthing_scripts/sprint/story_add.py +202 -27
- package/pennyfarthing_scripts/sprint/story_finish.py +211 -0
- package/pennyfarthing_scripts/sprint/validate_cmd.py +44 -5
- package/pennyfarthing_scripts/sprint/validator.py +13 -3
- package/pennyfarthing_scripts/sprint/work.py +43 -3
- package/pennyfarthing_scripts/sprint/yaml_io.py +124 -15
- package/pennyfarthing_scripts/tests/__pycache__/test_codemarkers.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_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_update.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_yaml_io.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/test_codemarkers.py +682 -0
- package/pennyfarthing_scripts/tests/test_healthscore.py +524 -0
- package/pennyfarthing_scripts/tests/test_sprint_package.py +166 -0
- package/pennyfarthing_scripts/tests/test_yaml_io.py +117 -0
- package/pennyfarthing_scripts/theme/__init__.py +5 -0
- package/pennyfarthing_scripts/theme/__main__.py +6 -0
- 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 +286 -0
- package/scripts/README.md +53 -0
- package/scripts/postinstall.cjs +34 -0
- package/pennyfarthing-dist/agents/workflow-status-check.md +0 -96
- package/pennyfarthing-dist/scripts/sprint/archive-story.sh +0 -133
- package/pennyfarthing-dist/scripts/sprint/available-stories.sh +0 -91
- package/pennyfarthing-dist/scripts/sprint/check-story.sh +0 -158
- package/pennyfarthing-dist/scripts/sprint/get-epic-field.sh +0 -52
- package/pennyfarthing-dist/scripts/sprint/get-story-field.sh +0 -63
- package/pennyfarthing-dist/scripts/sprint/list-future.sh +0 -145
- package/pennyfarthing-dist/scripts/sprint/new-sprint.sh +0 -110
- package/pennyfarthing-dist/scripts/sprint/promote-epic.sh +0 -148
- package/pennyfarthing-dist/scripts/sprint/sprint-common.sh +0 -415
- package/pennyfarthing-dist/scripts/sprint/sprint-info.sh +0 -33
- package/pennyfarthing-dist/scripts/sprint/sprint-metrics.sh +0 -230
- package/pennyfarthing-dist/scripts/sprint/sprint-status.sh +0 -134
- package/pennyfarthing-dist/scripts/sprint/validate-sprint-yaml.sh +0 -139
- package/pennyfarthing-dist/skills/sprint/scripts/archive-story.sh +0 -101
- package/pennyfarthing-dist/skills/sprint/scripts/available-stories.sh +0 -97
- package/pennyfarthing-dist/skills/sprint/scripts/check-story.sh +0 -164
- package/pennyfarthing-dist/skills/sprint/scripts/create-jira-epic.sh +0 -23
- package/pennyfarthing-dist/skills/sprint/scripts/new-sprint.sh +0 -116
- package/pennyfarthing-dist/skills/sprint/scripts/promote-epic.sh +0 -164
- package/pennyfarthing-dist/skills/sprint/scripts/sprint-info.sh +0 -39
- package/pennyfarthing-dist/skills/sprint/scripts/sprint-status.sh +0 -147
- package/pennyfarthing-dist/skills/sprint/scripts/sync-epic-jira.sh +0 -23
|
@@ -696,3 +696,120 @@ class TestReadSprint:
|
|
|
696
696
|
assert isinstance(data["epics"], list)
|
|
697
697
|
assert isinstance(data["epics"][0]["stories"], list)
|
|
698
698
|
assert isinstance(data["epics"][0]["stories"][0]["points"], int)
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
# =============================================================================
|
|
702
|
+
# Sharded format support
|
|
703
|
+
# =============================================================================
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
SHARDED_INDEX_YAML = """\
|
|
707
|
+
sprint:
|
|
708
|
+
name: "TO Sprint 2606"
|
|
709
|
+
jira_sprint_id: 309
|
|
710
|
+
jira_sprint_name: "TO Sprint 2606"
|
|
711
|
+
goal: Test sharding
|
|
712
|
+
start_date: 2026-02-02
|
|
713
|
+
end_date: 2026-02-15
|
|
714
|
+
status: active
|
|
715
|
+
epics:
|
|
716
|
+
- MSSCI-14298
|
|
717
|
+
- epic-40
|
|
718
|
+
stories: []
|
|
719
|
+
"""
|
|
720
|
+
|
|
721
|
+
SHARD_JIRA_YAML = """\
|
|
722
|
+
id: MSSCI-14298
|
|
723
|
+
type: epic
|
|
724
|
+
title: "Epic: Stepped Workflow"
|
|
725
|
+
priority: P1
|
|
726
|
+
status: in_progress
|
|
727
|
+
jira: MSSCI-14298
|
|
728
|
+
stories:
|
|
729
|
+
- id: MSSCI-14299
|
|
730
|
+
title: Wire up stepped workflow
|
|
731
|
+
points: 5
|
|
732
|
+
priority: P0
|
|
733
|
+
status: done
|
|
734
|
+
"""
|
|
735
|
+
|
|
736
|
+
SHARD_INTERNAL_YAML = """\
|
|
737
|
+
id: epic-40
|
|
738
|
+
type: epic
|
|
739
|
+
title: "Epic: Scale Adaptation"
|
|
740
|
+
priority: P2
|
|
741
|
+
status: backlog
|
|
742
|
+
stories:
|
|
743
|
+
- id: 40-1
|
|
744
|
+
title: First story
|
|
745
|
+
points: 3
|
|
746
|
+
priority: P1
|
|
747
|
+
status: backlog
|
|
748
|
+
"""
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
@pytest.fixture
|
|
752
|
+
def sharded_sprint_dir(tmp_path: Path) -> Path:
|
|
753
|
+
"""Create a sharded sprint directory structure."""
|
|
754
|
+
(tmp_path / "current-sprint.yaml").write_text(SHARDED_INDEX_YAML)
|
|
755
|
+
(tmp_path / "epic-MSSCI-14298.yaml").write_text(SHARD_JIRA_YAML)
|
|
756
|
+
(tmp_path / "epic-epic-40.yaml").write_text(SHARD_INTERNAL_YAML)
|
|
757
|
+
return tmp_path
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
class TestShardedReadWrite:
|
|
761
|
+
"""Tests for sharded epic format in yaml_io."""
|
|
762
|
+
|
|
763
|
+
def test_read_merges_shards(self, sharded_sprint_dir: Path) -> None:
|
|
764
|
+
"""read_sprint should merge shard files into full epics."""
|
|
765
|
+
data = read_sprint(sharded_sprint_dir / "current-sprint.yaml")
|
|
766
|
+
|
|
767
|
+
assert len(data["epics"]) == 2
|
|
768
|
+
assert data["epics"][0]["id"] == "MSSCI-14298"
|
|
769
|
+
assert data["epics"][1]["id"] == "epic-40"
|
|
770
|
+
assert len(data["epics"][0]["stories"]) == 1
|
|
771
|
+
assert len(data["epics"][1]["stories"]) == 1
|
|
772
|
+
|
|
773
|
+
def test_write_preserves_sharded_format(self, sharded_sprint_dir: Path) -> None:
|
|
774
|
+
"""write_sprint should write back to shard files when format is sharded."""
|
|
775
|
+
index_path = sharded_sprint_dir / "current-sprint.yaml"
|
|
776
|
+
data = read_sprint(index_path)
|
|
777
|
+
|
|
778
|
+
# Mutate a story
|
|
779
|
+
data["epics"][1]["stories"][0]["status"] = "in_progress"
|
|
780
|
+
|
|
781
|
+
write_sprint(index_path, data)
|
|
782
|
+
|
|
783
|
+
# Index should still have string refs
|
|
784
|
+
import yaml
|
|
785
|
+
with open(index_path) as f:
|
|
786
|
+
raw_index = yaml.safe_load(f)
|
|
787
|
+
assert isinstance(raw_index["epics"][0], str)
|
|
788
|
+
assert raw_index["epics"][0] == "MSSCI-14298"
|
|
789
|
+
|
|
790
|
+
# Shard file should have the updated story
|
|
791
|
+
shard = read_sprint(sharded_sprint_dir / "epic-epic-40.yaml")
|
|
792
|
+
assert shard["stories"][0]["status"] == "in_progress"
|
|
793
|
+
|
|
794
|
+
def test_sharded_round_trip(self, sharded_sprint_dir: Path) -> None:
|
|
795
|
+
"""Read-write-read on sharded format should be stable."""
|
|
796
|
+
index_path = sharded_sprint_dir / "current-sprint.yaml"
|
|
797
|
+
|
|
798
|
+
data1 = read_sprint(index_path)
|
|
799
|
+
write_sprint(index_path, data1)
|
|
800
|
+
data2 = read_sprint(index_path)
|
|
801
|
+
|
|
802
|
+
assert canonical_dump(data1) == canonical_dump(data2)
|
|
803
|
+
|
|
804
|
+
def test_non_sharded_write_unchanged(self, tmp_path: Path, full_sprint_file: Path) -> None:
|
|
805
|
+
"""write_sprint on non-sharded data should write a single file."""
|
|
806
|
+
data = read_sprint(full_sprint_file)
|
|
807
|
+
out_path = tmp_path / "output.yaml"
|
|
808
|
+
|
|
809
|
+
write_sprint(out_path, data)
|
|
810
|
+
|
|
811
|
+
# Should be a single file, no shard files created
|
|
812
|
+
import yaml
|
|
813
|
+
with open(out_path) as f:
|
|
814
|
+
raw = yaml.safe_load(f)
|
|
815
|
+
assert isinstance(raw["epics"][0], dict) # Full dicts, not refs
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI commands for theme management.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
pf theme list
|
|
6
|
+
pf theme show [NAME] [--full]
|
|
7
|
+
pf theme set <NAME>
|
|
8
|
+
pf theme create <NAME> [--base <THEME>] [--user]
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
|
|
15
|
+
import click
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@click.group()
|
|
19
|
+
def theme():
|
|
20
|
+
"""Persona theme management.
|
|
21
|
+
|
|
22
|
+
\b
|
|
23
|
+
Commands:
|
|
24
|
+
list - Show all available themes
|
|
25
|
+
show - Show theme details
|
|
26
|
+
set - Set the active theme
|
|
27
|
+
create - Create a new custom theme
|
|
28
|
+
"""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@theme.command("list")
|
|
33
|
+
def list_cmd():
|
|
34
|
+
"""Show all available themes with current theme highlighted."""
|
|
35
|
+
from pennyfarthing_scripts.common.themes import format_theme_list
|
|
36
|
+
|
|
37
|
+
click.echo(format_theme_list())
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@theme.command("show")
|
|
41
|
+
@click.argument("name", required=False)
|
|
42
|
+
@click.option("--full", is_flag=True, help="Show full agent details (OCEAN, quirks, catchphrases, etc.)")
|
|
43
|
+
def show(name: str | None, full: bool):
|
|
44
|
+
"""Show theme details including agent character mappings.
|
|
45
|
+
|
|
46
|
+
\b
|
|
47
|
+
Arguments:
|
|
48
|
+
NAME - Theme to show (defaults to current theme)
|
|
49
|
+
"""
|
|
50
|
+
import yaml
|
|
51
|
+
|
|
52
|
+
from pennyfarthing_scripts.common.themes import (
|
|
53
|
+
get_current_theme,
|
|
54
|
+
list_themes,
|
|
55
|
+
resolve_theme_path,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
theme_name = name
|
|
59
|
+
if not theme_name:
|
|
60
|
+
theme_name = get_current_theme()
|
|
61
|
+
if not theme_name:
|
|
62
|
+
click.echo("No theme currently set.")
|
|
63
|
+
click.echo("Use 'pf theme set <name>' to select a theme.")
|
|
64
|
+
return
|
|
65
|
+
|
|
66
|
+
theme_path = resolve_theme_path(theme_name)
|
|
67
|
+
if not theme_path:
|
|
68
|
+
available = ", ".join(list_themes()[:10])
|
|
69
|
+
raise click.ClickException(f"Theme '{theme_name}' not found.\nAvailable: {available}...")
|
|
70
|
+
|
|
71
|
+
data = yaml.safe_load(theme_path.read_text())
|
|
72
|
+
if not data:
|
|
73
|
+
raise click.ClickException(f"Invalid theme file: {theme_path}")
|
|
74
|
+
|
|
75
|
+
theme_meta = data.get("theme", {})
|
|
76
|
+
agents = data.get("agents", {})
|
|
77
|
+
|
|
78
|
+
# Header
|
|
79
|
+
click.echo(f"Theme: {theme_name}")
|
|
80
|
+
if theme_meta.get("description"):
|
|
81
|
+
click.echo(f"Description: {theme_meta['description']}")
|
|
82
|
+
if theme_meta.get("tier"):
|
|
83
|
+
click.echo(f"Tier: {theme_meta['tier']}")
|
|
84
|
+
click.echo()
|
|
85
|
+
|
|
86
|
+
# Agents
|
|
87
|
+
click.echo("Agents:")
|
|
88
|
+
agent_order = [
|
|
89
|
+
"sm", "tea", "dev", "reviewer", "orchestrator",
|
|
90
|
+
"pm", "architect", "devops", "tech-writer", "ux-designer",
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
displayed: set[str] = set()
|
|
94
|
+
for agent_name in agent_order:
|
|
95
|
+
agent = agents.get(agent_name)
|
|
96
|
+
if agent and agent.get("character"):
|
|
97
|
+
_display_agent(agent_name, agent, full)
|
|
98
|
+
displayed.add(agent_name)
|
|
99
|
+
|
|
100
|
+
for agent_name, agent in agents.items():
|
|
101
|
+
if agent_name not in displayed and isinstance(agent, dict) and agent.get("character"):
|
|
102
|
+
_display_agent(agent_name, agent, full)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _display_agent(name: str, agent: dict, full: bool) -> None:
|
|
106
|
+
"""Display a single agent's info."""
|
|
107
|
+
click.echo(f" {name}:")
|
|
108
|
+
click.echo(f" Character: {agent['character']}")
|
|
109
|
+
if agent.get("style"):
|
|
110
|
+
click.echo(f" Style: {agent['style']}")
|
|
111
|
+
|
|
112
|
+
if not full:
|
|
113
|
+
return
|
|
114
|
+
|
|
115
|
+
if agent.get("ocean"):
|
|
116
|
+
ocean = agent["ocean"]
|
|
117
|
+
click.echo(
|
|
118
|
+
f" OCEAN: O={ocean.get('O', '?')} C={ocean.get('C', '?')} "
|
|
119
|
+
f"E={ocean.get('E', '?')} A={ocean.get('A', '?')} N={ocean.get('N', '?')}"
|
|
120
|
+
)
|
|
121
|
+
if agent.get("trait"):
|
|
122
|
+
click.echo(f" Trait: {agent['trait']}")
|
|
123
|
+
if agent.get("expertise"):
|
|
124
|
+
click.echo(f" Expertise: {agent['expertise']}")
|
|
125
|
+
if agent.get("role"):
|
|
126
|
+
click.echo(f" Role: {agent['role']}")
|
|
127
|
+
if agent.get("quirks"):
|
|
128
|
+
click.echo(" Quirks:")
|
|
129
|
+
for q in agent["quirks"]:
|
|
130
|
+
click.echo(f" - {q}")
|
|
131
|
+
if agent.get("catchphrases"):
|
|
132
|
+
click.echo(" Catchphrases:")
|
|
133
|
+
for c in agent["catchphrases"]:
|
|
134
|
+
click.echo(f" - {c}")
|
|
135
|
+
if agent.get("emoji"):
|
|
136
|
+
click.echo(f" Emoji: {agent['emoji']}")
|
|
137
|
+
if agent.get("helper"):
|
|
138
|
+
helper = agent["helper"]
|
|
139
|
+
hname = helper.get("name", "?")
|
|
140
|
+
hstyle = helper.get("style", "?")
|
|
141
|
+
click.echo(f" Helper: {hname} ({hstyle})")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@theme.command("set")
|
|
145
|
+
@click.argument("name")
|
|
146
|
+
def set_theme(name: str):
|
|
147
|
+
"""Set the active persona theme.
|
|
148
|
+
|
|
149
|
+
\b
|
|
150
|
+
Arguments:
|
|
151
|
+
NAME - Theme name to activate
|
|
152
|
+
"""
|
|
153
|
+
import yaml
|
|
154
|
+
|
|
155
|
+
from pennyfarthing_scripts.common.config import get_project_root
|
|
156
|
+
from pennyfarthing_scripts.common.themes import (
|
|
157
|
+
list_themes,
|
|
158
|
+
resolve_theme_path,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
theme_path = resolve_theme_path(name)
|
|
162
|
+
if not theme_path:
|
|
163
|
+
available = ", ".join(list_themes()[:10])
|
|
164
|
+
raise click.ClickException(f"Theme '{name}' not found.\nAvailable: {available}...")
|
|
165
|
+
|
|
166
|
+
theme_data = yaml.safe_load(theme_path.read_text())
|
|
167
|
+
theme_meta = theme_data.get("theme", {})
|
|
168
|
+
agents = theme_data.get("agents", {})
|
|
169
|
+
|
|
170
|
+
# Build theme_characters map (matches Node.js setTheme behavior)
|
|
171
|
+
theme_characters: dict[str, str] = {}
|
|
172
|
+
for agent_id, agent_info in agents.items():
|
|
173
|
+
if isinstance(agent_info, dict) and agent_info.get("character"):
|
|
174
|
+
theme_characters[agent_id] = agent_info["character"]
|
|
175
|
+
|
|
176
|
+
# Read existing config, preserve other fields
|
|
177
|
+
root = get_project_root()
|
|
178
|
+
config_path = root / ".pennyfarthing" / "config.local.yaml"
|
|
179
|
+
|
|
180
|
+
config: dict = {}
|
|
181
|
+
if config_path.exists():
|
|
182
|
+
try:
|
|
183
|
+
existing = yaml.safe_load(config_path.read_text())
|
|
184
|
+
if existing and isinstance(existing, dict):
|
|
185
|
+
config = existing
|
|
186
|
+
except Exception:
|
|
187
|
+
pass
|
|
188
|
+
|
|
189
|
+
config["theme"] = name
|
|
190
|
+
config["theme_characters"] = theme_characters
|
|
191
|
+
|
|
192
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
193
|
+
header = (
|
|
194
|
+
"# Pennyfarthing Local Configuration\n"
|
|
195
|
+
"# This file is gitignored - your personal preferences\n\n"
|
|
196
|
+
)
|
|
197
|
+
config_path.write_text(
|
|
198
|
+
header + yaml.dump(config, default_flow_style=False, sort_keys=False)
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
click.echo(f"Theme changed to '{name}'.")
|
|
202
|
+
click.echo()
|
|
203
|
+
click.echo(f" {theme_meta.get('name', name)}")
|
|
204
|
+
|
|
205
|
+
samples = []
|
|
206
|
+
for key in ["sm", "tea", "dev"]:
|
|
207
|
+
agent = agents.get(key)
|
|
208
|
+
if agent and agent.get("character"):
|
|
209
|
+
samples.append(f"{key.upper()}: {agent['character']}")
|
|
210
|
+
if samples:
|
|
211
|
+
click.echo(f" {' | '.join(samples)}")
|
|
212
|
+
|
|
213
|
+
click.echo()
|
|
214
|
+
click.echo("Start a new agent session to use the new theme.")
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@theme.command("create")
|
|
218
|
+
@click.argument("name")
|
|
219
|
+
@click.option("--base", default=None, help="Base theme to copy from (defaults to current theme)")
|
|
220
|
+
@click.option("--user", is_flag=True, help="Create as user-level theme (~/.claude/pennyfarthing/themes/)")
|
|
221
|
+
def create(name: str, base: str | None, user: bool):
|
|
222
|
+
"""Create a new custom theme from a base theme.
|
|
223
|
+
|
|
224
|
+
\b
|
|
225
|
+
Arguments:
|
|
226
|
+
NAME - Name for the new theme (lowercase, hyphens allowed)
|
|
227
|
+
"""
|
|
228
|
+
import yaml
|
|
229
|
+
from pathlib import Path
|
|
230
|
+
|
|
231
|
+
from pennyfarthing_scripts.common.config import get_project_root
|
|
232
|
+
from pennyfarthing_scripts.common.themes import (
|
|
233
|
+
get_current_theme,
|
|
234
|
+
list_themes,
|
|
235
|
+
resolve_theme_path,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
if not base:
|
|
239
|
+
base = get_current_theme() or "blade-runner"
|
|
240
|
+
|
|
241
|
+
if name != name.lower():
|
|
242
|
+
raise click.ClickException("Theme name must be lowercase")
|
|
243
|
+
if " " in name:
|
|
244
|
+
raise click.ClickException("Theme name cannot contain spaces (use hyphens)")
|
|
245
|
+
if not re.match(r"^[a-z][a-z0-9-]*$", name):
|
|
246
|
+
raise click.ClickException(
|
|
247
|
+
"Theme name must start with a letter and contain only lowercase letters, numbers, and hyphens"
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
if name in list_themes():
|
|
251
|
+
raise click.ClickException(f"Theme '{name}' already exists")
|
|
252
|
+
|
|
253
|
+
base_path = resolve_theme_path(base)
|
|
254
|
+
if not base_path:
|
|
255
|
+
available = ", ".join(list_themes()[:10])
|
|
256
|
+
raise click.ClickException(f"Base theme '{base}' not found.\nAvailable: {available}...")
|
|
257
|
+
|
|
258
|
+
if user:
|
|
259
|
+
target_dir = Path.home() / ".claude" / "pennyfarthing" / "themes"
|
|
260
|
+
else:
|
|
261
|
+
root = get_project_root()
|
|
262
|
+
target_dir = root / ".claude" / "pennyfarthing" / "themes"
|
|
263
|
+
|
|
264
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
265
|
+
target_path = target_dir / f"{name}.yaml"
|
|
266
|
+
|
|
267
|
+
base_data = yaml.safe_load(base_path.read_text())
|
|
268
|
+
base_data["theme"]["name"] = name.replace("-", " ").title()
|
|
269
|
+
base_data["theme"]["description"] = f"Custom theme based on {base}"
|
|
270
|
+
|
|
271
|
+
file_header = (
|
|
272
|
+
f"# Custom Theme: {name}\n"
|
|
273
|
+
f"# Based on: {base}\n"
|
|
274
|
+
"# Edit this file to customize your agent personas\n\n"
|
|
275
|
+
)
|
|
276
|
+
target_path.write_text(
|
|
277
|
+
file_header + yaml.dump(base_data, default_flow_style=False, sort_keys=False)
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
click.echo(f"Created theme '{name}'.")
|
|
281
|
+
click.echo()
|
|
282
|
+
click.echo(f" File: {target_path}")
|
|
283
|
+
click.echo()
|
|
284
|
+
click.echo("Next steps:")
|
|
285
|
+
click.echo(f" 1. Edit the theme file to customize your agents")
|
|
286
|
+
click.echo(f" 2. Run 'pf theme set {name}' to activate")
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Meta Scripts
|
|
2
|
+
|
|
3
|
+
**These scripts are NOT distributed to users.** They are for Pennyfarthing framework development only.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
| Script | Purpose |
|
|
8
|
+
|--------|---------|
|
|
9
|
+
| `deploy.sh` | Release Pennyfarthing (version bump, tag, push, GitHub release) |
|
|
10
|
+
| `benchmark-runner.{sh,js}` | Run persona benchmarks |
|
|
11
|
+
| `job-fair-*.sh` | Job Fair character evaluations |
|
|
12
|
+
| `aggregate-benchmark-stats.{sh,js}` | Aggregate benchmark results |
|
|
13
|
+
| `solo-runner.sh` | Run single agent on a scenario |
|
|
14
|
+
| `parallel-benchmark.sh` | Parallel benchmark execution |
|
|
15
|
+
| `consolidate-job-fair.sh` | Consolidate Job Fair results |
|
|
16
|
+
| `convert-jobfair-to-benchmarks.sh` | Format conversion |
|
|
17
|
+
| `generate-leaderboard.sh` | Build leaderboard from benchmarks |
|
|
18
|
+
| `regenerate-summaries.sh` | Regenerate benchmark summaries |
|
|
19
|
+
| `cyclist-debug.mjs` | Debug Cyclist connection |
|
|
20
|
+
| `handoff-cli.{sh,js}` | Test handoff flow |
|
|
21
|
+
| `verify-visual-mapping.js` | Verify theme visual mappings |
|
|
22
|
+
| `migrate-assets-to-slug.sh` | One-time migration script |
|
|
23
|
+
| `resize-portraits.sh` | Resize portrait images |
|
|
24
|
+
| `resolve-portrait.mjs` | Portrait resolution logic |
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
Run from pennyfarthing repo root:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Release a new version
|
|
32
|
+
./scripts/deploy.sh --dry-run patch
|
|
33
|
+
./scripts/deploy.sh patch
|
|
34
|
+
|
|
35
|
+
# Run benchmarks
|
|
36
|
+
./scripts/benchmark-runner.sh --theme mash --agent sm
|
|
37
|
+
|
|
38
|
+
# Job Fair
|
|
39
|
+
./scripts/job-fair-runner.sh mash
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Where Should My Script Go?
|
|
43
|
+
|
|
44
|
+
**Put it here if:**
|
|
45
|
+
- It's for framework development/CI only
|
|
46
|
+
- Users should NOT have access to it
|
|
47
|
+
- It uses GPU/heavy dependencies (keep in meta, not distributed)
|
|
48
|
+
|
|
49
|
+
**Put it in `pennyfarthing-dist/scripts/` if:**
|
|
50
|
+
- Users need it for their workflows
|
|
51
|
+
- It's part of the sprint/story/jira tooling
|
|
52
|
+
|
|
53
|
+
See `CLAUDE.md` for the full decision tree.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Fix execute permissions on shell scripts after npm install.
|
|
3
|
+
// npm may strip +x bits depending on platform/version.
|
|
4
|
+
|
|
5
|
+
const { readdirSync, chmodSync, statSync } = require('fs');
|
|
6
|
+
const { join } = require('path');
|
|
7
|
+
|
|
8
|
+
const scriptsDir = join(__dirname, '..', 'pennyfarthing-dist', 'scripts');
|
|
9
|
+
|
|
10
|
+
function fixPermissions(dir) {
|
|
11
|
+
let entries;
|
|
12
|
+
try {
|
|
13
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
14
|
+
} catch {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
for (const entry of entries) {
|
|
18
|
+
const full = join(dir, entry.name);
|
|
19
|
+
if (entry.isDirectory()) {
|
|
20
|
+
fixPermissions(full);
|
|
21
|
+
} else if (entry.name.endsWith('.sh') && entry.name !== 'find-root.sh') {
|
|
22
|
+
try {
|
|
23
|
+
const stat = statSync(full);
|
|
24
|
+
if ((stat.mode & 0o111) === 0) {
|
|
25
|
+
chmodSync(full, 0o755);
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
// Skip files we can't chmod
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
fixPermissions(scriptsDir);
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: workflow-status-check
|
|
3
|
-
description: Determine workflow state using sprint scripts
|
|
4
|
-
tools: Bash, Read
|
|
5
|
-
model: haiku
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
<info>
|
|
9
|
-
Universal entry point telling agents: what work exists, what phase, and whether to activate.
|
|
10
|
-
Uses `/sprint` skill scripts for deterministic output.
|
|
11
|
-
</info>
|
|
12
|
-
|
|
13
|
-
<arguments>
|
|
14
|
-
| Argument | Required | Description |
|
|
15
|
-
|----------|----------|-------------|
|
|
16
|
-
| `CALLING_AGENT` | Yes | Agent requesting status check (e.g., "SM", "Architect", "PM") |
|
|
17
|
-
</arguments>
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Execution
|
|
22
|
-
|
|
23
|
-
Run the sprint status script and parse output:
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
.pennyfarthing/scripts/sprint/sprint-status.sh
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Then check for active sessions:
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
if ls .session/*-session.md 1>/dev/null 2>&1; then
|
|
33
|
-
for f in .session/*-session.md; do head -30 "$f"; done
|
|
34
|
-
else
|
|
35
|
-
echo "No active sessions"
|
|
36
|
-
fi
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## State Determination
|
|
42
|
-
|
|
43
|
-
| State | Condition |
|
|
44
|
-
|-------|-----------|
|
|
45
|
-
| `FINISH_STATE` | Session exists with Phase=approved OR Status=approved |
|
|
46
|
-
| `IN_PROGRESS_STATE` | Phased workflow session with active phase (setup/red/green/implement/review) |
|
|
47
|
-
| `STEPPED_WORKFLOW_STATE` | Stepped workflow session (workflow type = stepped) |
|
|
48
|
-
| `NEW_WORK_STATE` | No sessions AND sprint has backlog/ready stories |
|
|
49
|
-
| `EMPTY_BACKLOG_STATE` | No sessions AND sprint has NO backlog/ready stories |
|
|
50
|
-
|
|
51
|
-
**Detecting workflow type from session:**
|
|
52
|
-
```bash
|
|
53
|
-
# Read workflow name from session
|
|
54
|
-
WORKFLOW=$(grep '^\*\*Workflow:\*\*' .session/*-session.md | head -1 | sed 's/.*: //')
|
|
55
|
-
# Check if stepped
|
|
56
|
-
.pennyfarthing/scripts/workflow/get-workflow-type.sh "$WORKFLOW"
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
**Important:** Sprints are fixed two-week periods (kanban-style). Never suggest closing a sprint early or starting sprint planning when backlog is empty. The correct response to `EMPTY_BACKLOG_STATE` is to suggest promoting stories from `future.yaml`.
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
<output>
|
|
64
|
-
## Output Format
|
|
65
|
-
|
|
66
|
-
Return a `STATUS_CHECK_RESULT` block:
|
|
67
|
-
|
|
68
|
-
### Success
|
|
69
|
-
```
|
|
70
|
-
STATUS_CHECK_RESULT:
|
|
71
|
-
status: success
|
|
72
|
-
state: {FINISH_STATE|IN_PROGRESS_STATE|NEW_WORK_STATE|EMPTY_BACKLOG_STATE}
|
|
73
|
-
story_id: {ID or null}
|
|
74
|
-
phase: {current phase or null}
|
|
75
|
-
phase_owner: {agent name or null}
|
|
76
|
-
sprint_number: {N}
|
|
77
|
-
backlog_count: {N}
|
|
78
|
-
|
|
79
|
-
next_steps:
|
|
80
|
-
- FINISH_STATE: "Proceed to Finish Flow - spawn sm-finish with PHASE=preflight"
|
|
81
|
-
- IN_PROGRESS_STATE: "Report phase owner '{phase_owner}' should continue. Run handoff-marker.sh {phase_owner}"
|
|
82
|
-
- STEPPED_WORKFLOW_STATE: "Stepped workflow in progress. Tell user to run /workflow resume or /workflow status"
|
|
83
|
-
- NEW_WORK_STATE: "Present available stories to user. Await selection, then spawn sm-setup MODE=setup"
|
|
84
|
-
- EMPTY_BACKLOG_STATE: "Report backlog empty. Suggest promoting from future.yaml"
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### Active Session Details (if IN_PROGRESS_STATE)
|
|
88
|
-
```
|
|
89
|
-
session:
|
|
90
|
-
story_id: {ID}
|
|
91
|
-
title: "{title}"
|
|
92
|
-
workflow: {workflow}
|
|
93
|
-
phase: {phase}
|
|
94
|
-
branch: {branch}
|
|
95
|
-
```
|
|
96
|
-
</output>
|