@pennyfarthing/core 7.8.2 → 7.9.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 +1 -1
- package/package.json +1 -1
- package/packages/core/dist/cli/commands/init.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/init.js +8 -7
- package/packages/core/dist/cli/commands/init.js.map +1 -1
- package/packages/core/dist/cli/cyclist-migration.test.js +16 -13
- package/packages/core/dist/cli/cyclist-migration.test.js.map +1 -1
- package/packages/core/dist/cli/utils/files.d.ts +5 -4
- package/packages/core/dist/cli/utils/files.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/files.js +8 -6
- package/packages/core/dist/cli/utils/files.js.map +1 -1
- package/packages/core/dist/cli/utils/symlinks.d.ts +7 -0
- package/packages/core/dist/cli/utils/symlinks.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/symlinks.js +25 -0
- package/packages/core/dist/cli/utils/symlinks.js.map +1 -1
- package/packages/core/dist/cli/utils/themes.d.ts +1 -1
- package/packages/core/dist/cli/utils/themes.d.ts.map +1 -1
- package/packages/core/dist/scripts/run-ci.test.js +1 -1
- package/packages/core/dist/scripts/run-ci.test.js.map +1 -1
- package/pennyfarthing-dist/agents/README.md +25 -17
- package/pennyfarthing-dist/agents/architect.md +3 -11
- package/pennyfarthing-dist/agents/dev.md +2 -2
- package/pennyfarthing-dist/agents/devops.md +3 -11
- package/pennyfarthing-dist/agents/handoff.md +4 -4
- package/pennyfarthing-dist/agents/orchestrator.md +2 -4
- package/pennyfarthing-dist/agents/pm.md +4 -11
- package/pennyfarthing-dist/agents/reviewer-preflight.md +4 -3
- package/pennyfarthing-dist/agents/reviewer.md +2 -8
- package/pennyfarthing-dist/agents/sm-handoff.md +3 -3
- package/pennyfarthing-dist/agents/sm-setup.md +1 -1
- package/pennyfarthing-dist/agents/sm.md +5 -29
- package/pennyfarthing-dist/agents/tea.md +2 -2
- package/pennyfarthing-dist/agents/tech-writer.md +3 -12
- package/pennyfarthing-dist/agents/testing-runner.md +8 -8
- package/pennyfarthing-dist/agents/ux-designer.md +3 -12
- package/pennyfarthing-dist/commands/git-cleanup.md +29 -53
- package/pennyfarthing-dist/commands/party-mode.md +20 -10
- package/pennyfarthing-dist/commands/work.md +6 -105
- package/pennyfarthing-dist/guides/agent-behavior.md +19 -7
- package/pennyfarthing-dist/personas/themes/1984.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/a-team.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/agatha-christie.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/alice-in-wonderland.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/all-stars.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/ancient-philosophers.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/ancient-strategists.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/arcane.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/arthurian-mythos.yaml +0 -13
- package/pennyfarthing-dist/personas/themes/avatar-the-last-airbender.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/babylon-5.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/battlestar-galactica.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/better-call-saul.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/big-lebowski.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/black-sails.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/blade-runner.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/bobiverse.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/breaking-bad.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/catch-22.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/classical-composers.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/count-of-monte-cristo.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/cowboy-bebop.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/deadwood.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/dickens.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/discworld.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/doctor-who.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/don-quixote.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/dune.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/enlightenment-thinkers.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/expeditionary-force.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/fargo.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/film-auteurs.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/firefly.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/foundation.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/futurama.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/game-of-thrones.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/gilligans-island.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/gothic-literature.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/great-gatsby.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/greek-mythology.yaml +0 -13
- package/pennyfarthing-dist/personas/themes/hannibal.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/harry-potter.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/his-dark-materials.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/historical-figures.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/hitchhikers-guide.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/house-md.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/imperial-radch.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/inspector-morse.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/jane-austen.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/jazz-legends.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/justified.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/legion-of-doom.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/les-miserables.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/lord-of-the-rings.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/lovecraft-mythos.yaml +0 -13
- package/pennyfarthing-dist/personas/themes/mad-max.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/mad-men.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/marvel-mcu.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/mash.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/mass-effect.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/military-commanders.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/moby-dick.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/monty-python.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/neuromancer.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/norse-mythology.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/parks-and-rec.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/peaky-blinders.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/princess-bride.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/renaissance-masters.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/rome.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/russian-masters.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/sandman.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/scientific-revolutionaries.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/shakespeare.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/sherlock-holmes.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/snow-crash.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/software-pioneers.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/star-trek-tng.yaml +0 -11
- package/pennyfarthing-dist/personas/themes/star-trek-tos.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/star-wars.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/succession.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/superfriends.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/ted-lasso.yaml +0 -11
- package/pennyfarthing-dist/personas/themes/the-americans.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/the-crown.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/the-expanse.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/the-good-place.yaml +0 -11
- package/pennyfarthing-dist/personas/themes/the-matrix.yaml +0 -15
- package/pennyfarthing-dist/personas/themes/the-odyssey.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/the-office.yaml +0 -11
- package/pennyfarthing-dist/personas/themes/the-simpsons.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/the-sopranos.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/the-wire.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/the-witcher.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/twin-peaks.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/vorkosigan-saga.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/watchmen.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/west-wing.yaml +0 -10
- package/pennyfarthing-dist/personas/themes/world-explorers.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/wwii-leaders.yaml +0 -12
- package/pennyfarthing-dist/personas/themes/x-files.yaml +0 -10
- package/pennyfarthing-dist/scripts/core/agent-session.sh +13 -14
- package/pennyfarthing-dist/scripts/core/phase-check-start.sh +1 -1
- package/pennyfarthing-dist/scripts/core/prime.sh +17 -2
- package/pennyfarthing-dist/scripts/core/run.sh +5 -5
- package/pennyfarthing-dist/scripts/git/install-git-hooks.sh +2 -2
- package/pennyfarthing-dist/scripts/git/release.sh +2 -2
- package/pennyfarthing-dist/scripts/health/drift-detection.sh +1 -1
- package/pennyfarthing-dist/scripts/hooks/post-merge.sh +2 -2
- package/pennyfarthing-dist/scripts/hooks/pre-push.sh +2 -2
- package/pennyfarthing-dist/scripts/hooks/session-stop.sh +1 -1
- package/pennyfarthing-dist/scripts/jira/create-jira-epic.sh +1 -1
- package/pennyfarthing-dist/scripts/jira/create-jira-story.sh +1 -1
- package/pennyfarthing-dist/scripts/jira/jira-reconcile.sh +1 -1
- package/pennyfarthing-dist/scripts/lib/common.sh +1 -1
- package/pennyfarthing-dist/scripts/lib/find-root.sh +4 -4
- package/pennyfarthing-dist/scripts/maintenance/migrate-theme-schema.mjs +102 -0
- package/pennyfarthing-dist/scripts/maintenance/sidecar-health.sh +1 -1
- package/pennyfarthing-dist/scripts/misc/add_short_names.py +2 -2
- package/pennyfarthing-dist/scripts/misc/backlog.sh +2 -2
- package/pennyfarthing-dist/scripts/misc/deploy.sh +2 -2
- package/pennyfarthing-dist/scripts/misc/generate-skill-docs.sh +4 -4
- package/pennyfarthing-dist/scripts/misc/log-skill-usage.sh +2 -2
- package/pennyfarthing-dist/scripts/misc/run-ci.sh +1 -1
- package/pennyfarthing-dist/scripts/misc/skill-usage-report.sh +2 -2
- package/pennyfarthing-dist/scripts/sprint/archive-story.sh +6 -2
- package/pennyfarthing-dist/scripts/sprint/available-stories.sh +1 -1
- package/pennyfarthing-dist/scripts/sprint/check-story.sh +1 -1
- package/pennyfarthing-dist/scripts/sprint/get-epic-field.sh +1 -1
- package/pennyfarthing-dist/scripts/sprint/get-story-field.sh +1 -1
- package/pennyfarthing-dist/scripts/sprint/import_epic_to_future.py +2 -2
- package/pennyfarthing-dist/scripts/sprint/list-future.sh +1 -1
- package/pennyfarthing-dist/scripts/sprint/new-sprint.sh +1 -1
- package/pennyfarthing-dist/scripts/sprint/promote-epic.sh +1 -1
- package/pennyfarthing-dist/scripts/sprint/sprint-common.sh +3 -3
- package/pennyfarthing-dist/scripts/sprint/sprint-info.sh +1 -1
- package/pennyfarthing-dist/scripts/sprint/sprint-metrics.sh +2 -2
- package/pennyfarthing-dist/scripts/theme/compute_theme_tiers.py +2 -2
- package/pennyfarthing-dist/scripts/validation/validate-agent-schema.sh +3 -2
- package/pennyfarthing-dist/scripts/workflow/check.py +2 -2
- package/pennyfarthing-dist/scripts/workflow/finish-story.sh +1 -1
- package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +1 -1
- package/pennyfarthing-dist/scripts/workflow/get-workflow-type.py +2 -2
- package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +1 -1
- package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +1 -1
- package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +1 -1
- package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +1 -1
- package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +1 -1
- package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +1 -1
- package/pennyfarthing-dist/workflows/git-cleanup/steps/step-01-analyze.md +18 -0
- package/pennyfarthing-dist/workflows/git-cleanup/steps/step-03-execute.md +18 -4
- package/pennyfarthing-dist/workflows/git-cleanup/steps/step-05-complete.md +13 -5
- package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/client.py +1 -1
- package/pennyfarthing_scripts/prime/__init__.py +98 -11
- package/pennyfarthing_scripts/prime/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/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__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/cli.py +208 -53
- package/pennyfarthing_scripts/prime/models.py +169 -0
- package/pennyfarthing_scripts/prime/persona.py +288 -0
- package/pennyfarthing_scripts/prime/session.py +183 -0
- package/pennyfarthing_scripts/prime/workflow.py +275 -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_prime.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/test_prime.py +653 -0
|
@@ -395,3 +395,656 @@ class TestMainCLI:
|
|
|
395
395
|
assert result == 1
|
|
396
396
|
captured = capsys.readouterr()
|
|
397
397
|
assert "Error" in captured.err
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
# =============================================================================
|
|
401
|
+
# Prime v2 Tests - Workflow State Detection
|
|
402
|
+
# =============================================================================
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
class TestWorkflowStateDetection:
|
|
406
|
+
"""Tests for workflow state detection (Prime v2)."""
|
|
407
|
+
|
|
408
|
+
def test_detect_finish_state(self, tmp_path: Path) -> None:
|
|
409
|
+
"""Test detecting FINISH_STATE when phase is approved."""
|
|
410
|
+
from pennyfarthing_scripts.prime.workflow import detect_workflow_state
|
|
411
|
+
from pennyfarthing_scripts.prime.models import WorkflowState
|
|
412
|
+
|
|
413
|
+
# Setup
|
|
414
|
+
pf_dir = tmp_path / ".pennyfarthing"
|
|
415
|
+
pf_dir.mkdir()
|
|
416
|
+
session_dir = tmp_path / ".session"
|
|
417
|
+
session_dir.mkdir()
|
|
418
|
+
session_file = session_dir / "MSSCI-12345-session.md"
|
|
419
|
+
session_file.write_text("""# MSSCI-12345: Test Story
|
|
420
|
+
|
|
421
|
+
## Story Context
|
|
422
|
+
- **ID:** MSSCI-12345
|
|
423
|
+
- **Workflow:** tdd
|
|
424
|
+
|
|
425
|
+
## Workflow Phase
|
|
426
|
+
- **Current Phase:** REVIEW (APPROVED)
|
|
427
|
+
""")
|
|
428
|
+
|
|
429
|
+
# Test
|
|
430
|
+
result = detect_workflow_state(tmp_path)
|
|
431
|
+
|
|
432
|
+
# Verify
|
|
433
|
+
assert result.state == WorkflowState.FINISH_STATE
|
|
434
|
+
assert result.story_id == "MSSCI-12345"
|
|
435
|
+
assert result.phase_owner == "sm"
|
|
436
|
+
|
|
437
|
+
def test_detect_in_progress_state(self, tmp_path: Path) -> None:
|
|
438
|
+
"""Test detecting IN_PROGRESS_STATE with active phase."""
|
|
439
|
+
from pennyfarthing_scripts.prime.workflow import detect_workflow_state
|
|
440
|
+
from pennyfarthing_scripts.prime.models import WorkflowState
|
|
441
|
+
import yaml
|
|
442
|
+
|
|
443
|
+
# Setup
|
|
444
|
+
pf_dir = tmp_path / ".pennyfarthing"
|
|
445
|
+
pf_dir.mkdir()
|
|
446
|
+
|
|
447
|
+
# Create workflow YAML
|
|
448
|
+
workflows_dir = tmp_path / "pennyfarthing-dist" / "workflows"
|
|
449
|
+
workflows_dir.mkdir(parents=True)
|
|
450
|
+
(workflows_dir / "tdd.yaml").write_text(yaml.dump({
|
|
451
|
+
"workflow": {
|
|
452
|
+
"phases": [
|
|
453
|
+
{"name": "setup", "agent": "sm"},
|
|
454
|
+
{"name": "red", "agent": "tea"},
|
|
455
|
+
{"name": "green", "agent": "dev"},
|
|
456
|
+
{"name": "review", "agent": "reviewer"},
|
|
457
|
+
]
|
|
458
|
+
}
|
|
459
|
+
}))
|
|
460
|
+
|
|
461
|
+
session_dir = tmp_path / ".session"
|
|
462
|
+
session_dir.mkdir()
|
|
463
|
+
session_file = session_dir / "63-5-session.md"
|
|
464
|
+
session_file.write_text("""# 63-5: Test Story
|
|
465
|
+
|
|
466
|
+
## Story Context
|
|
467
|
+
- **ID:** 63-5
|
|
468
|
+
- **Workflow:** tdd
|
|
469
|
+
|
|
470
|
+
## Workflow Phase
|
|
471
|
+
- **Current Phase:** green
|
|
472
|
+
""")
|
|
473
|
+
|
|
474
|
+
# Test
|
|
475
|
+
result = detect_workflow_state(tmp_path)
|
|
476
|
+
|
|
477
|
+
# Verify
|
|
478
|
+
assert result.state == WorkflowState.IN_PROGRESS_STATE
|
|
479
|
+
assert result.story_id == "63-5"
|
|
480
|
+
assert result.phase == "green"
|
|
481
|
+
assert result.phase_owner == "dev"
|
|
482
|
+
assert result.workflow == "tdd"
|
|
483
|
+
|
|
484
|
+
def test_detect_new_work_state(self, tmp_path: Path) -> None:
|
|
485
|
+
"""Test detecting NEW_WORK_STATE with backlog stories."""
|
|
486
|
+
from pennyfarthing_scripts.prime.workflow import detect_workflow_state
|
|
487
|
+
from pennyfarthing_scripts.prime.models import WorkflowState
|
|
488
|
+
import yaml
|
|
489
|
+
|
|
490
|
+
# Setup
|
|
491
|
+
pf_dir = tmp_path / ".pennyfarthing"
|
|
492
|
+
pf_dir.mkdir()
|
|
493
|
+
sprint_dir = tmp_path / "sprint"
|
|
494
|
+
sprint_dir.mkdir()
|
|
495
|
+
(sprint_dir / "current-sprint.yaml").write_text(yaml.dump({
|
|
496
|
+
"sprint": {"number": 12},
|
|
497
|
+
"epics": [
|
|
498
|
+
{
|
|
499
|
+
"id": "epic-1",
|
|
500
|
+
"stories": [
|
|
501
|
+
{"id": "1-1", "status": "backlog", "points": 3},
|
|
502
|
+
{"id": "1-2", "status": "ready", "points": 5},
|
|
503
|
+
]
|
|
504
|
+
}
|
|
505
|
+
]
|
|
506
|
+
}))
|
|
507
|
+
|
|
508
|
+
# Test
|
|
509
|
+
result = detect_workflow_state(tmp_path)
|
|
510
|
+
|
|
511
|
+
# Verify
|
|
512
|
+
assert result.state == WorkflowState.NEW_WORK_STATE
|
|
513
|
+
assert result.backlog_count == 2
|
|
514
|
+
|
|
515
|
+
def test_detect_empty_backlog_state(self, tmp_path: Path) -> None:
|
|
516
|
+
"""Test detecting EMPTY_BACKLOG_STATE with no backlog."""
|
|
517
|
+
from pennyfarthing_scripts.prime.workflow import detect_workflow_state
|
|
518
|
+
from pennyfarthing_scripts.prime.models import WorkflowState
|
|
519
|
+
import yaml
|
|
520
|
+
|
|
521
|
+
# Setup
|
|
522
|
+
pf_dir = tmp_path / ".pennyfarthing"
|
|
523
|
+
pf_dir.mkdir()
|
|
524
|
+
sprint_dir = tmp_path / "sprint"
|
|
525
|
+
sprint_dir.mkdir()
|
|
526
|
+
(sprint_dir / "current-sprint.yaml").write_text(yaml.dump({
|
|
527
|
+
"sprint": {"number": 12},
|
|
528
|
+
"epics": [
|
|
529
|
+
{
|
|
530
|
+
"id": "epic-1",
|
|
531
|
+
"stories": [
|
|
532
|
+
{"id": "1-1", "status": "done", "points": 3},
|
|
533
|
+
]
|
|
534
|
+
}
|
|
535
|
+
]
|
|
536
|
+
}))
|
|
537
|
+
|
|
538
|
+
# Test
|
|
539
|
+
result = detect_workflow_state(tmp_path)
|
|
540
|
+
|
|
541
|
+
# Verify
|
|
542
|
+
assert result.state == WorkflowState.EMPTY_BACKLOG_STATE
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
class TestParseSessionHeader:
|
|
546
|
+
"""Tests for session header parsing."""
|
|
547
|
+
|
|
548
|
+
def test_parse_standard_header(self, tmp_path: Path) -> None:
|
|
549
|
+
"""Test parsing a standard session header."""
|
|
550
|
+
from pennyfarthing_scripts.prime.workflow import parse_session_header
|
|
551
|
+
|
|
552
|
+
session_file = tmp_path / "MSSCI-12345-session.md"
|
|
553
|
+
session_file.write_text("""# MSSCI-12345: Test Story
|
|
554
|
+
|
|
555
|
+
## Story Context
|
|
556
|
+
- **ID:** MSSCI-12345
|
|
557
|
+
- **Workflow:** tdd
|
|
558
|
+
|
|
559
|
+
## Workflow Phase
|
|
560
|
+
- **Current Phase:** green
|
|
561
|
+
""")
|
|
562
|
+
|
|
563
|
+
result = parse_session_header(session_file)
|
|
564
|
+
|
|
565
|
+
assert result["story_id"] == "MSSCI-12345"
|
|
566
|
+
assert result["workflow"] == "tdd"
|
|
567
|
+
assert result["phase"] == "green"
|
|
568
|
+
|
|
569
|
+
def test_parse_approved_phase(self, tmp_path: Path) -> None:
|
|
570
|
+
"""Test parsing phase with APPROVED status."""
|
|
571
|
+
from pennyfarthing_scripts.prime.workflow import parse_session_header
|
|
572
|
+
|
|
573
|
+
session_file = tmp_path / "63-1-session.md"
|
|
574
|
+
session_file.write_text("""# Session
|
|
575
|
+
|
|
576
|
+
- **Current Phase:** REVIEW (APPROVED)
|
|
577
|
+
""")
|
|
578
|
+
|
|
579
|
+
result = parse_session_header(session_file)
|
|
580
|
+
|
|
581
|
+
assert result["phase"] == "review"
|
|
582
|
+
assert result["phase_status"] == "approved"
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
class TestCheckRedirect:
|
|
586
|
+
"""Tests for redirect detection."""
|
|
587
|
+
|
|
588
|
+
def test_redirect_when_wrong_agent(self) -> None:
|
|
589
|
+
"""Test redirect is detected when wrong agent is activated."""
|
|
590
|
+
from pennyfarthing_scripts.prime.workflow import check_redirect
|
|
591
|
+
from pennyfarthing_scripts.prime.models import WorkflowState, WorkflowStatus
|
|
592
|
+
|
|
593
|
+
status = WorkflowStatus(
|
|
594
|
+
state=WorkflowState.IN_PROGRESS_STATE,
|
|
595
|
+
phase="green",
|
|
596
|
+
phase_owner="dev",
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
result = check_redirect(status, "tea")
|
|
600
|
+
|
|
601
|
+
assert result is not None
|
|
602
|
+
target, reason = result
|
|
603
|
+
assert target == "dev"
|
|
604
|
+
assert "green" in reason
|
|
605
|
+
assert "dev" in reason
|
|
606
|
+
|
|
607
|
+
def test_no_redirect_when_correct_agent(self) -> None:
|
|
608
|
+
"""Test no redirect when correct agent is activated."""
|
|
609
|
+
from pennyfarthing_scripts.prime.workflow import check_redirect
|
|
610
|
+
from pennyfarthing_scripts.prime.models import WorkflowState, WorkflowStatus
|
|
611
|
+
|
|
612
|
+
status = WorkflowStatus(
|
|
613
|
+
state=WorkflowState.IN_PROGRESS_STATE,
|
|
614
|
+
phase="green",
|
|
615
|
+
phase_owner="dev",
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
result = check_redirect(status, "dev")
|
|
619
|
+
|
|
620
|
+
assert result is None
|
|
621
|
+
|
|
622
|
+
def test_no_redirect_for_new_work(self) -> None:
|
|
623
|
+
"""Test no redirect for NEW_WORK_STATE."""
|
|
624
|
+
from pennyfarthing_scripts.prime.workflow import check_redirect
|
|
625
|
+
from pennyfarthing_scripts.prime.models import WorkflowState, WorkflowStatus
|
|
626
|
+
|
|
627
|
+
status = WorkflowStatus(
|
|
628
|
+
state=WorkflowState.NEW_WORK_STATE,
|
|
629
|
+
backlog_count=5,
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
result = check_redirect(status, "dev")
|
|
633
|
+
|
|
634
|
+
assert result is None
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
# =============================================================================
|
|
638
|
+
# Prime v2 Tests - Persona Loading
|
|
639
|
+
# =============================================================================
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
class TestPersonaLoading:
|
|
643
|
+
"""Tests for persona loading (Prime v2)."""
|
|
644
|
+
|
|
645
|
+
def test_load_persona_from_theme(self, tmp_path: Path) -> None:
|
|
646
|
+
"""Test loading persona from theme YAML."""
|
|
647
|
+
from pennyfarthing_scripts.prime.persona import load_persona
|
|
648
|
+
import yaml
|
|
649
|
+
|
|
650
|
+
# Setup
|
|
651
|
+
pf_dir = tmp_path / ".pennyfarthing"
|
|
652
|
+
pf_dir.mkdir()
|
|
653
|
+
|
|
654
|
+
# Create config
|
|
655
|
+
(pf_dir / "config.local.yaml").write_text(yaml.dump({"theme": "test-theme"}))
|
|
656
|
+
|
|
657
|
+
# Create theme
|
|
658
|
+
themes_dir = pf_dir / "personas" / "themes"
|
|
659
|
+
themes_dir.mkdir(parents=True)
|
|
660
|
+
(themes_dir / "test-theme.yaml").write_text(yaml.dump({
|
|
661
|
+
"theme": {"name": "Test Theme"},
|
|
662
|
+
"agents": {
|
|
663
|
+
"dev": {
|
|
664
|
+
"character": "Test Developer",
|
|
665
|
+
"style": "Test style",
|
|
666
|
+
"role": "Test role",
|
|
667
|
+
"quote": "Test quote",
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}))
|
|
671
|
+
|
|
672
|
+
# Test
|
|
673
|
+
persona, theme = load_persona("dev", tmp_path)
|
|
674
|
+
|
|
675
|
+
# Verify
|
|
676
|
+
assert persona is not None
|
|
677
|
+
assert persona.character == "Test Developer"
|
|
678
|
+
assert persona.style == "Test style"
|
|
679
|
+
assert persona.role == "Test role"
|
|
680
|
+
assert persona.quote == "Test quote"
|
|
681
|
+
assert theme == "test-theme"
|
|
682
|
+
|
|
683
|
+
def test_load_persona_no_theme(self, tmp_path: Path) -> None:
|
|
684
|
+
"""Test load_persona returns None when no theme configured."""
|
|
685
|
+
from pennyfarthing_scripts.prime.persona import load_persona
|
|
686
|
+
|
|
687
|
+
# Setup - no config
|
|
688
|
+
pf_dir = tmp_path / ".pennyfarthing"
|
|
689
|
+
pf_dir.mkdir()
|
|
690
|
+
|
|
691
|
+
# Test
|
|
692
|
+
persona, theme = load_persona("dev", tmp_path)
|
|
693
|
+
|
|
694
|
+
# Verify
|
|
695
|
+
assert persona is None
|
|
696
|
+
assert theme is None
|
|
697
|
+
|
|
698
|
+
def test_get_crew_manifest(self, tmp_path: Path) -> None:
|
|
699
|
+
"""Test getting crew manifest for handoff reference."""
|
|
700
|
+
from pennyfarthing_scripts.prime.persona import get_crew_manifest
|
|
701
|
+
import yaml
|
|
702
|
+
|
|
703
|
+
# Setup
|
|
704
|
+
pf_dir = tmp_path / ".pennyfarthing"
|
|
705
|
+
pf_dir.mkdir()
|
|
706
|
+
|
|
707
|
+
# Create config
|
|
708
|
+
(pf_dir / "config.local.yaml").write_text(yaml.dump({"theme": "test-theme"}))
|
|
709
|
+
|
|
710
|
+
# Create theme with multiple agents
|
|
711
|
+
themes_dir = pf_dir / "personas" / "themes"
|
|
712
|
+
themes_dir.mkdir(parents=True)
|
|
713
|
+
(themes_dir / "test-theme.yaml").write_text(yaml.dump({
|
|
714
|
+
"agents": {
|
|
715
|
+
"sm": {"character": "Scrum Master"},
|
|
716
|
+
"tea": {"character": "Test Engineer"},
|
|
717
|
+
"dev": {"character": "Developer"},
|
|
718
|
+
}
|
|
719
|
+
}))
|
|
720
|
+
|
|
721
|
+
# Test
|
|
722
|
+
crew = get_crew_manifest(tmp_path)
|
|
723
|
+
|
|
724
|
+
# Verify
|
|
725
|
+
assert len(crew) == 3
|
|
726
|
+
roles = {c.role for c in crew}
|
|
727
|
+
assert "sm" in roles
|
|
728
|
+
assert "tea" in roles
|
|
729
|
+
assert "dev" in roles
|
|
730
|
+
|
|
731
|
+
def test_format_persona_output(self) -> None:
|
|
732
|
+
"""Test formatting persona as XML."""
|
|
733
|
+
from pennyfarthing_scripts.prime.persona import format_persona_output
|
|
734
|
+
from pennyfarthing_scripts.prime.models import Persona, CrewMember
|
|
735
|
+
|
|
736
|
+
persona = Persona(
|
|
737
|
+
character="Naomi Nagata",
|
|
738
|
+
style="Precise, systematic",
|
|
739
|
+
role="The XO and engineer",
|
|
740
|
+
quote="I can fix this.",
|
|
741
|
+
)
|
|
742
|
+
crew = [
|
|
743
|
+
CrewMember(role="sm", character="Drummer"),
|
|
744
|
+
CrewMember(role="dev", character="Naomi"),
|
|
745
|
+
]
|
|
746
|
+
|
|
747
|
+
result = format_persona_output(persona, "the-expanse", "dev", crew, "Bossmang")
|
|
748
|
+
|
|
749
|
+
assert '<persona agent="dev" theme="the-expanse">' in result
|
|
750
|
+
assert "Character: Naomi Nagata" in result
|
|
751
|
+
assert "Quote: I can fix this." in result
|
|
752
|
+
assert "<user-title>Address the user as: Bossmang</user-title>" in result
|
|
753
|
+
assert '<crew theme="the-expanse">' in result
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
# =============================================================================
|
|
757
|
+
# Prime v2 Tests - Session Registration
|
|
758
|
+
# =============================================================================
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
class TestSessionRegistration:
|
|
762
|
+
"""Tests for session registration (Prime v2)."""
|
|
763
|
+
|
|
764
|
+
def test_register_session(self, tmp_path: Path) -> None:
|
|
765
|
+
"""Test registering a new session."""
|
|
766
|
+
from pennyfarthing_scripts.prime.session import register_session
|
|
767
|
+
|
|
768
|
+
# Setup
|
|
769
|
+
pf_dir = tmp_path / ".pennyfarthing"
|
|
770
|
+
pf_dir.mkdir()
|
|
771
|
+
|
|
772
|
+
# Test
|
|
773
|
+
result = register_session("dev", session_id="test-123", project_root=tmp_path)
|
|
774
|
+
|
|
775
|
+
# Verify
|
|
776
|
+
assert result.session_id == "test-123"
|
|
777
|
+
assert result.agent_name == "dev"
|
|
778
|
+
|
|
779
|
+
# Check file was created
|
|
780
|
+
session_file = tmp_path / ".session" / "agents" / "test-123"
|
|
781
|
+
assert session_file.exists()
|
|
782
|
+
assert session_file.read_text() == "dev"
|
|
783
|
+
|
|
784
|
+
def test_register_session_generates_id(self, tmp_path: Path) -> None:
|
|
785
|
+
"""Test that session ID is generated if not provided."""
|
|
786
|
+
from pennyfarthing_scripts.prime.session import register_session
|
|
787
|
+
|
|
788
|
+
# Setup
|
|
789
|
+
pf_dir = tmp_path / ".pennyfarthing"
|
|
790
|
+
pf_dir.mkdir()
|
|
791
|
+
|
|
792
|
+
# Test
|
|
793
|
+
result = register_session("tea", project_root=tmp_path)
|
|
794
|
+
|
|
795
|
+
# Verify - should have a UUID-like session ID
|
|
796
|
+
assert result.session_id is not None
|
|
797
|
+
assert len(result.session_id) > 0
|
|
798
|
+
|
|
799
|
+
def test_cleanup_old_sessions(self, tmp_path: Path) -> None:
|
|
800
|
+
"""Test cleanup of old session files."""
|
|
801
|
+
from pennyfarthing_scripts.prime.session import cleanup_old_sessions
|
|
802
|
+
import time
|
|
803
|
+
|
|
804
|
+
# Setup
|
|
805
|
+
pf_dir = tmp_path / ".pennyfarthing"
|
|
806
|
+
pf_dir.mkdir()
|
|
807
|
+
agents_dir = tmp_path / ".session" / "agents"
|
|
808
|
+
agents_dir.mkdir(parents=True)
|
|
809
|
+
|
|
810
|
+
# Create an old session file
|
|
811
|
+
old_session = agents_dir / "old-session"
|
|
812
|
+
old_session.write_text("sm")
|
|
813
|
+
# Set mtime to 10 days ago
|
|
814
|
+
old_time = time.time() - (10 * 86400)
|
|
815
|
+
import os
|
|
816
|
+
os.utime(old_session, (old_time, old_time))
|
|
817
|
+
|
|
818
|
+
# Create a new session file
|
|
819
|
+
new_session = agents_dir / "new-session"
|
|
820
|
+
new_session.write_text("dev")
|
|
821
|
+
|
|
822
|
+
# Test
|
|
823
|
+
removed = cleanup_old_sessions(tmp_path, max_age_days=7)
|
|
824
|
+
|
|
825
|
+
# Verify
|
|
826
|
+
assert removed == 1
|
|
827
|
+
assert not old_session.exists()
|
|
828
|
+
assert new_session.exists()
|
|
829
|
+
|
|
830
|
+
def test_get_session_agent(self, tmp_path: Path) -> None:
|
|
831
|
+
"""Test getting agent name for a session."""
|
|
832
|
+
from pennyfarthing_scripts.prime.session import get_session_agent
|
|
833
|
+
|
|
834
|
+
# Setup
|
|
835
|
+
pf_dir = tmp_path / ".pennyfarthing"
|
|
836
|
+
pf_dir.mkdir()
|
|
837
|
+
agents_dir = tmp_path / ".session" / "agents"
|
|
838
|
+
agents_dir.mkdir(parents=True)
|
|
839
|
+
(agents_dir / "test-session").write_text("reviewer")
|
|
840
|
+
|
|
841
|
+
# Test
|
|
842
|
+
result = get_session_agent("test-session", tmp_path)
|
|
843
|
+
|
|
844
|
+
# Verify
|
|
845
|
+
assert result == "reviewer"
|
|
846
|
+
|
|
847
|
+
def test_unregister_session(self, tmp_path: Path) -> None:
|
|
848
|
+
"""Test unregistering a session."""
|
|
849
|
+
from pennyfarthing_scripts.prime.session import unregister_session
|
|
850
|
+
|
|
851
|
+
# Setup
|
|
852
|
+
pf_dir = tmp_path / ".pennyfarthing"
|
|
853
|
+
pf_dir.mkdir()
|
|
854
|
+
agents_dir = tmp_path / ".session" / "agents"
|
|
855
|
+
agents_dir.mkdir(parents=True)
|
|
856
|
+
session_file = agents_dir / "to-remove"
|
|
857
|
+
session_file.write_text("dev")
|
|
858
|
+
|
|
859
|
+
# Test
|
|
860
|
+
result = unregister_session("to-remove", tmp_path)
|
|
861
|
+
|
|
862
|
+
# Verify
|
|
863
|
+
assert result is True
|
|
864
|
+
assert not session_file.exists()
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
# =============================================================================
|
|
868
|
+
# Prime v2 Tests - JSON Output
|
|
869
|
+
# =============================================================================
|
|
870
|
+
|
|
871
|
+
|
|
872
|
+
class TestJSONOutput:
|
|
873
|
+
"""Tests for JSON output (Prime v2)."""
|
|
874
|
+
|
|
875
|
+
def test_json_output_minimal(self, tmp_path: Path, capsys) -> None:
|
|
876
|
+
"""Test JSON output in minimal mode."""
|
|
877
|
+
import json
|
|
878
|
+
|
|
879
|
+
result = prime(minimal=True, json_output=True, project_root=tmp_path)
|
|
880
|
+
|
|
881
|
+
assert result == 0
|
|
882
|
+
captured = capsys.readouterr()
|
|
883
|
+
data = json.loads(captured.out)
|
|
884
|
+
assert data["minimal"] is True
|
|
885
|
+
|
|
886
|
+
def test_json_output_with_workflow(self, tmp_path: Path, capsys) -> None:
|
|
887
|
+
"""Test JSON output includes workflow status."""
|
|
888
|
+
import json
|
|
889
|
+
import yaml
|
|
890
|
+
|
|
891
|
+
# Setup
|
|
892
|
+
pf_dir = tmp_path / ".pennyfarthing"
|
|
893
|
+
pf_dir.mkdir()
|
|
894
|
+
sprint_dir = tmp_path / "sprint"
|
|
895
|
+
sprint_dir.mkdir()
|
|
896
|
+
(sprint_dir / "current-sprint.yaml").write_text(yaml.dump({
|
|
897
|
+
"sprint": {"number": 12},
|
|
898
|
+
"epics": [
|
|
899
|
+
{
|
|
900
|
+
"id": "epic-1",
|
|
901
|
+
"stories": [
|
|
902
|
+
{"id": "1-1", "status": "backlog", "points": 3},
|
|
903
|
+
]
|
|
904
|
+
}
|
|
905
|
+
]
|
|
906
|
+
}))
|
|
907
|
+
|
|
908
|
+
# Test
|
|
909
|
+
result = prime(
|
|
910
|
+
agent_name="sm",
|
|
911
|
+
json_output=True,
|
|
912
|
+
no_persona=True,
|
|
913
|
+
no_register=True,
|
|
914
|
+
project_root=tmp_path,
|
|
915
|
+
)
|
|
916
|
+
|
|
917
|
+
assert result == 0
|
|
918
|
+
captured = capsys.readouterr()
|
|
919
|
+
data = json.loads(captured.out)
|
|
920
|
+
|
|
921
|
+
assert "workflow_status" in data
|
|
922
|
+
assert data["workflow_status"]["state"] == "NEW_WORK_STATE"
|
|
923
|
+
assert data["workflow_status"]["backlog_count"] == 1
|
|
924
|
+
|
|
925
|
+
def test_json_output_with_redirect(self, tmp_path: Path, capsys) -> None:
|
|
926
|
+
"""Test JSON output includes redirect info."""
|
|
927
|
+
import json
|
|
928
|
+
import yaml
|
|
929
|
+
|
|
930
|
+
# Setup
|
|
931
|
+
pf_dir = tmp_path / ".pennyfarthing"
|
|
932
|
+
pf_dir.mkdir()
|
|
933
|
+
|
|
934
|
+
# Create workflow YAML
|
|
935
|
+
workflows_dir = tmp_path / "pennyfarthing-dist" / "workflows"
|
|
936
|
+
workflows_dir.mkdir(parents=True)
|
|
937
|
+
(workflows_dir / "tdd.yaml").write_text(yaml.dump({
|
|
938
|
+
"workflow": {
|
|
939
|
+
"phases": [
|
|
940
|
+
{"name": "green", "agent": "dev"},
|
|
941
|
+
]
|
|
942
|
+
}
|
|
943
|
+
}))
|
|
944
|
+
|
|
945
|
+
session_dir = tmp_path / ".session"
|
|
946
|
+
session_dir.mkdir()
|
|
947
|
+
(session_dir / "test-session.md").write_text("""# Test
|
|
948
|
+
|
|
949
|
+
- **Workflow:** tdd
|
|
950
|
+
- **Current Phase:** green
|
|
951
|
+
""")
|
|
952
|
+
|
|
953
|
+
# Test - activating TEA when DEV owns the phase
|
|
954
|
+
result = prime(
|
|
955
|
+
agent_name="tea",
|
|
956
|
+
json_output=True,
|
|
957
|
+
no_persona=True,
|
|
958
|
+
no_register=True,
|
|
959
|
+
project_root=tmp_path,
|
|
960
|
+
)
|
|
961
|
+
|
|
962
|
+
assert result == 0
|
|
963
|
+
captured = capsys.readouterr()
|
|
964
|
+
data = json.loads(captured.out)
|
|
965
|
+
|
|
966
|
+
assert data["redirect_to"] == "dev"
|
|
967
|
+
assert "green" in data["redirect_reason"]
|
|
968
|
+
|
|
969
|
+
|
|
970
|
+
# =============================================================================
|
|
971
|
+
# Prime v2 Tests - CLI Flags
|
|
972
|
+
# =============================================================================
|
|
973
|
+
|
|
974
|
+
|
|
975
|
+
class TestCLIFlagsV2:
|
|
976
|
+
"""Tests for new CLI flags in Prime v2."""
|
|
977
|
+
|
|
978
|
+
def test_json_flag(self, tmp_path: Path, capsys) -> None:
|
|
979
|
+
"""Test --json flag."""
|
|
980
|
+
import json
|
|
981
|
+
|
|
982
|
+
pf_dir = tmp_path / ".pennyfarthing"
|
|
983
|
+
pf_dir.mkdir()
|
|
984
|
+
|
|
985
|
+
with patch("pennyfarthing_scripts.prime.cli.get_project_root", return_value=tmp_path):
|
|
986
|
+
result = main(["--json", "--no-workflow", "--no-register"])
|
|
987
|
+
|
|
988
|
+
assert result == 0
|
|
989
|
+
captured = capsys.readouterr()
|
|
990
|
+
# Should be valid JSON
|
|
991
|
+
data = json.loads(captured.out)
|
|
992
|
+
assert "agent_name" in data
|
|
993
|
+
|
|
994
|
+
def test_no_persona_flag(self, tmp_path: Path, capsys) -> None:
|
|
995
|
+
"""Test --no-persona flag skips persona loading."""
|
|
996
|
+
import yaml
|
|
997
|
+
|
|
998
|
+
# Setup
|
|
999
|
+
pf_dir = tmp_path / ".pennyfarthing"
|
|
1000
|
+
pf_dir.mkdir()
|
|
1001
|
+
agents_dir = pf_dir / "agents"
|
|
1002
|
+
agents_dir.mkdir()
|
|
1003
|
+
(agents_dir / "dev.md").write_text("# Dev Agent")
|
|
1004
|
+
|
|
1005
|
+
# Create config and theme that would normally load
|
|
1006
|
+
(pf_dir / "config.local.yaml").write_text(yaml.dump({"theme": "test"}))
|
|
1007
|
+
themes_dir = pf_dir / "personas" / "themes"
|
|
1008
|
+
themes_dir.mkdir(parents=True)
|
|
1009
|
+
(themes_dir / "test.yaml").write_text(yaml.dump({
|
|
1010
|
+
"agents": {"dev": {"character": "Test", "style": "s", "role": "r"}}
|
|
1011
|
+
}))
|
|
1012
|
+
|
|
1013
|
+
with patch("pennyfarthing_scripts.prime.cli.get_project_root", return_value=tmp_path):
|
|
1014
|
+
result = main(["--agent", "dev", "--no-persona", "--no-workflow", "--no-register"])
|
|
1015
|
+
|
|
1016
|
+
assert result == 0
|
|
1017
|
+
captured = capsys.readouterr()
|
|
1018
|
+
# Should NOT contain persona XML
|
|
1019
|
+
assert "<persona" not in captured.out
|
|
1020
|
+
# Should contain agent definition
|
|
1021
|
+
assert "# Dev Agent" in captured.out
|
|
1022
|
+
|
|
1023
|
+
def test_no_workflow_flag(self, tmp_path: Path, capsys) -> None:
|
|
1024
|
+
"""Test --no-workflow flag skips workflow detection."""
|
|
1025
|
+
pf_dir = tmp_path / ".pennyfarthing"
|
|
1026
|
+
pf_dir.mkdir()
|
|
1027
|
+
|
|
1028
|
+
with patch("pennyfarthing_scripts.prime.cli.get_project_root", return_value=tmp_path):
|
|
1029
|
+
result = main(["--no-workflow", "--no-register"])
|
|
1030
|
+
|
|
1031
|
+
assert result == 0
|
|
1032
|
+
captured = capsys.readouterr()
|
|
1033
|
+
# Should NOT contain workflow state header
|
|
1034
|
+
assert "# Workflow State" not in captured.out
|
|
1035
|
+
|
|
1036
|
+
def test_session_id_flag(self, tmp_path: Path) -> None:
|
|
1037
|
+
"""Test --session-id flag uses explicit ID."""
|
|
1038
|
+
from pennyfarthing_scripts.prime.session import get_session_agent
|
|
1039
|
+
|
|
1040
|
+
pf_dir = tmp_path / ".pennyfarthing"
|
|
1041
|
+
pf_dir.mkdir()
|
|
1042
|
+
|
|
1043
|
+
with patch("pennyfarthing_scripts.prime.cli.get_project_root", return_value=tmp_path):
|
|
1044
|
+
result = main(["--agent", "sm", "--session-id", "explicit-123", "--no-workflow"])
|
|
1045
|
+
|
|
1046
|
+
assert result == 0
|
|
1047
|
+
|
|
1048
|
+
# Verify session was created with explicit ID
|
|
1049
|
+
agent = get_session_agent("explicit-123", tmp_path)
|
|
1050
|
+
assert agent == "sm"
|