@pennyfarthing/core 11.2.0 → 11.2.1
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 +1 -1
- package/package.json +2 -1
- package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/doctor.js +381 -66
- package/packages/core/dist/cli/commands/doctor.js.map +1 -1
- package/packages/core/dist/cli/commands/init.js +4 -4
- 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 +4 -5
- package/packages/core/dist/cli/commands/update.js.map +1 -1
- package/packages/core/dist/cli/utils/constants.d.ts +3 -8
- package/packages/core/dist/cli/utils/constants.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/constants.js +3 -4
- package/packages/core/dist/cli/utils/constants.js.map +1 -1
- package/packages/core/dist/cli/utils/settings.d.ts +11 -0
- package/packages/core/dist/cli/utils/settings.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/settings.js +65 -29
- package/packages/core/dist/cli/utils/settings.js.map +1 -1
- package/packages/core/dist/cli/utils/symlinks.js +16 -16
- package/packages/core/dist/cli/utils/symlinks.js.map +1 -1
- package/packages/core/dist/consultation/tandem-metrics.d.ts +91 -0
- package/packages/core/dist/consultation/tandem-metrics.d.ts.map +1 -0
- package/packages/core/dist/consultation/tandem-metrics.js +131 -0
- package/packages/core/dist/consultation/tandem-metrics.js.map +1 -0
- package/packages/core/dist/consultation/tandem-metrics.test.d.ts +18 -0
- package/packages/core/dist/consultation/tandem-metrics.test.d.ts.map +1 -0
- package/packages/core/dist/consultation/tandem-metrics.test.js +457 -0
- package/packages/core/dist/consultation/tandem-metrics.test.js.map +1 -0
- package/packages/core/dist/public/js/react/react.js +14 -14
- package/packages/core/dist/scripts/benchmark-integration.d.ts +182 -0
- package/packages/core/dist/scripts/benchmark-integration.d.ts.map +1 -0
- package/packages/core/dist/scripts/benchmark-integration.js +691 -0
- package/packages/core/dist/scripts/benchmark-integration.js.map +1 -0
- package/packages/core/dist/scripts/job-fair-aggregator.d.ts +150 -0
- package/packages/core/dist/scripts/job-fair-aggregator.d.ts.map +1 -0
- package/packages/core/dist/scripts/job-fair-aggregator.js +547 -0
- package/packages/core/dist/scripts/job-fair-aggregator.js.map +1 -0
- package/packages/core/dist/server/api/agent-load.js +1 -1
- package/packages/core/dist/server/api/agent-load.js.map +1 -1
- package/packages/core/dist/server/server.d.ts +0 -3
- package/packages/core/dist/server/server.d.ts.map +1 -1
- package/packages/core/dist/server/server.js +3 -37
- package/packages/core/dist/server/server.js.map +1 -1
- package/packages/core/dist/server/server.test.d.ts +1 -1
- package/packages/core/dist/server/server.test.js +12 -23
- package/packages/core/dist/server/server.test.js.map +1 -1
- package/packages/core/dist/shared/capabilities.d.ts +88 -0
- package/packages/core/dist/shared/capabilities.d.ts.map +1 -0
- package/packages/core/dist/shared/capabilities.js +133 -0
- package/packages/core/dist/shared/capabilities.js.map +1 -0
- package/packages/core/dist/shared/capabilities.test.d.ts +2 -0
- package/packages/core/dist/shared/capabilities.test.d.ts.map +1 -0
- package/packages/core/dist/shared/capabilities.test.js +217 -0
- package/packages/core/dist/shared/capabilities.test.js.map +1 -0
- package/packages/core/dist/shared/spawn-prompt.d.ts +47 -0
- package/packages/core/dist/shared/spawn-prompt.d.ts.map +1 -0
- package/packages/core/dist/shared/spawn-prompt.js +82 -0
- package/packages/core/dist/shared/spawn-prompt.js.map +1 -0
- package/packages/core/dist/shared/spawn-prompt.test.d.ts +2 -0
- package/packages/core/dist/shared/spawn-prompt.test.d.ts.map +1 -0
- package/packages/core/dist/shared/spawn-prompt.test.js +251 -0
- package/packages/core/dist/shared/spawn-prompt.test.js.map +1 -0
- package/packages/core/dist/workflow/tandem-workflow-templates.test.d.ts +18 -0
- package/packages/core/dist/workflow/tandem-workflow-templates.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/tandem-workflow-templates.test.js +434 -0
- package/packages/core/dist/workflow/tandem-workflow-templates.test.js.map +1 -0
- package/packages/core/dist/workflow/workflow-schema.d.ts +32 -0
- package/packages/core/dist/workflow/workflow-schema.d.ts.map +1 -1
- package/packages/core/dist/workflow/workflow-schema.js +120 -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 +570 -1
- package/packages/core/dist/workflow/workflow-schema.test.js.map +1 -1
- package/pennyfarthing-dist/agents/dev.md +6 -10
- package/pennyfarthing-dist/agents/reviewer.md +8 -2
- package/pennyfarthing-dist/agents/sm-finish.md +18 -1
- package/pennyfarthing-dist/commands/pf-git.md +4 -2
- package/pennyfarthing-dist/gates/approval.md +63 -0
- package/pennyfarthing-dist/gates/confidence-sm.md +71 -0
- package/pennyfarthing-dist/gates/context-ok.md +56 -0
- package/pennyfarthing-dist/gates/evaluations/confidence-sm.md +54 -0
- package/pennyfarthing-dist/gates/quality-pass.md +67 -0
- package/pennyfarthing-dist/gates/tests-fail.md +84 -0
- package/pennyfarthing-dist/gates/tests-pass.md +79 -0
- package/pennyfarthing-dist/guides/agent-behavior.md +23 -19
- package/pennyfarthing-dist/guides/bell-mode.md +1 -1
- package/pennyfarthing-dist/guides/hooks.md +28 -28
- package/pennyfarthing-dist/guides/reflector.md +1 -1
- package/pennyfarthing-dist/guides/tandem-protocol.md +3 -3
- package/pennyfarthing-dist/scripts/core/check-context.sh +2 -0
- package/pennyfarthing-dist/scripts/core/phase-check-start.sh +5 -87
- package/pennyfarthing-dist/scripts/hooks/README.md +5 -5
- package/pennyfarthing-dist/scripts/hooks/__pycache__/question_reflector_check.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +4 -183
- package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +4 -95
- package/pennyfarthing-dist/scripts/hooks/context-warning.sh +4 -65
- package/pennyfarthing-dist/scripts/hooks/cyclist-pretooluse-hook.sh +3 -31
- package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +27 -33
- package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +4 -71
- package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +3 -19
- package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +4 -30
- package/pennyfarthing-dist/scripts/hooks/session-start.sh +3 -32
- package/pennyfarthing-dist/scripts/hooks/session-stop.sh +4 -65
- package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +4 -78
- package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +4 -93
- package/pennyfarthing-dist/scripts/misc/README.md +1 -1
- package/pennyfarthing-dist/scripts/misc/statusline.sh +4 -301
- package/pennyfarthing-dist/templates/settings.local.json.template +19 -10
- package/pennyfarthing-dist/workflows/tdd.yaml +11 -2
- package/pennyfarthing_scripts/CLAUDE.md +19 -10
- package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/bellmode_hook.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/config.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/context.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/hooks.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_bidirectional_sync.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_epic_creation.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_sync.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_sync_story.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/output.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/patch_mode.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/pretooluse_hook.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/schema_validation_hook.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/session_start_hook.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/sprint.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/workflow.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/__pycache__/focus.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bellmode_hook.py +12 -296
- 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__/audit_log_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/background_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/changed_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/context_meter_footer.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/debug_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/diffs_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/events.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/git_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/portrait_resolver.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/progress_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/story_detail_data.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/story_detail_screen.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/ws_client.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/audit_log_panel.py +119 -0
- package/pennyfarthing_scripts/bikerack/base_panel.py +27 -4
- package/pennyfarthing_scripts/bikerack/changed_panel.py +96 -4
- package/pennyfarthing_scripts/bikerack/context_meter_footer.py +88 -0
- package/pennyfarthing_scripts/bikerack/debug_panel.py +1 -1
- package/pennyfarthing_scripts/bikerack/diffs_panel.py +30 -0
- package/pennyfarthing_scripts/bikerack/events.py +28 -0
- package/pennyfarthing_scripts/bikerack/portrait_resolver.py +139 -0
- package/pennyfarthing_scripts/bikerack/sprint_panel.py +373 -142
- package/pennyfarthing_scripts/bikerack/story_detail_data.py +244 -0
- package/pennyfarthing_scripts/bikerack/story_detail_screen.py +176 -0
- package/pennyfarthing_scripts/bikerack/tui.py +293 -61
- package/pennyfarthing_scripts/brownfield/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/discover.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/cli.py +5 -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/common/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/output.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/themes.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/pr_config.py +38 -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/consultation/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/consultation/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/consultation/__pycache__/dialogue_manager.cpython-314.pyc +0 -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/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/epic/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/epic/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git_group/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git_group/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/complete_phase.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/gate_file.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/gate_runner.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/marker.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/cli.py +33 -1
- package/pennyfarthing_scripts/handoff/complete_phase.py +28 -0
- package/pennyfarthing_scripts/handoff/marker.py +15 -15
- package/pennyfarthing_scripts/handoff/phase_check.py +96 -0
- package/pennyfarthing_scripts/handoff/resolve_gate.py +13 -1
- 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/hooks/__init__.py +437 -0
- package/pennyfarthing_scripts/hooks/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/bell_mode.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/context_breaker.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/context_warning.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/cyclist_pretooluse.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/pre_edit_check.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/reflector_check.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/schema_validation.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/session_start.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/session_stop.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/sprint_yaml_validation.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/statusline.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/bell_mode.py +215 -0
- package/pennyfarthing_scripts/hooks/cli.py +96 -0
- package/pennyfarthing_scripts/hooks/context_breaker.py +104 -0
- package/pennyfarthing_scripts/hooks/context_warning.py +66 -0
- package/pennyfarthing_scripts/hooks/cyclist_pretooluse.py +129 -0
- package/pennyfarthing_scripts/hooks/pre_edit_check.py +78 -0
- package/pennyfarthing_scripts/hooks/reflector_check.py +271 -0
- package/pennyfarthing_scripts/hooks/schema_validation.py +203 -0
- package/pennyfarthing_scripts/hooks/session_start.py +296 -0
- package/pennyfarthing_scripts/hooks/session_stop.py +111 -0
- package/pennyfarthing_scripts/hooks/sprint_yaml_validation.py +97 -0
- package/pennyfarthing_scripts/hooks/statusline.py +420 -0
- package/pennyfarthing_scripts/hooks.py +27 -432
- 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/jira/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/compat.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__/mappings.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/operations.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/reconcile.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/story.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/launch/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/launch/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/migration/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/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/preflight/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/finish.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/pretooluse_hook.py +3 -185
- package/pennyfarthing_scripts/prime/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/loader.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/session.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/tiers.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/version_sentinel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/workflow.py +2 -1
- package/pennyfarthing_scripts/schema_validation_hook.py +3 -298
- package/pennyfarthing_scripts/session/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/session/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/session_start_hook.py +4 -186
- package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/archive_epic.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/epic_update.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/story_update.py +19 -0
- package/pennyfarthing_scripts/story/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/create.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/size.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/template.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_108_1_gate_migration.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_archive_epic.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_bc.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_brownfield.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_cli_modules.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_cli_normalization.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_codemarkers.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_common.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_confidence_sm_evaluation.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_confidence_sm_gate.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_dialogue_manager.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_git_utils.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_handoff_cli.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_handoff_e2e.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_healthscore.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_jira_package.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_package_structure.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_patch_mode.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_prime.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_sprint_package.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_sprint_panel.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_story_add.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_story_package.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_story_update.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_tiers.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_token_counting.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_topology_loader.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_tui_focus.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_tui_panel_persistence.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_version_sentinel.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_workflow_cli.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_sprint_panel.py +344 -265
- 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/validate/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-314.pyc +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__/tandem_awareness.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/workflow.py +19 -0
- package/pennyfarthing_scripts/welcome_hook.py +3 -149
- package/pennyfarthing_scripts/workflow/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/helpers.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/scale.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/state.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/cli.py +7 -6
- package/pennyfarthing_scripts/workflow/team_lifecycle.py +257 -0
- package/packages/core/dist/scripts/theme-detail.test.d.ts +0 -10
- package/packages/core/dist/scripts/theme-detail.test.js +0 -199
- package/pennyfarthing_scripts/bikerack/__pycache__/portrait.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/gate/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/gate/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/gate/__pycache__/validate.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/hooks_installer.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/repos.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/worktree.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/heatmap.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_108_2_remove_handoff_fallback.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_gate_file_resolution.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_gate_runner.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_resolve_gate_file_field.cpython-314-pytest-9.0.2.pyc +0 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { detectTeamsCapability, isTeamsEnvVarSet, isInteractiveMode, getTeammateMode, checkTeamsCapability, resolvePhaseExecution, } from './capabilities.js';
|
|
4
|
+
describe('capabilities', () => {
|
|
5
|
+
// Save and restore env vars between tests
|
|
6
|
+
const savedEnv = {};
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
savedEnv.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS =
|
|
9
|
+
process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
|
|
10
|
+
});
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
if (savedEnv.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS !== undefined) {
|
|
13
|
+
process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS =
|
|
14
|
+
savedEnv.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
delete process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
// =========================================================================
|
|
21
|
+
// AC1: Check CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS environment variable
|
|
22
|
+
// =========================================================================
|
|
23
|
+
describe('isTeamsEnvVarSet', () => {
|
|
24
|
+
it('should return true when env var is set to "true"', () => {
|
|
25
|
+
process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = 'true';
|
|
26
|
+
assert.strictEqual(isTeamsEnvVarSet(), true);
|
|
27
|
+
});
|
|
28
|
+
it('should return true when env var is set to "1"', () => {
|
|
29
|
+
process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = '1';
|
|
30
|
+
assert.strictEqual(isTeamsEnvVarSet(), true);
|
|
31
|
+
});
|
|
32
|
+
it('should return false when env var is unset', () => {
|
|
33
|
+
delete process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
|
|
34
|
+
assert.strictEqual(isTeamsEnvVarSet(), false);
|
|
35
|
+
});
|
|
36
|
+
it('should return false when env var is "false"', () => {
|
|
37
|
+
process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = 'false';
|
|
38
|
+
assert.strictEqual(isTeamsEnvVarSet(), false);
|
|
39
|
+
});
|
|
40
|
+
it('should return false when env var is empty string', () => {
|
|
41
|
+
process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = '';
|
|
42
|
+
assert.strictEqual(isTeamsEnvVarSet(), false);
|
|
43
|
+
});
|
|
44
|
+
it('should return false when env var is "0"', () => {
|
|
45
|
+
process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = '0';
|
|
46
|
+
assert.strictEqual(isTeamsEnvVarSet(), false);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
// =========================================================================
|
|
50
|
+
// AC2: Detect interactive vs -p mode
|
|
51
|
+
// =========================================================================
|
|
52
|
+
describe('isInteractiveMode', () => {
|
|
53
|
+
it('should return a boolean', () => {
|
|
54
|
+
const result = isInteractiveMode();
|
|
55
|
+
assert.strictEqual(typeof result, 'boolean');
|
|
56
|
+
});
|
|
57
|
+
// Note: Full interactive mode testing requires mocking process.argv
|
|
58
|
+
// and TTY state. The implementation should check:
|
|
59
|
+
// - process.argv does not contain '-p'
|
|
60
|
+
// - process.stdin.isTTY is true
|
|
61
|
+
// These are environment-dependent and tested via integration tests.
|
|
62
|
+
// The unit test verifies the function exists and returns boolean.
|
|
63
|
+
});
|
|
64
|
+
// =========================================================================
|
|
65
|
+
// AC3: Detect teammateMode setting
|
|
66
|
+
// =========================================================================
|
|
67
|
+
describe('getTeammateMode', () => {
|
|
68
|
+
it('should return null when no config path is provided and no default exists', () => {
|
|
69
|
+
// With no config, should return null (not configured)
|
|
70
|
+
const result = getTeammateMode('/nonexistent/path/config.json');
|
|
71
|
+
assert.strictEqual(result, null);
|
|
72
|
+
});
|
|
73
|
+
it('should return "in-process" or "tmux" when teammateMode is configured', () => {
|
|
74
|
+
// This test validates the return type constraint
|
|
75
|
+
const result = getTeammateMode();
|
|
76
|
+
// Must be one of the valid values or null
|
|
77
|
+
assert.ok(result === null || result === 'in-process' || result === 'tmux', `Expected null, "in-process", or "tmux" but got: ${result}`);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
// =========================================================================
|
|
81
|
+
// AC4: Expose detection function returning result object
|
|
82
|
+
// =========================================================================
|
|
83
|
+
describe('detectTeamsCapability', () => {
|
|
84
|
+
it('should return a CapabilityResult with success field', () => {
|
|
85
|
+
const result = detectTeamsCapability();
|
|
86
|
+
assert.ok('success' in result, 'Result must have success field');
|
|
87
|
+
assert.strictEqual(typeof result.success, 'boolean');
|
|
88
|
+
});
|
|
89
|
+
it('should return data with all required fields when successful', () => {
|
|
90
|
+
process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = 'true';
|
|
91
|
+
const result = detectTeamsCapability();
|
|
92
|
+
assert.strictEqual(result.success, true, 'Should succeed when env var is set');
|
|
93
|
+
assert.ok(result.data, 'Should have data field on success');
|
|
94
|
+
const data = result.data;
|
|
95
|
+
assert.ok('teamsAvailable' in data, 'data must have teamsAvailable');
|
|
96
|
+
assert.ok('envVarSet' in data, 'data must have envVarSet');
|
|
97
|
+
assert.ok('isInteractive' in data, 'data must have isInteractive');
|
|
98
|
+
assert.ok('teammateMode' in data, 'data must have teammateMode');
|
|
99
|
+
});
|
|
100
|
+
it('should report envVarSet=true when env var is set', () => {
|
|
101
|
+
process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = 'true';
|
|
102
|
+
const result = detectTeamsCapability();
|
|
103
|
+
assert.strictEqual(result.success, true);
|
|
104
|
+
assert.strictEqual(result.data.envVarSet, true);
|
|
105
|
+
});
|
|
106
|
+
it('should report envVarSet=false when env var is unset', () => {
|
|
107
|
+
delete process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
|
|
108
|
+
const result = detectTeamsCapability();
|
|
109
|
+
// Should still succeed (detection itself works), but envVarSet=false
|
|
110
|
+
assert.strictEqual(result.success, true, 'Detection should always succeed');
|
|
111
|
+
assert.strictEqual(result.data.envVarSet, false);
|
|
112
|
+
});
|
|
113
|
+
it('should include reason when teams are unavailable', () => {
|
|
114
|
+
delete process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
|
|
115
|
+
const result = detectTeamsCapability();
|
|
116
|
+
assert.strictEqual(result.success, true);
|
|
117
|
+
assert.strictEqual(result.data.teamsAvailable, false);
|
|
118
|
+
assert.ok(result.data.reason, 'Should provide reason when teams unavailable');
|
|
119
|
+
assert.strictEqual(typeof result.data.reason, 'string');
|
|
120
|
+
});
|
|
121
|
+
it('should not have error field on successful detection', () => {
|
|
122
|
+
const result = detectTeamsCapability();
|
|
123
|
+
assert.strictEqual(result.success, true);
|
|
124
|
+
assert.strictEqual(result.error, undefined);
|
|
125
|
+
});
|
|
126
|
+
it('should detect teamsAvailable=false when env var is missing', () => {
|
|
127
|
+
delete process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
|
|
128
|
+
const result = detectTeamsCapability();
|
|
129
|
+
assert.strictEqual(result.data.teamsAvailable, false);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
// =========================================================================
|
|
133
|
+
// AC5: pennyfarthing doctor reports teams capability status
|
|
134
|
+
// =========================================================================
|
|
135
|
+
describe('checkTeamsCapability', () => {
|
|
136
|
+
it('should return an array of check results', () => {
|
|
137
|
+
const results = checkTeamsCapability();
|
|
138
|
+
assert.ok(Array.isArray(results), 'Should return an array');
|
|
139
|
+
assert.ok(results.length > 0, 'Should return at least one check result');
|
|
140
|
+
});
|
|
141
|
+
it('should return results with valid CheckResult shape', () => {
|
|
142
|
+
const results = checkTeamsCapability();
|
|
143
|
+
for (const result of results) {
|
|
144
|
+
assert.ok('name' in result, 'Each result must have name');
|
|
145
|
+
assert.ok('status' in result, 'Each result must have status');
|
|
146
|
+
assert.ok(['pass', 'warn', 'fail'].includes(result.status), `Status must be pass/warn/fail, got: ${result.status}`);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
it('should report pass when env var is set', () => {
|
|
150
|
+
process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = 'true';
|
|
151
|
+
const results = checkTeamsCapability();
|
|
152
|
+
const envCheck = results.find((r) => r.name.toLowerCase().includes('env') ||
|
|
153
|
+
r.name.toLowerCase().includes('teams'));
|
|
154
|
+
assert.ok(envCheck, 'Should have a check for teams env var');
|
|
155
|
+
assert.strictEqual(envCheck.status, 'pass');
|
|
156
|
+
});
|
|
157
|
+
it('should report warn when env var is not set', () => {
|
|
158
|
+
delete process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
|
|
159
|
+
const results = checkTeamsCapability();
|
|
160
|
+
const envCheck = results.find((r) => r.name.toLowerCase().includes('env') ||
|
|
161
|
+
r.name.toLowerCase().includes('teams'));
|
|
162
|
+
assert.ok(envCheck, 'Should have a check for teams env var');
|
|
163
|
+
assert.strictEqual(envCheck.status, 'warn', 'Missing env var should be a warning, not failure');
|
|
164
|
+
});
|
|
165
|
+
it('should include detail message in each result', () => {
|
|
166
|
+
const results = checkTeamsCapability();
|
|
167
|
+
for (const result of results) {
|
|
168
|
+
assert.ok(result.detail, `Check "${result.name}" should have a detail message`);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
// =========================================================================
|
|
173
|
+
// AC6: Graceful degradation for phases with team: block
|
|
174
|
+
// =========================================================================
|
|
175
|
+
describe('resolvePhaseExecution', () => {
|
|
176
|
+
it('should return team mode when teams are available and phase has team block', () => {
|
|
177
|
+
process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = 'true';
|
|
178
|
+
const phaseConfig = {
|
|
179
|
+
team: { agents: ['tea', 'architect'], strategy: 'parallel' },
|
|
180
|
+
};
|
|
181
|
+
const result = resolvePhaseExecution(phaseConfig);
|
|
182
|
+
assert.strictEqual(result.mode, 'team');
|
|
183
|
+
assert.strictEqual(result.degraded, false);
|
|
184
|
+
});
|
|
185
|
+
it('should degrade to solo-tandem when teams unavailable but phase has team block', () => {
|
|
186
|
+
delete process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
|
|
187
|
+
const phaseConfig = {
|
|
188
|
+
team: { agents: ['tea', 'architect'], strategy: 'parallel' },
|
|
189
|
+
};
|
|
190
|
+
const result = resolvePhaseExecution(phaseConfig);
|
|
191
|
+
assert.strictEqual(result.mode, 'solo-tandem');
|
|
192
|
+
assert.strictEqual(result.degraded, true);
|
|
193
|
+
assert.ok(result.reason, 'Should explain why degradation occurred');
|
|
194
|
+
});
|
|
195
|
+
it('should return solo mode when phase has no team block', () => {
|
|
196
|
+
const phaseConfig = {};
|
|
197
|
+
const result = resolvePhaseExecution(phaseConfig);
|
|
198
|
+
assert.strictEqual(result.mode, 'solo');
|
|
199
|
+
assert.strictEqual(result.degraded, false);
|
|
200
|
+
});
|
|
201
|
+
it('should not mark as degraded when phase has no team block regardless of capability', () => {
|
|
202
|
+
process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = 'true';
|
|
203
|
+
const phaseConfig = {};
|
|
204
|
+
const result = resolvePhaseExecution(phaseConfig);
|
|
205
|
+
assert.strictEqual(result.degraded, false);
|
|
206
|
+
});
|
|
207
|
+
it('should include reason string when degraded', () => {
|
|
208
|
+
delete process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
|
|
209
|
+
const phaseConfig = { team: { agents: ['dev'] } };
|
|
210
|
+
const result = resolvePhaseExecution(phaseConfig);
|
|
211
|
+
assert.strictEqual(result.degraded, true);
|
|
212
|
+
assert.strictEqual(typeof result.reason, 'string');
|
|
213
|
+
assert.ok(result.reason.length > 0, 'Reason should not be empty');
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
//# sourceMappingURL=capabilities.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.test.js","sourceRoot":"","sources":["../../src/shared/capabilities.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spawn prompt builder for native Agent Teams.
|
|
3
|
+
*
|
|
4
|
+
* Generates lightweight spawn prompts for teammate activation.
|
|
5
|
+
* Prompts invoke `pf agent start {agent}` for full Prime activation,
|
|
6
|
+
* keeping the prompt under 500 tokens. Teammates get persona, sidecars,
|
|
7
|
+
* and session context from Prime — not from prompt injection.
|
|
8
|
+
*
|
|
9
|
+
* @module spawn-prompt
|
|
10
|
+
*/
|
|
11
|
+
/** Valid core agent names */
|
|
12
|
+
export declare const CORE_AGENTS: readonly string[];
|
|
13
|
+
/** Configuration for building a spawn prompt */
|
|
14
|
+
export interface SpawnPromptConfig {
|
|
15
|
+
agent: string;
|
|
16
|
+
storyId: string;
|
|
17
|
+
task: string;
|
|
18
|
+
phase: string;
|
|
19
|
+
sessionFile?: string;
|
|
20
|
+
}
|
|
21
|
+
/** Result of spawn prompt generation */
|
|
22
|
+
export interface SpawnPromptResult {
|
|
23
|
+
success: boolean;
|
|
24
|
+
prompt?: string;
|
|
25
|
+
tokenEstimate?: number;
|
|
26
|
+
error?: string;
|
|
27
|
+
}
|
|
28
|
+
/** Result of spawn prompt validation */
|
|
29
|
+
export interface SpawnPromptValidation {
|
|
30
|
+
valid: boolean;
|
|
31
|
+
tokenCount: number;
|
|
32
|
+
errors: string[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Build a spawn prompt for teammate activation.
|
|
36
|
+
*/
|
|
37
|
+
export declare function buildSpawnPrompt(config: SpawnPromptConfig): SpawnPromptResult;
|
|
38
|
+
/**
|
|
39
|
+
* Estimate token count for a text string.
|
|
40
|
+
* Uses the ~4 chars per token heuristic.
|
|
41
|
+
*/
|
|
42
|
+
export declare function estimateTokenCount(text: string): number;
|
|
43
|
+
/**
|
|
44
|
+
* Validate a spawn prompt against constraints.
|
|
45
|
+
*/
|
|
46
|
+
export declare function validateSpawnPrompt(prompt: string): SpawnPromptValidation;
|
|
47
|
+
//# sourceMappingURL=spawn-prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn-prompt.d.ts","sourceRoot":"","sources":["../../src/shared/spawn-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,6BAA6B;AAC7B,eAAO,MAAM,WAAW,EAAE,SAAS,MAAM,EAI/B,CAAC;AAEX,gDAAgD;AAChD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wCAAwC;AACxC,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wCAAwC;AACxC,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAKD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,CAsC7E;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,qBAAqB,CAiBzE"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spawn prompt builder for native Agent Teams.
|
|
3
|
+
*
|
|
4
|
+
* Generates lightweight spawn prompts for teammate activation.
|
|
5
|
+
* Prompts invoke `pf agent start {agent}` for full Prime activation,
|
|
6
|
+
* keeping the prompt under 500 tokens. Teammates get persona, sidecars,
|
|
7
|
+
* and session context from Prime — not from prompt injection.
|
|
8
|
+
*
|
|
9
|
+
* @module spawn-prompt
|
|
10
|
+
*/
|
|
11
|
+
/** Valid core agent names */
|
|
12
|
+
export const CORE_AGENTS = [
|
|
13
|
+
'sm', 'tea', 'dev', 'reviewer', 'architect',
|
|
14
|
+
'pm', 'tech-writer', 'ux-designer', 'devops',
|
|
15
|
+
'orchestrator', 'ba',
|
|
16
|
+
];
|
|
17
|
+
const TOKEN_LIMIT = 500;
|
|
18
|
+
const MAX_TASK_CHARS = 1200;
|
|
19
|
+
/**
|
|
20
|
+
* Build a spawn prompt for teammate activation.
|
|
21
|
+
*/
|
|
22
|
+
export function buildSpawnPrompt(config) {
|
|
23
|
+
const { agent, storyId, task, phase, sessionFile } = config;
|
|
24
|
+
if (!agent) {
|
|
25
|
+
return { success: false, error: 'agent is required' };
|
|
26
|
+
}
|
|
27
|
+
if (!storyId) {
|
|
28
|
+
return { success: false, error: 'storyId is required' };
|
|
29
|
+
}
|
|
30
|
+
if (!task) {
|
|
31
|
+
return { success: false, error: 'task is required' };
|
|
32
|
+
}
|
|
33
|
+
if (!CORE_AGENTS.includes(agent)) {
|
|
34
|
+
return { success: false, error: `Unknown agent "${agent}". Valid agents: ${CORE_AGENTS.join(', ')}` };
|
|
35
|
+
}
|
|
36
|
+
const session = sessionFile || `.session/${storyId}-session.md`;
|
|
37
|
+
const truncatedTask = task.length > MAX_TASK_CHARS
|
|
38
|
+
? task.slice(0, MAX_TASK_CHARS) + '...'
|
|
39
|
+
: task;
|
|
40
|
+
const prompt = [
|
|
41
|
+
`**FIRST:** Run this command to activate:`,
|
|
42
|
+
'```bash',
|
|
43
|
+
`pf agent start "${agent}"`,
|
|
44
|
+
'```',
|
|
45
|
+
'',
|
|
46
|
+
`**Story:** ${storyId}`,
|
|
47
|
+
`**Phase:** ${phase}`,
|
|
48
|
+
`**Task:** ${truncatedTask}`,
|
|
49
|
+
'',
|
|
50
|
+
`Read the session file at \`${session}\` for full workflow state and context.`,
|
|
51
|
+
].join('\n');
|
|
52
|
+
const tokenEstimate = estimateTokenCount(prompt);
|
|
53
|
+
return { success: true, prompt, tokenEstimate };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Estimate token count for a text string.
|
|
57
|
+
* Uses the ~4 chars per token heuristic.
|
|
58
|
+
*/
|
|
59
|
+
export function estimateTokenCount(text) {
|
|
60
|
+
if (!text)
|
|
61
|
+
return 0;
|
|
62
|
+
return Math.ceil(text.length / 4);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Validate a spawn prompt against constraints.
|
|
66
|
+
*/
|
|
67
|
+
export function validateSpawnPrompt(prompt) {
|
|
68
|
+
const errors = [];
|
|
69
|
+
const tokenCount = estimateTokenCount(prompt);
|
|
70
|
+
if (tokenCount > TOKEN_LIMIT) {
|
|
71
|
+
errors.push(`Prompt exceeds token limit: ${tokenCount} > ${TOKEN_LIMIT}`);
|
|
72
|
+
}
|
|
73
|
+
if (!prompt.includes('pf agent start')) {
|
|
74
|
+
errors.push('Missing activation command (pf agent start)');
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
valid: errors.length === 0,
|
|
78
|
+
tokenCount,
|
|
79
|
+
errors,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=spawn-prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn-prompt.js","sourceRoot":"","sources":["../../src/shared/spawn-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,6BAA6B;AAC7B,MAAM,CAAC,MAAM,WAAW,GAAsB;IAC5C,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW;IAC3C,IAAI,EAAE,aAAa,EAAE,aAAa,EAAE,QAAQ;IAC5C,cAAc,EAAE,IAAI;CACZ,CAAC;AA0BX,MAAM,WAAW,GAAG,GAAG,CAAC;AACxB,MAAM,cAAc,GAAG,IAAI,CAAC;AAE5B;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAyB;IACxD,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IAE5D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC;IACxD,CAAC;IACD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC;IAC1D,CAAC;IACD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC;IACvD,CAAC;IAED,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,kBAAkB,KAAK,oBAAoB,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;IACxG,CAAC;IAED,MAAM,OAAO,GAAG,WAAW,IAAI,YAAY,OAAO,aAAa,CAAC;IAChE,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,GAAG,cAAc;QAChD,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,GAAG,KAAK;QACvC,CAAC,CAAC,IAAI,CAAC;IAET,MAAM,MAAM,GAAG;QACb,0CAA0C;QAC1C,SAAS;QACT,mBAAmB,KAAK,GAAG;QAC3B,KAAK;QACL,EAAE;QACF,cAAc,OAAO,EAAE;QACvB,cAAc,KAAK,EAAE;QACrB,aAAa,aAAa,EAAE;QAC5B,EAAE;QACF,8BAA8B,OAAO,yCAAyC;KAC/E,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,MAAM,aAAa,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAEjD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;AAClD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,IAAI,CAAC,IAAI;QAAE,OAAO,CAAC,CAAC;IACpB,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,UAAU,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAE9C,IAAI,UAAU,GAAG,WAAW,EAAE,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,+BAA+B,UAAU,MAAM,WAAW,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;IAC7D,CAAC;IAED,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC;QAC1B,UAAU;QACV,MAAM;KACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn-prompt.test.d.ts","sourceRoot":"","sources":["../../src/shared/spawn-prompt.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { buildSpawnPrompt, estimateTokenCount, validateSpawnPrompt, CORE_AGENTS, } from './spawn-prompt.js';
|
|
4
|
+
describe('spawn-prompt', () => {
|
|
5
|
+
// Helper: valid config for reuse across tests
|
|
6
|
+
const validConfig = {
|
|
7
|
+
agent: 'dev',
|
|
8
|
+
storyId: '86-8',
|
|
9
|
+
task: 'Implement the buildSpawnPrompt function',
|
|
10
|
+
phase: 'green',
|
|
11
|
+
sessionFile: '.session/86-8-session.md',
|
|
12
|
+
};
|
|
13
|
+
// =========================================================================
|
|
14
|
+
// AC1: Spawn prompt runs `pf agent start {agent}` for full Prime activation
|
|
15
|
+
// =========================================================================
|
|
16
|
+
describe('AC1: Prime activation command', () => {
|
|
17
|
+
it('should include pf agent start command in the prompt', () => {
|
|
18
|
+
const result = buildSpawnPrompt(validConfig);
|
|
19
|
+
assert.strictEqual(result.success, true, 'Should succeed with valid config');
|
|
20
|
+
assert.ok(result.prompt, 'Should have prompt text');
|
|
21
|
+
assert.ok(result.prompt.includes('pf agent start'), 'Prompt must include pf agent start command');
|
|
22
|
+
});
|
|
23
|
+
it('should include the specific agent name in the activation command', () => {
|
|
24
|
+
const result = buildSpawnPrompt({ ...validConfig, agent: 'tea' });
|
|
25
|
+
assert.strictEqual(result.success, true);
|
|
26
|
+
assert.ok(result.prompt.includes('pf agent start "tea"') ||
|
|
27
|
+
result.prompt.includes("pf agent start 'tea'") ||
|
|
28
|
+
result.prompt.includes('pf agent start tea'), `Prompt must reference the agent name in activation command, got: ${result.prompt}`);
|
|
29
|
+
});
|
|
30
|
+
it('should place activation command prominently (not buried in middle)', () => {
|
|
31
|
+
const result = buildSpawnPrompt(validConfig);
|
|
32
|
+
assert.strictEqual(result.success, true);
|
|
33
|
+
// Activation command should appear in the first half of the prompt
|
|
34
|
+
const prompt = result.prompt;
|
|
35
|
+
const cmdIndex = prompt.indexOf('pf agent start');
|
|
36
|
+
assert.ok(cmdIndex < prompt.length / 2, 'Activation command should appear in the first half of the prompt');
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
// =========================================================================
|
|
40
|
+
// AC2: Spawn prompt includes story ID, task assignment, phase context
|
|
41
|
+
// =========================================================================
|
|
42
|
+
describe('AC2: Required context in prompt', () => {
|
|
43
|
+
it('should include the story ID in the prompt', () => {
|
|
44
|
+
const result = buildSpawnPrompt(validConfig);
|
|
45
|
+
assert.strictEqual(result.success, true);
|
|
46
|
+
assert.ok(result.prompt.includes('86-8'), 'Prompt must include story ID');
|
|
47
|
+
});
|
|
48
|
+
it('should include the task assignment in the prompt', () => {
|
|
49
|
+
const result = buildSpawnPrompt(validConfig);
|
|
50
|
+
assert.strictEqual(result.success, true);
|
|
51
|
+
assert.ok(result.prompt.includes('Implement the buildSpawnPrompt function'), 'Prompt must include task description');
|
|
52
|
+
});
|
|
53
|
+
it('should include the phase context in the prompt', () => {
|
|
54
|
+
const result = buildSpawnPrompt(validConfig);
|
|
55
|
+
assert.strictEqual(result.success, true);
|
|
56
|
+
assert.ok(result.prompt.includes('green'), 'Prompt must include phase name');
|
|
57
|
+
});
|
|
58
|
+
it('should include the session file path when provided', () => {
|
|
59
|
+
const result = buildSpawnPrompt(validConfig);
|
|
60
|
+
assert.strictEqual(result.success, true);
|
|
61
|
+
assert.ok(result.prompt.includes('.session/86-8-session.md'), 'Prompt must include session file path when provided');
|
|
62
|
+
});
|
|
63
|
+
it('should return error when agent is missing', () => {
|
|
64
|
+
const result = buildSpawnPrompt({
|
|
65
|
+
...validConfig,
|
|
66
|
+
agent: '',
|
|
67
|
+
});
|
|
68
|
+
assert.strictEqual(result.success, false);
|
|
69
|
+
assert.ok(result.error, 'Should have error message');
|
|
70
|
+
});
|
|
71
|
+
it('should return error when storyId is missing', () => {
|
|
72
|
+
const result = buildSpawnPrompt({
|
|
73
|
+
...validConfig,
|
|
74
|
+
storyId: '',
|
|
75
|
+
});
|
|
76
|
+
assert.strictEqual(result.success, false);
|
|
77
|
+
assert.ok(result.error, 'Should have error message');
|
|
78
|
+
});
|
|
79
|
+
it('should return error when task is missing', () => {
|
|
80
|
+
const result = buildSpawnPrompt({
|
|
81
|
+
...validConfig,
|
|
82
|
+
task: '',
|
|
83
|
+
});
|
|
84
|
+
assert.strictEqual(result.success, false);
|
|
85
|
+
assert.ok(result.error, 'Should have error message');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
// =========================================================================
|
|
89
|
+
// AC3: Teammates activate via Prime, not prompt injection
|
|
90
|
+
// =========================================================================
|
|
91
|
+
describe('AC3: No prompt injection of context', () => {
|
|
92
|
+
it('should NOT contain inline persona definitions', () => {
|
|
93
|
+
const result = buildSpawnPrompt(validConfig);
|
|
94
|
+
assert.strictEqual(result.success, true);
|
|
95
|
+
const prompt = result.prompt.toLowerCase();
|
|
96
|
+
// Should not embed persona XML tags or character definitions
|
|
97
|
+
assert.ok(!prompt.includes('<persona'), 'Prompt must NOT contain inline persona definitions');
|
|
98
|
+
});
|
|
99
|
+
it('should NOT contain inline sidecar content', () => {
|
|
100
|
+
const result = buildSpawnPrompt(validConfig);
|
|
101
|
+
assert.strictEqual(result.success, true);
|
|
102
|
+
const prompt = result.prompt.toLowerCase();
|
|
103
|
+
assert.ok(!prompt.includes('<pattern'), 'Prompt must NOT contain inline sidecar patterns');
|
|
104
|
+
assert.ok(!prompt.includes('<gotcha'), 'Prompt must NOT contain inline sidecar gotchas');
|
|
105
|
+
});
|
|
106
|
+
it('should NOT contain inline agent behavior guide', () => {
|
|
107
|
+
const result = buildSpawnPrompt(validConfig);
|
|
108
|
+
assert.strictEqual(result.success, true);
|
|
109
|
+
const prompt = result.prompt;
|
|
110
|
+
// The agent-behavior guide is large — prompt should NOT embed it
|
|
111
|
+
assert.ok(!prompt.includes('<agent-exit-protocol>'), 'Prompt must NOT contain inline agent behavior guide');
|
|
112
|
+
});
|
|
113
|
+
it('should rely on pf agent start for context loading', () => {
|
|
114
|
+
const result = buildSpawnPrompt(validConfig);
|
|
115
|
+
assert.strictEqual(result.success, true);
|
|
116
|
+
// Must have the activation command — that IS the context loading mechanism
|
|
117
|
+
assert.ok(result.prompt.includes('pf agent start'), 'Must use pf agent start for context loading');
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
// =========================================================================
|
|
121
|
+
// AC4: Spawn prompt stays under 500 tokens
|
|
122
|
+
// =========================================================================
|
|
123
|
+
describe('AC4: Token budget constraint', () => {
|
|
124
|
+
it('should include token estimate in result', () => {
|
|
125
|
+
const result = buildSpawnPrompt(validConfig);
|
|
126
|
+
assert.strictEqual(result.success, true);
|
|
127
|
+
assert.ok(typeof result.tokenEstimate === 'number', 'Result must include token estimate');
|
|
128
|
+
assert.ok(result.tokenEstimate > 0, 'Token estimate must be positive');
|
|
129
|
+
});
|
|
130
|
+
it('should generate prompt under 500 tokens', () => {
|
|
131
|
+
const result = buildSpawnPrompt(validConfig);
|
|
132
|
+
assert.strictEqual(result.success, true);
|
|
133
|
+
assert.ok(result.tokenEstimate <= 500, `Prompt must be under 500 tokens, got ${result.tokenEstimate}`);
|
|
134
|
+
});
|
|
135
|
+
it('should stay under 500 tokens even with long task descriptions', () => {
|
|
136
|
+
const longTask = 'Implement comprehensive error handling across all API endpoints including retry logic, circuit breakers, and graceful degradation with user-facing error messages that are helpful but not leaking implementation details';
|
|
137
|
+
const result = buildSpawnPrompt({
|
|
138
|
+
...validConfig,
|
|
139
|
+
task: longTask,
|
|
140
|
+
});
|
|
141
|
+
assert.strictEqual(result.success, true);
|
|
142
|
+
assert.ok(result.tokenEstimate <= 500, `Prompt with long task must still be under 500 tokens, got ${result.tokenEstimate}`);
|
|
143
|
+
});
|
|
144
|
+
it('estimateTokenCount should return reasonable estimate for known text', () => {
|
|
145
|
+
// "hello world" is 2 tokens in most tokenizers, ~11 chars / 4 ≈ 2-3
|
|
146
|
+
const estimate = estimateTokenCount('hello world');
|
|
147
|
+
assert.ok(estimate > 0, 'Should return positive number');
|
|
148
|
+
assert.ok(estimate < 10, 'Should be reasonable for short text');
|
|
149
|
+
});
|
|
150
|
+
it('estimateTokenCount should return 0 for empty string', () => {
|
|
151
|
+
assert.strictEqual(estimateTokenCount(''), 0);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
// =========================================================================
|
|
155
|
+
// AC5: Works with all core agents
|
|
156
|
+
// =========================================================================
|
|
157
|
+
describe('AC5: All core agents supported', () => {
|
|
158
|
+
const expectedAgents = [
|
|
159
|
+
'sm', 'tea', 'dev', 'reviewer', 'architect',
|
|
160
|
+
'pm', 'tech-writer', 'ux-designer', 'devops',
|
|
161
|
+
'orchestrator', 'ba',
|
|
162
|
+
];
|
|
163
|
+
it('should export CORE_AGENTS array with all core agents', () => {
|
|
164
|
+
assert.ok(Array.isArray(CORE_AGENTS), 'CORE_AGENTS must be an array');
|
|
165
|
+
for (const agent of expectedAgents) {
|
|
166
|
+
assert.ok(CORE_AGENTS.includes(agent), `CORE_AGENTS must include "${agent}"`);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
it('should have at least 10 core agents', () => {
|
|
170
|
+
assert.ok(CORE_AGENTS.length >= 10, `Expected at least 10 core agents, got ${CORE_AGENTS.length}`);
|
|
171
|
+
});
|
|
172
|
+
for (const agent of expectedAgents) {
|
|
173
|
+
it(`should build prompt successfully for agent: ${agent}`, () => {
|
|
174
|
+
const result = buildSpawnPrompt({
|
|
175
|
+
...validConfig,
|
|
176
|
+
agent,
|
|
177
|
+
});
|
|
178
|
+
assert.strictEqual(result.success, true, `Should succeed for agent "${agent}", got error: ${result.error}`);
|
|
179
|
+
assert.ok(result.prompt, `Should produce prompt for agent "${agent}"`);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
it('should reject unknown agent names', () => {
|
|
183
|
+
const result = buildSpawnPrompt({
|
|
184
|
+
...validConfig,
|
|
185
|
+
agent: 'nonexistent-agent',
|
|
186
|
+
});
|
|
187
|
+
assert.strictEqual(result.success, false);
|
|
188
|
+
assert.ok(result.error?.toLowerCase().includes('unknown') ||
|
|
189
|
+
result.error?.toLowerCase().includes('invalid') ||
|
|
190
|
+
result.error?.toLowerCase().includes('not'), `Should indicate unknown agent, got: ${result.error}`);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
// =========================================================================
|
|
194
|
+
// AC6: Teammate correctly reads session file and workflow state
|
|
195
|
+
// =========================================================================
|
|
196
|
+
describe('AC6: Session file and workflow state reference', () => {
|
|
197
|
+
it('should instruct teammate to read the session file', () => {
|
|
198
|
+
const result = buildSpawnPrompt(validConfig);
|
|
199
|
+
assert.strictEqual(result.success, true);
|
|
200
|
+
const prompt = result.prompt.toLowerCase();
|
|
201
|
+
assert.ok(prompt.includes('session') && prompt.includes('read'), 'Prompt should instruct teammate to read the session file');
|
|
202
|
+
});
|
|
203
|
+
it('should reference the workflow state', () => {
|
|
204
|
+
const result = buildSpawnPrompt(validConfig);
|
|
205
|
+
assert.strictEqual(result.success, true);
|
|
206
|
+
const prompt = result.prompt.toLowerCase();
|
|
207
|
+
assert.ok(prompt.includes('workflow') || prompt.includes('phase'), 'Prompt should reference workflow state or phase');
|
|
208
|
+
});
|
|
209
|
+
it('should work without explicit sessionFile (uses convention)', () => {
|
|
210
|
+
const config = {
|
|
211
|
+
agent: 'dev',
|
|
212
|
+
storyId: '86-8',
|
|
213
|
+
task: 'Implement feature',
|
|
214
|
+
phase: 'green',
|
|
215
|
+
// no sessionFile
|
|
216
|
+
};
|
|
217
|
+
const result = buildSpawnPrompt(config);
|
|
218
|
+
assert.strictEqual(result.success, true);
|
|
219
|
+
// Should derive session file path from storyId convention
|
|
220
|
+
assert.ok(result.prompt.includes('.session/86-8-session.md') ||
|
|
221
|
+
result.prompt.includes('session'), 'Should reference session file even without explicit path');
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
// =========================================================================
|
|
225
|
+
// validateSpawnPrompt
|
|
226
|
+
// =========================================================================
|
|
227
|
+
describe('validateSpawnPrompt', () => {
|
|
228
|
+
it('should validate a well-formed prompt as valid', () => {
|
|
229
|
+
const result = buildSpawnPrompt(validConfig);
|
|
230
|
+
assert.strictEqual(result.success, true);
|
|
231
|
+
const validation = validateSpawnPrompt(result.prompt);
|
|
232
|
+
assert.strictEqual(validation.valid, true);
|
|
233
|
+
assert.strictEqual(validation.errors.length, 0);
|
|
234
|
+
});
|
|
235
|
+
it('should reject prompt exceeding 500 tokens', () => {
|
|
236
|
+
// Create an artificially long prompt (500 tokens ≈ 2000 chars)
|
|
237
|
+
const longPrompt = 'x'.repeat(2500);
|
|
238
|
+
const validation = validateSpawnPrompt(longPrompt);
|
|
239
|
+
assert.strictEqual(validation.valid, false);
|
|
240
|
+
assert.ok(validation.tokenCount > 500);
|
|
241
|
+
assert.ok(validation.errors.some((e) => e.toLowerCase().includes('token')), 'Should report token limit violation');
|
|
242
|
+
});
|
|
243
|
+
it('should reject prompt missing activation command', () => {
|
|
244
|
+
const badPrompt = 'Just do the task for story 86-8';
|
|
245
|
+
const validation = validateSpawnPrompt(badPrompt);
|
|
246
|
+
assert.strictEqual(validation.valid, false);
|
|
247
|
+
assert.ok(validation.errors.some((e) => e.toLowerCase().includes('activation') || e.toLowerCase().includes('pf agent')), 'Should report missing activation command');
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
//# sourceMappingURL=spawn-prompt.test.js.map
|