@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
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Prime CLI -
|
|
2
|
+
Prime CLI - Unified agent bootstrap for Pennyfarthing.
|
|
3
3
|
|
|
4
4
|
Usage:
|
|
5
5
|
python -m pennyfarthing_scripts.prime [options]
|
|
6
6
|
|
|
7
7
|
Options:
|
|
8
|
-
--agent <name>
|
|
9
|
-
--minimal
|
|
10
|
-
--full
|
|
11
|
-
--quiet
|
|
8
|
+
--agent <name> Load agent definition and sidecar
|
|
9
|
+
--minimal Skip all context (fastest)
|
|
10
|
+
--full Include domain docs from .claude/project/
|
|
11
|
+
--quiet Suppress section headers
|
|
12
|
+
--json Output JSON (for Cyclist integration)
|
|
13
|
+
--no-persona Skip persona loading
|
|
14
|
+
--no-workflow Skip workflow detection
|
|
15
|
+
--no-register Skip session registration
|
|
16
|
+
--session-id ID Use explicit session ID
|
|
12
17
|
"""
|
|
13
18
|
|
|
14
19
|
from __future__ import annotations
|
|
15
20
|
|
|
16
21
|
import argparse
|
|
22
|
+
import json
|
|
17
23
|
import sys
|
|
18
24
|
from pathlib import Path
|
|
19
25
|
|
|
@@ -26,6 +32,16 @@ from pennyfarthing_scripts.prime.loader import (
|
|
|
26
32
|
load_sidecars,
|
|
27
33
|
load_sprint_context,
|
|
28
34
|
)
|
|
35
|
+
from pennyfarthing_scripts.prime.models import PrimeResult, WorkflowState
|
|
36
|
+
from pennyfarthing_scripts.prime.persona import (
|
|
37
|
+
format_persona_output,
|
|
38
|
+
get_crew_manifest,
|
|
39
|
+
get_user_title,
|
|
40
|
+
is_character_voice_enabled,
|
|
41
|
+
load_persona,
|
|
42
|
+
)
|
|
43
|
+
from pennyfarthing_scripts.prime.session import cleanup_old_sessions, register_session
|
|
44
|
+
from pennyfarthing_scripts.prime.workflow import check_redirect, detect_workflow_state
|
|
29
45
|
|
|
30
46
|
|
|
31
47
|
def _print_header(title: str, quiet: bool) -> None:
|
|
@@ -40,28 +56,73 @@ def _print_header(title: str, quiet: bool) -> None:
|
|
|
40
56
|
print(f"# {title}")
|
|
41
57
|
|
|
42
58
|
|
|
59
|
+
def _format_workflow_state_text(result: PrimeResult) -> str:
|
|
60
|
+
"""Format workflow state as text output.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
result: PrimeResult with workflow status
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Formatted text block
|
|
67
|
+
"""
|
|
68
|
+
if not result.workflow_status:
|
|
69
|
+
return ""
|
|
70
|
+
|
|
71
|
+
ws = result.workflow_status
|
|
72
|
+
lines = [
|
|
73
|
+
f"state: {ws.state.value}",
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
if ws.story_id:
|
|
77
|
+
lines.append(f"story_id: {ws.story_id}")
|
|
78
|
+
if ws.phase:
|
|
79
|
+
lines.append(f"phase: {ws.phase}")
|
|
80
|
+
if ws.phase_owner:
|
|
81
|
+
lines.append(f"phase_owner: {ws.phase_owner}")
|
|
82
|
+
if ws.workflow:
|
|
83
|
+
lines.append(f"workflow: {ws.workflow}")
|
|
84
|
+
if ws.backlog_count > 0:
|
|
85
|
+
lines.append(f"backlog_count: {ws.backlog_count}")
|
|
86
|
+
|
|
87
|
+
return "\n".join(lines)
|
|
88
|
+
|
|
89
|
+
|
|
43
90
|
def prime(
|
|
44
91
|
agent_name: str | None = None,
|
|
45
92
|
minimal: bool = False,
|
|
46
93
|
full: bool = False,
|
|
47
94
|
quiet: bool = False,
|
|
95
|
+
json_output: bool = False,
|
|
96
|
+
no_persona: bool = False,
|
|
97
|
+
no_workflow: bool = False,
|
|
98
|
+
no_register: bool = False,
|
|
99
|
+
session_id: str | None = None,
|
|
48
100
|
project_root: Path | None = None,
|
|
49
101
|
) -> int:
|
|
50
102
|
"""Load and print context.
|
|
51
103
|
|
|
52
104
|
Loads context in priority order (optimized for attention):
|
|
53
|
-
1.
|
|
54
|
-
2.
|
|
55
|
-
3.
|
|
56
|
-
4.
|
|
57
|
-
5.
|
|
58
|
-
6.
|
|
105
|
+
1. Workflow State (HIGHEST PRIORITY - routing decision)
|
|
106
|
+
2. Agent definition
|
|
107
|
+
3. Persona (character voice)
|
|
108
|
+
4. Behavior guide
|
|
109
|
+
5. Crew manifest (handoff reference)
|
|
110
|
+
6. Sprint context
|
|
111
|
+
7. Session context (header + last assessment)
|
|
112
|
+
8. Sidecars (patterns, gotchas, decisions)
|
|
113
|
+
9. Domain docs (--full only)
|
|
114
|
+
10. Redirect marker (if wrong agent)
|
|
59
115
|
|
|
60
116
|
Args:
|
|
61
117
|
agent_name: Name of agent to load context for
|
|
62
118
|
minimal: If True, skip all context (fastest)
|
|
63
119
|
full: If True, include domain docs
|
|
64
120
|
quiet: If True, suppress section headers
|
|
121
|
+
json_output: If True, output JSON instead of text
|
|
122
|
+
no_persona: If True, skip persona loading
|
|
123
|
+
no_workflow: If True, skip workflow detection
|
|
124
|
+
no_register: If True, skip session registration
|
|
125
|
+
session_id: Explicit session ID (generated if not provided)
|
|
65
126
|
project_root: Project root path (auto-detected if not provided)
|
|
66
127
|
|
|
67
128
|
Returns:
|
|
@@ -69,81 +130,137 @@ def prime(
|
|
|
69
130
|
"""
|
|
70
131
|
# Stop immediately for minimal mode
|
|
71
132
|
if minimal:
|
|
133
|
+
if json_output:
|
|
134
|
+
print(json.dumps({"minimal": True}))
|
|
72
135
|
return 0
|
|
73
136
|
|
|
74
137
|
root = project_root or get_project_root()
|
|
75
138
|
|
|
139
|
+
# Build result for JSON output
|
|
140
|
+
result = PrimeResult(agent_name=agent_name or "")
|
|
141
|
+
|
|
76
142
|
# ==========================================================================
|
|
77
|
-
#
|
|
143
|
+
# Session registration (if enabled)
|
|
144
|
+
# ==========================================================================
|
|
145
|
+
if agent_name and not no_register:
|
|
146
|
+
# Clean up old sessions first
|
|
147
|
+
cleanup_old_sessions(root)
|
|
148
|
+
# Register this session
|
|
149
|
+
session_info = register_session(agent_name, session_id, root)
|
|
150
|
+
result.session_id = session_info.session_id
|
|
151
|
+
|
|
152
|
+
# ==========================================================================
|
|
153
|
+
# PRIORITY 1: Workflow State (HIGHEST ATTENTION ZONE - routing decision)
|
|
154
|
+
# ==========================================================================
|
|
155
|
+
if not no_workflow:
|
|
156
|
+
workflow_status = detect_workflow_state(root)
|
|
157
|
+
result.workflow_status = workflow_status
|
|
158
|
+
|
|
159
|
+
# Check for redirect
|
|
160
|
+
if agent_name and workflow_status.state == WorkflowState.IN_PROGRESS_STATE:
|
|
161
|
+
redirect = check_redirect(workflow_status, agent_name)
|
|
162
|
+
if redirect:
|
|
163
|
+
result.redirect_to, result.redirect_reason = redirect
|
|
164
|
+
|
|
165
|
+
if not json_output:
|
|
166
|
+
_print_header("Workflow State", quiet)
|
|
167
|
+
print(_format_workflow_state_text(result))
|
|
168
|
+
|
|
169
|
+
# ==========================================================================
|
|
170
|
+
# PRIORITY 2: Agent definition
|
|
78
171
|
# ==========================================================================
|
|
79
172
|
if agent_name:
|
|
80
173
|
agent_content = load_agent_definition(agent_name, root)
|
|
81
|
-
if agent_content:
|
|
174
|
+
if agent_content and not json_output:
|
|
82
175
|
_print_header(f"Agent Definition: {agent_name}", quiet)
|
|
83
176
|
print(agent_content)
|
|
84
177
|
|
|
85
178
|
# ==========================================================================
|
|
86
|
-
# PRIORITY
|
|
179
|
+
# PRIORITY 3: Persona (if enabled)
|
|
87
180
|
# ==========================================================================
|
|
88
|
-
if agent_name:
|
|
181
|
+
if agent_name and not no_persona and is_character_voice_enabled(root):
|
|
182
|
+
persona, theme = load_persona(agent_name, root)
|
|
183
|
+
if persona and theme:
|
|
184
|
+
result.persona = persona
|
|
185
|
+
result.theme = theme
|
|
186
|
+
|
|
187
|
+
if not json_output:
|
|
188
|
+
crew = get_crew_manifest(root)
|
|
189
|
+
result.crew = crew
|
|
190
|
+
user_title = get_user_title(root)
|
|
191
|
+
_print_header(f"Persona: {persona.character} ({agent_name})", quiet)
|
|
192
|
+
print(format_persona_output(persona, theme, agent_name, crew, user_title))
|
|
193
|
+
|
|
194
|
+
# ==========================================================================
|
|
195
|
+
# PRIORITY 4: Agent behavior guide
|
|
196
|
+
# ==========================================================================
|
|
197
|
+
if agent_name and not json_output:
|
|
89
198
|
guide_content = load_behavior_guide(root)
|
|
90
199
|
if guide_content:
|
|
91
200
|
_print_header("Agent Behavior Guide", quiet)
|
|
92
201
|
print(guide_content)
|
|
93
202
|
|
|
94
203
|
# ==========================================================================
|
|
95
|
-
# PRIORITY
|
|
204
|
+
# PRIORITY 5: Sprint context
|
|
96
205
|
# ==========================================================================
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
206
|
+
if not json_output:
|
|
207
|
+
sprint_content = load_sprint_context(root)
|
|
208
|
+
if sprint_content:
|
|
209
|
+
_print_header("Sprint Context", quiet)
|
|
210
|
+
print(sprint_content)
|
|
101
211
|
|
|
102
212
|
# ==========================================================================
|
|
103
|
-
# PRIORITY
|
|
213
|
+
# PRIORITY 6: Session context
|
|
104
214
|
# ==========================================================================
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
215
|
+
if not json_output:
|
|
216
|
+
session_result = load_session_context(root)
|
|
217
|
+
if session_result:
|
|
218
|
+
filename, header, assessment = session_result
|
|
219
|
+
_print_header(f"Active Session: {filename}", quiet)
|
|
220
|
+
|
|
221
|
+
# Print header
|
|
222
|
+
if header:
|
|
223
|
+
print(header)
|
|
224
|
+
|
|
225
|
+
# Print last assessment with separator
|
|
226
|
+
if assessment:
|
|
227
|
+
print()
|
|
228
|
+
print("---")
|
|
229
|
+
print(assessment)
|
|
119
230
|
|
|
120
231
|
# ==========================================================================
|
|
121
|
-
# PRIORITY
|
|
232
|
+
# PRIORITY 7: Sidecars (LOWEST PRIORITY)
|
|
122
233
|
# ==========================================================================
|
|
123
|
-
if agent_name:
|
|
234
|
+
if agent_name and not json_output:
|
|
124
235
|
sidecars = load_sidecars(agent_name, root)
|
|
125
236
|
for filename, content in sidecars.items():
|
|
126
237
|
_print_header(f"Agent Sidecar: {filename}", quiet)
|
|
127
238
|
print(content)
|
|
128
239
|
|
|
129
240
|
# ==========================================================================
|
|
130
|
-
# PRIORITY
|
|
241
|
+
# PRIORITY 8: Domain docs (--full only)
|
|
131
242
|
# ==========================================================================
|
|
132
|
-
if full:
|
|
243
|
+
if full and not json_output:
|
|
133
244
|
domain_docs = load_domain_docs(root)
|
|
134
245
|
for filename, content in domain_docs:
|
|
135
246
|
_print_header(filename, quiet)
|
|
136
247
|
print(content)
|
|
137
248
|
|
|
138
249
|
# ==========================================================================
|
|
139
|
-
#
|
|
250
|
+
# PRIORITY 9: Redirect marker (if wrong agent activated)
|
|
140
251
|
# ==========================================================================
|
|
141
|
-
if
|
|
252
|
+
if result.redirect_to and not json_output:
|
|
142
253
|
print()
|
|
143
|
-
print("
|
|
144
|
-
print("
|
|
145
|
-
print("
|
|
146
|
-
print("
|
|
254
|
+
print("=" * 60)
|
|
255
|
+
print(f"REDIRECT: You ({agent_name}) should hand off to {result.redirect_to}")
|
|
256
|
+
print(f"Reason: {result.redirect_reason}")
|
|
257
|
+
print("=" * 60)
|
|
258
|
+
|
|
259
|
+
# ==========================================================================
|
|
260
|
+
# JSON output
|
|
261
|
+
# ==========================================================================
|
|
262
|
+
if json_output:
|
|
263
|
+
print(json.dumps(result.to_dict(), indent=2))
|
|
147
264
|
|
|
148
265
|
return 0
|
|
149
266
|
|
|
@@ -159,22 +276,29 @@ def main(args: list[str] | None = None) -> int:
|
|
|
159
276
|
"""
|
|
160
277
|
parser = argparse.ArgumentParser(
|
|
161
278
|
prog="prime",
|
|
162
|
-
description="
|
|
279
|
+
description="Unified agent bootstrap for Pennyfarthing",
|
|
163
280
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
164
281
|
epilog="""
|
|
165
282
|
Loads context in priority order (optimized for attention):
|
|
166
|
-
1.
|
|
167
|
-
2.
|
|
168
|
-
3.
|
|
169
|
-
4.
|
|
170
|
-
5.
|
|
171
|
-
6.
|
|
283
|
+
1. Workflow State (HIGHEST - routing decision)
|
|
284
|
+
2. Agent definition
|
|
285
|
+
3. Persona (character voice)
|
|
286
|
+
4. Behavior guide
|
|
287
|
+
5. Crew manifest (handoff reference)
|
|
288
|
+
6. Sprint context
|
|
289
|
+
7. Session context (header + last assessment)
|
|
290
|
+
8. Sidecars (patterns, gotchas, decisions)
|
|
291
|
+
9. Domain docs (--full only)
|
|
292
|
+
10. Redirect marker (if wrong agent)
|
|
172
293
|
|
|
173
294
|
Examples:
|
|
174
|
-
prime --agent
|
|
295
|
+
prime --agent sm
|
|
296
|
+
prime --agent dev --json
|
|
175
297
|
prime --agent tea --full
|
|
176
298
|
prime --minimal
|
|
177
299
|
prime --quiet --agent sm
|
|
300
|
+
prime --agent dev --no-persona
|
|
301
|
+
prime --agent dev --session-id abc123
|
|
178
302
|
""",
|
|
179
303
|
)
|
|
180
304
|
|
|
@@ -198,6 +322,32 @@ Examples:
|
|
|
198
322
|
action="store_true",
|
|
199
323
|
help="Suppress section headers",
|
|
200
324
|
)
|
|
325
|
+
parser.add_argument(
|
|
326
|
+
"--json",
|
|
327
|
+
action="store_true",
|
|
328
|
+
dest="json_output",
|
|
329
|
+
help="Output JSON (for Cyclist integration)",
|
|
330
|
+
)
|
|
331
|
+
parser.add_argument(
|
|
332
|
+
"--no-persona",
|
|
333
|
+
action="store_true",
|
|
334
|
+
help="Skip persona loading (disable character voice)",
|
|
335
|
+
)
|
|
336
|
+
parser.add_argument(
|
|
337
|
+
"--no-workflow",
|
|
338
|
+
action="store_true",
|
|
339
|
+
help="Skip workflow state detection",
|
|
340
|
+
)
|
|
341
|
+
parser.add_argument(
|
|
342
|
+
"--no-register",
|
|
343
|
+
action="store_true",
|
|
344
|
+
help="Skip session registration",
|
|
345
|
+
)
|
|
346
|
+
parser.add_argument(
|
|
347
|
+
"--session-id",
|
|
348
|
+
metavar="ID",
|
|
349
|
+
help="Use explicit session ID",
|
|
350
|
+
)
|
|
201
351
|
|
|
202
352
|
parsed = parser.parse_args(args)
|
|
203
353
|
|
|
@@ -207,6 +357,11 @@ Examples:
|
|
|
207
357
|
minimal=parsed.minimal,
|
|
208
358
|
full=parsed.full,
|
|
209
359
|
quiet=parsed.quiet,
|
|
360
|
+
json_output=parsed.json_output,
|
|
361
|
+
no_persona=parsed.no_persona,
|
|
362
|
+
no_workflow=parsed.no_workflow,
|
|
363
|
+
no_register=parsed.no_register,
|
|
364
|
+
session_id=parsed.session_id,
|
|
210
365
|
)
|
|
211
366
|
except FileNotFoundError as e:
|
|
212
367
|
print(f"Error: {e}", file=sys.stderr)
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Data models for Prime v2.
|
|
3
|
+
|
|
4
|
+
Provides structured types for workflow state, persona, and session data.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class WorkflowState(Enum):
|
|
15
|
+
"""Workflow states detected by prime."""
|
|
16
|
+
|
|
17
|
+
FINISH_STATE = "FINISH_STATE"
|
|
18
|
+
IN_PROGRESS_STATE = "IN_PROGRESS_STATE"
|
|
19
|
+
NEW_WORK_STATE = "NEW_WORK_STATE"
|
|
20
|
+
EMPTY_BACKLOG_STATE = "EMPTY_BACKLOG_STATE"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class WorkflowStatus:
|
|
25
|
+
"""Result of workflow state detection.
|
|
26
|
+
|
|
27
|
+
Attributes:
|
|
28
|
+
state: Current workflow state
|
|
29
|
+
story_id: Active story ID if in progress/finish
|
|
30
|
+
phase: Current workflow phase (setup, red, green, review, finish)
|
|
31
|
+
phase_owner: Agent that owns the current phase
|
|
32
|
+
workflow: Workflow name (tdd, trivial, bdd, etc.)
|
|
33
|
+
backlog_count: Number of stories in backlog (for NEW_WORK_STATE)
|
|
34
|
+
session_file: Path to active session file if exists
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
state: WorkflowState
|
|
38
|
+
story_id: str | None = None
|
|
39
|
+
phase: str | None = None
|
|
40
|
+
phase_owner: str | None = None
|
|
41
|
+
workflow: str | None = None
|
|
42
|
+
backlog_count: int = 0
|
|
43
|
+
session_file: str | None = None
|
|
44
|
+
|
|
45
|
+
def to_dict(self) -> dict[str, Any]:
|
|
46
|
+
"""Convert to dictionary for JSON serialization."""
|
|
47
|
+
return {
|
|
48
|
+
"state": self.state.value,
|
|
49
|
+
"story_id": self.story_id,
|
|
50
|
+
"phase": self.phase,
|
|
51
|
+
"phase_owner": self.phase_owner,
|
|
52
|
+
"workflow": self.workflow,
|
|
53
|
+
"backlog_count": self.backlog_count,
|
|
54
|
+
"session_file": self.session_file,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class Persona:
|
|
60
|
+
"""Agent persona from theme configuration.
|
|
61
|
+
|
|
62
|
+
Attributes:
|
|
63
|
+
character: Character name (e.g., "Camina Drummer")
|
|
64
|
+
style: Communication style description
|
|
65
|
+
role: Role description
|
|
66
|
+
quote: Optional signature quote
|
|
67
|
+
trait: Optional personality trait
|
|
68
|
+
quirk: Optional personality quirk
|
|
69
|
+
motto: Optional character motto
|
|
70
|
+
helper_name: Optional helper/assistant name
|
|
71
|
+
helper_style: Optional helper communication style
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
character: str
|
|
75
|
+
style: str
|
|
76
|
+
role: str
|
|
77
|
+
quote: str | None = None
|
|
78
|
+
trait: str | None = None
|
|
79
|
+
quirk: str | None = None
|
|
80
|
+
motto: str | None = None
|
|
81
|
+
helper_name: str | None = None
|
|
82
|
+
helper_style: str | None = None
|
|
83
|
+
|
|
84
|
+
def to_dict(self) -> dict[str, Any]:
|
|
85
|
+
"""Convert to dictionary for JSON serialization."""
|
|
86
|
+
result = {
|
|
87
|
+
"character": self.character,
|
|
88
|
+
"style": self.style,
|
|
89
|
+
"role": self.role,
|
|
90
|
+
}
|
|
91
|
+
if self.quote:
|
|
92
|
+
result["quote"] = self.quote
|
|
93
|
+
if self.trait:
|
|
94
|
+
result["trait"] = self.trait
|
|
95
|
+
if self.quirk:
|
|
96
|
+
result["quirk"] = self.quirk
|
|
97
|
+
if self.motto:
|
|
98
|
+
result["motto"] = self.motto
|
|
99
|
+
if self.helper_name:
|
|
100
|
+
result["helper_name"] = self.helper_name
|
|
101
|
+
if self.helper_style:
|
|
102
|
+
result["helper_style"] = self.helper_style
|
|
103
|
+
return result
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@dataclass
|
|
107
|
+
class CrewMember:
|
|
108
|
+
"""A crew member in the theme manifest.
|
|
109
|
+
|
|
110
|
+
Attributes:
|
|
111
|
+
role: Agent role (sm, tea, dev, etc.)
|
|
112
|
+
character: Character name
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
role: str
|
|
116
|
+
character: str
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@dataclass
|
|
120
|
+
class SessionInfo:
|
|
121
|
+
"""Session registration information.
|
|
122
|
+
|
|
123
|
+
Attributes:
|
|
124
|
+
session_id: Unique session identifier
|
|
125
|
+
agent_name: Name of the registered agent
|
|
126
|
+
file_path: Path to the session file
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
session_id: str
|
|
130
|
+
agent_name: str
|
|
131
|
+
file_path: str
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@dataclass
|
|
135
|
+
class PrimeResult:
|
|
136
|
+
"""Complete result from prime() for JSON output.
|
|
137
|
+
|
|
138
|
+
Attributes:
|
|
139
|
+
agent_name: Requested agent name
|
|
140
|
+
workflow_status: Current workflow state
|
|
141
|
+
persona: Loaded persona (if enabled)
|
|
142
|
+
theme: Active theme name
|
|
143
|
+
redirect_to: Agent to redirect to (if wrong agent activated)
|
|
144
|
+
redirect_reason: Reason for redirect
|
|
145
|
+
session_id: Session ID (if registered)
|
|
146
|
+
crew: List of crew members for handoff reference
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
agent_name: str
|
|
150
|
+
workflow_status: WorkflowStatus | None = None
|
|
151
|
+
persona: Persona | None = None
|
|
152
|
+
theme: str | None = None
|
|
153
|
+
redirect_to: str | None = None
|
|
154
|
+
redirect_reason: str | None = None
|
|
155
|
+
session_id: str | None = None
|
|
156
|
+
crew: list[CrewMember] = field(default_factory=list)
|
|
157
|
+
|
|
158
|
+
def to_dict(self) -> dict[str, Any]:
|
|
159
|
+
"""Convert to dictionary for JSON serialization."""
|
|
160
|
+
return {
|
|
161
|
+
"agent_name": self.agent_name,
|
|
162
|
+
"workflow_status": self.workflow_status.to_dict() if self.workflow_status else None,
|
|
163
|
+
"persona": self.persona.to_dict() if self.persona else None,
|
|
164
|
+
"theme": self.theme,
|
|
165
|
+
"redirect_to": self.redirect_to,
|
|
166
|
+
"redirect_reason": self.redirect_reason,
|
|
167
|
+
"session_id": self.session_id,
|
|
168
|
+
"crew": [{"role": c.role, "character": c.character} for c in self.crew],
|
|
169
|
+
}
|