@pennyfarthing/core 8.0.4 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/package.json +1 -1
  2. package/pennyfarthing-dist/agents/reviewer.md +21 -4
  3. package/pennyfarthing-dist/agents/sm.md +10 -0
  4. package/pennyfarthing-dist/commands/architect.md +1 -1
  5. package/pennyfarthing-dist/commands/dev.md +1 -1
  6. package/pennyfarthing-dist/commands/devops.md +1 -1
  7. package/pennyfarthing-dist/commands/health-check.md +1 -1
  8. package/pennyfarthing-dist/commands/orchestrator.md +1 -1
  9. package/pennyfarthing-dist/commands/parallel-work.md +2 -2
  10. package/pennyfarthing-dist/commands/pm.md +1 -1
  11. package/pennyfarthing-dist/commands/prime.md +18 -22
  12. package/pennyfarthing-dist/commands/reviewer.md +1 -1
  13. package/pennyfarthing-dist/commands/set-theme.md +1 -1
  14. package/pennyfarthing-dist/commands/sm.md +1 -1
  15. package/pennyfarthing-dist/commands/sprint.md +13 -4
  16. package/pennyfarthing-dist/commands/tea.md +1 -1
  17. package/pennyfarthing-dist/commands/tech-writer.md +1 -1
  18. package/pennyfarthing-dist/commands/ux-designer.md +1 -1
  19. package/pennyfarthing-dist/commands/work.md +2 -2
  20. package/pennyfarthing-dist/guides/agent-behavior.md +15 -1
  21. package/pennyfarthing-dist/personas/themes/rome.yaml +11 -11
  22. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  23. package/pennyfarthing_scripts/__pycache__/config.cpython-314.pyc +0 -0
  24. package/pennyfarthing_scripts/__pycache__/jira_bidirectional_sync.cpython-314.pyc +0 -0
  25. package/pennyfarthing_scripts/__pycache__/jira_epic_creation.cpython-314.pyc +0 -0
  26. package/pennyfarthing_scripts/__pycache__/jira_sync.cpython-314.pyc +0 -0
  27. package/pennyfarthing_scripts/__pycache__/jira_sync_story.cpython-314.pyc +0 -0
  28. package/pennyfarthing_scripts/__pycache__/output.cpython-314.pyc +0 -0
  29. package/pennyfarthing_scripts/__pycache__/workflow.cpython-314.pyc +0 -0
  30. package/pennyfarthing_scripts/brownfield/__pycache__/__init__.cpython-314.pyc +0 -0
  31. package/pennyfarthing_scripts/brownfield/__pycache__/__main__.cpython-314.pyc +0 -0
  32. package/pennyfarthing_scripts/brownfield/__pycache__/cli.cpython-314.pyc +0 -0
  33. package/pennyfarthing_scripts/brownfield/__pycache__/discover.cpython-314.pyc +0 -0
  34. package/pennyfarthing_scripts/cli.py +168 -0
  35. package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
  36. package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
  37. package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
  38. package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
  39. package/pennyfarthing_scripts/prime/cli.py +8 -1
  40. package/pennyfarthing_scripts/sprint/__pycache__/__main__.cpython-314.pyc +0 -0
  41. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  42. package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-314.pyc +0 -0
  43. package/pennyfarthing_scripts/sprint/cli.py +144 -84
  44. package/pennyfarthing_scripts/story/__pycache__/__init__.cpython-314.pyc +0 -0
  45. package/pennyfarthing_scripts/story/__pycache__/__main__.cpython-314.pyc +0 -0
  46. package/pennyfarthing_scripts/story/__pycache__/cli.cpython-314.pyc +0 -0
  47. package/pennyfarthing_scripts/story/__pycache__/create.cpython-314.pyc +0 -0
  48. package/pennyfarthing_scripts/story/__pycache__/size.cpython-314.pyc +0 -0
  49. package/pennyfarthing_scripts/story/__pycache__/template.cpython-314.pyc +0 -0
  50. package/pennyfarthing_scripts/tests/__pycache__/test_brownfield.cpython-314-pytest-9.0.2.pyc +0 -0
  51. package/pennyfarthing_scripts/tests/__pycache__/test_cli_modules.cpython-314-pytest-9.0.2.pyc +0 -0
  52. package/pennyfarthing_scripts/tests/__pycache__/test_common.cpython-314-pytest-9.0.2.pyc +0 -0
  53. package/pennyfarthing_scripts/tests/__pycache__/test_git_utils.cpython-314-pytest-9.0.2.pyc +0 -0
  54. package/pennyfarthing_scripts/tests/__pycache__/test_jira_package.cpython-314-pytest-9.0.2.pyc +0 -0
  55. package/pennyfarthing_scripts/tests/__pycache__/test_package_structure.cpython-314-pytest-9.0.2.pyc +0 -0
  56. package/pennyfarthing_scripts/tests/__pycache__/test_prime.cpython-314-pytest-9.0.2.pyc +0 -0
  57. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_package.cpython-314-pytest-9.0.2.pyc +0 -0
  58. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
  59. package/pennyfarthing_scripts/tests/__pycache__/test_story_package.cpython-314-pytest-9.0.2.pyc +0 -0
  60. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
  61. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_cli.cpython-314-pytest-9.0.2.pyc +0 -0
  62. package/pennyfarthing_scripts/tests/test_workflow_check.py +341 -0
  63. package/pennyfarthing_scripts/workflow.py +104 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pennyfarthing/core",
3
- "version": "8.0.4",
3
+ "version": "8.1.0",
4
4
  "description": "Claude Code agent framework with TDD workflow and persona system",
5
5
  "type": "module",
6
6
  "bin": {
@@ -124,6 +124,7 @@ OWNER=$(.pennyfarthing/scripts/workflow/phase-owner.sh {workflow} {phase})
124
124
  ## MANDATORY: Complete Before Exiting
125
125
 
126
126
  - [ ] Write Reviewer Assessment to session file
127
+ - [ ] **If APPROVED:** Merge PR directly with `gh pr merge {PR_NUMBER} --merge --delete-branch`
127
128
  - [ ] Spawn `handoff` subagent with VERDICT (approved/rejected)
128
129
  - [ ] Verify handoff completed (subagent emits marker)
129
130
  </handoff-gate>
@@ -158,16 +159,32 @@ OWNER=$(.pennyfarthing/scripts/workflow/phase-owner.sh {workflow} {phase})
158
159
  <exit-sequence>
159
160
  ## Exit Sequence
160
161
 
162
+ ### If APPROVED:
161
163
  1. Write Reviewer Assessment to session file
162
- 2. Spawn `handoff` subagent with VERDICT
163
- 3. Await `HANDOFF_RESULT` with `next_agent`
164
+ 2. **Merge the PR directly** (don't wait for SM):
165
+ ```bash
166
+ gh pr merge {PR_NUMBER} --merge --delete-branch
167
+ ```
168
+ 3. Update session phase to `finish`
169
+ 4. Spawn `handoff` subagent with VERDICT=approved
170
+ 5. Await `HANDOFF_RESULT` with `next_agent` (will be `sm`)
171
+ 6. **ABSOLUTE LAST ACTION:**
172
+ ```bash
173
+ .pennyfarthing/scripts/core/handoff-marker.sh sm
174
+ ```
175
+ 7. Output result verbatim and EXIT
176
+
177
+ ### If REJECTED:
178
+ 1. Write Reviewer Assessment to session file
179
+ 2. Spawn `handoff` subagent with VERDICT=rejected
180
+ 3. Await `HANDOFF_RESULT` with `next_agent` (will be `dev`)
164
181
  4. **ABSOLUTE LAST ACTION:**
165
182
  ```bash
166
- .pennyfarthing/scripts/handoff/handoff-marker.sh {next_agent}
183
+ .pennyfarthing/scripts/core/handoff-marker.sh dev
167
184
  ```
168
185
  5. Output result verbatim and EXIT
169
186
 
170
- **Verdict routing:** APPROVED → sm | REJECTED → dev
187
+ **Verdict routing:** APPROVED → merge PR, then sm | REJECTED → dev
171
188
  </exit-sequence>
172
189
 
173
190
  <skills>
@@ -175,6 +175,16 @@ Present to user:
175
175
  - **Stepped workflow** → Tell user to run `/workflow start {workflow}` (no handoff)
176
176
  </new-work-flow>
177
177
 
178
+ <merge-gate>
179
+ ## Merge Gate (BLOCKING)
180
+
181
+ Before starting new work: `gh pr list --state open` - BLOCKS if any exist.
182
+
183
+ Open PRs → incomplete work → merge conflicts, stale branches, CI failures.
184
+
185
+ **Resolution:** Merge/close all PRs first. Use `/reviewer` to complete reviews.
186
+ </merge-gate>
187
+
178
188
  <gate>
179
189
  ## Pre-Handoff Checklist (BLOCKING)
180
190
 
@@ -3,5 +3,5 @@ description: System Architect - Technical design and architecture
3
3
  ---
4
4
 
5
5
  ```bash
6
- d="$PWD"; while [[ ! -d "$d/.claude" ]] && [[ "$d" != "/" ]]; do d="$(dirname "$d")"; done; "$d/.pennyfarthing/scripts/core/agent-session.sh" start "architect"
6
+ python3 -m pennyfarthing_scripts.cli agent start "architect"
7
7
  ```
@@ -3,5 +3,5 @@ description: Developer - Feature implementation and coding
3
3
  ---
4
4
 
5
5
  ```bash
6
- d="$PWD"; while [[ ! -d "$d/.claude" ]] && [[ "$d" != "/" ]]; do d="$(dirname "$d")"; done; "$d/.pennyfarthing/scripts/core/phase-check-start.sh" "dev"
6
+ python3 -m pennyfarthing_scripts.cli agent start "dev"
7
7
  ```
@@ -3,5 +3,5 @@ description: DevOps Engineer - Infrastructure and deployment automation
3
3
  ---
4
4
 
5
5
  ```bash
6
- d="$PWD"; while [[ ! -d "$d/.claude" ]] && [[ "$d" != "/" ]]; do d="$(dirname "$d")"; done; "$d/.pennyfarthing/scripts/core/agent-session.sh" start "devops"
6
+ python3 -m pennyfarthing_scripts.cli agent start "devops"
7
7
  ```
@@ -3,7 +3,7 @@ description: Check Pennyfarthing installation health and apply updates
3
3
  ---
4
4
 
5
5
  ```bash
6
- ./scripts/core/agent-session.sh start "devops"
6
+ python3 -m pennyfarthing_scripts.cli agent start "devops"
7
7
  ```
8
8
 
9
9
  <agent-activation>
@@ -3,5 +3,5 @@ description: Orchestrator - Coordinator of all agents and meta operations
3
3
  ---
4
4
 
5
5
  ```bash
6
- d="$PWD"; while [[ ! -d "$d/.claude" ]] && [[ "$d" != "/" ]]; do d="$(dirname "$d")"; done; "$d/.pennyfarthing/scripts/core/agent-session.sh" start "orchestrator"
6
+ python3 -m pennyfarthing_scripts.cli agent start "orchestrator"
7
7
  ```
@@ -3,7 +3,7 @@ description: Start parallel work in a new worktree
3
3
  ---
4
4
 
5
5
  ```bash
6
- ./scripts/core/agent-session.sh start "parallel-work"
6
+ python3 -m pennyfarthing_scripts.cli agent start "sm"
7
7
  ```
8
8
 
9
9
  <parallel-work-flow>
@@ -67,5 +67,5 @@ Invoke SM to complete story setup in the worktree context.
67
67
  </agent-activation>
68
68
 
69
69
  <agent-exit>
70
- On exit: Capture learnings to sidecar, run `./scripts/core/agent-session.sh stop`
70
+ On exit: Capture learnings to sidecar.
71
71
  </agent-exit>
@@ -3,5 +3,5 @@ description: Product Manager - Strategic planning and prioritization
3
3
  ---
4
4
 
5
5
  ```bash
6
- d="$PWD"; while [[ ! -d "$d/.claude" ]] && [[ "$d" != "/" ]]; do d="$(dirname "$d")"; done; "$d/.pennyfarthing/scripts/core/agent-session.sh" start "pm"
6
+ python3 -m pennyfarthing_scripts.cli agent start "pm"
7
7
  ```
@@ -4,7 +4,7 @@ description: Load essential project context at agent activation
4
4
 
5
5
  <purpose>
6
6
  Quickly load essential context files to reduce agent cold-start overhead.
7
- Automatically invoked on agent activation via agent-session.sh.
7
+ Automatically invoked on agent activation via `pf agent start`.
8
8
  </purpose>
9
9
 
10
10
  <when-to-use>
@@ -17,23 +17,23 @@ Automatically invoked on agent activation via agent-session.sh.
17
17
 
18
18
  ## Running /prime
19
19
 
20
- Use the prime.sh script:
20
+ Use the Python CLI:
21
21
 
22
22
  ```bash
23
23
  # Load all essential context (default)
24
- $CLAUDE_PROJECT_DIR/.pennyfarthing/scripts/prime.sh
24
+ python3 -m pennyfarthing_scripts.cli agent start "sm"
25
25
 
26
- # Minimal mode - CLAUDE.md only (fastest)
27
- $CLAUDE_PROJECT_DIR/.pennyfarthing/scripts/prime.sh --minimal
26
+ # Minimal mode - fastest startup
27
+ python3 -m pennyfarthing_scripts.cli agent start "sm" --minimal
28
28
 
29
29
  # Full mode - include domain docs
30
- $CLAUDE_PROJECT_DIR/.pennyfarthing/scripts/prime.sh --full
30
+ python3 -m pennyfarthing_scripts.cli agent start "sm" --full
31
31
 
32
- # Quiet mode - suppress headers (used by agent-session.sh)
33
- $CLAUDE_PROJECT_DIR/.pennyfarthing/scripts/prime.sh --quiet
32
+ # Skip persona loading
33
+ python3 -m pennyfarthing_scripts.cli agent start "sm" --no-persona
34
34
 
35
- # With agent sidecar loading
36
- $CLAUDE_PROJECT_DIR/.pennyfarthing/scripts/prime.sh --agent reviewer
35
+ # JSON output (for Cyclist integration)
36
+ python3 -m pennyfarthing_scripts.cli agent start "sm" --json
37
37
  ```
38
38
 
39
39
  ## Options
@@ -106,17 +106,16 @@ With `--quiet`, section headers are suppressed.
106
106
 
107
107
  <integration>
108
108
 
109
- ## agent-session.sh Integration
109
+ ## Python CLI Integration
110
110
 
111
111
  The `/prime` command is automatically invoked when agents activate:
112
112
 
113
113
  1. User invokes `/sm`, `/tea`, `/dev`, or `/reviewer`
114
- 2. `agent-session.sh start` runs
115
- 3. Persona XML is output
116
- 4. `/prime --quiet --agent <agent-name>` loads context automatically
117
- 5. Agent starts with full context AND their learned patterns loaded
114
+ 2. `pf agent start <name>` runs
115
+ 3. Context is loaded: workflow state, agent definition, persona, behavior guide, sprint context, session, sidecars
116
+ 4. Agent starts with full context AND their learned patterns loaded
118
117
 
119
- This reduces the "cold start" problem where agents must discover context through multiple file reads. The `--agent` flag ensures each agent gets their project-specific sidecar patterns immediately.
118
+ This reduces the "cold start" problem where agents must discover context through multiple file reads.
120
119
 
121
120
  ## Manual Refresh
122
121
 
@@ -130,11 +129,8 @@ If context becomes stale mid-session, run `/prime` manually:
130
129
  </integration>
131
130
 
132
131
  <reference>
133
- - **Script:** `.pennyfarthing/scripts/prime.sh`
134
- - **Called by:** agent-session.sh on agent start (with `--agent` flag)
135
- - **Loads:** CLAUDE.md, sprint, session, sidecar, shared context, shared behavior, tactical guide
132
+ - **CLI:** `pf agent start <name>` or `python3 -m pennyfarthing_scripts.cli agent start <name>`
133
+ - **Loads:** Workflow state, agent definition, persona, behavior guide, sprint context, session, sidecars
136
134
  - **Sidecar location:** `.pennyfarthing/sidecars/{agent}/*.md`
137
- - **Shared context:** `.pennyfarthing/guides/agent-behavior.md` (all agents)
138
- - **Shared behavior:** `.pennyfarthing/guides/agent-behavior.md` (all agents)
139
- - **Tactical guide:** `.pennyfarthing/guides/agent-behavior.md` (sm, tea, dev, reviewer only)
135
+ - **Behavior guide:** `.pennyfarthing/guides/agent-behavior.md` (all agents)
140
136
  </reference>
@@ -3,5 +3,5 @@ description: Code Reviewer - Critical code review and quality enforcement
3
3
  ---
4
4
 
5
5
  ```bash
6
- d="$PWD"; while [[ ! -d "$d/.claude" ]] && [[ "$d" != "/" ]]; do d="$(dirname "$d")"; done; "$d/.pennyfarthing/scripts/core/phase-check-start.sh" "reviewer"
6
+ python3 -m pennyfarthing_scripts.cli agent start "reviewer"
7
7
  ```
@@ -51,6 +51,6 @@ Change the active persona theme for all agents.
51
51
 
52
52
  6. Refresh the current agent's persona to apply the new theme:
53
53
  ```bash
54
- d="$PWD"; while [[ ! -d "$d/.claude" ]] && [[ "$d" != "/" ]]; do d="$(dirname "$d")"; done; "$d/.pennyfarthing/scripts/core/agent-session.sh" refresh
54
+ python3 -m pennyfarthing_scripts.cli agent start "sm"
55
55
  ```
56
56
  This outputs the updated persona. **Adopt the new character immediately** - do not continue using the old persona.
@@ -3,5 +3,5 @@ description: Scrum Master - Story coordination and sprint management
3
3
  ---
4
4
 
5
5
  ```bash
6
- d="$PWD"; while [[ ! -d "$d/.claude" ]] && [[ "$d" != "/" ]]; do d="$(dirname "$d")"; done; "$d/.pennyfarthing/scripts/core/phase-check-start.sh" "sm"
6
+ python3 -m pennyfarthing_scripts.cli agent start "sm"
7
7
  ```
@@ -50,6 +50,14 @@ Start work on a story. Primary entry point for development.
50
50
  | `next` | Auto-select highest priority story |
51
51
 
52
52
  ```bash
53
+ # MERGE GATE: Check for open PRs first (blocks if any exist)
54
+ OPEN_PRS=$(gh pr list --state open --json number --jq 'length' 2>/dev/null || echo "0")
55
+ if [[ "$OPEN_PRS" -gt 0 ]]; then
56
+ echo "⛔ BLOCKED: $OPEN_PRS open PR(s) - merge or close before starting new work"
57
+ gh pr list --state open
58
+ # Don't proceed until PRs are cleared
59
+ fi
60
+
53
61
  # Check if story is available
54
62
  .pennyfarthing/scripts/sprint/check-story.sh <story-id>
55
63
 
@@ -58,10 +66,11 @@ Start work on a story. Primary entry point for development.
58
66
 
59
67
  <workflow>
60
68
  When starting work, this command:
61
- 1. Validates story availability
62
- 2. Loads SM agent
63
- 3. SM creates context and claims Jira
64
- 4. Hands off to TEA (tdd) or Dev (trivial)
69
+ 1. **Checks merge gate** - blocks if open PRs exist
70
+ 2. Validates story availability
71
+ 3. Loads SM agent
72
+ 4. SM creates context and claims Jira
73
+ 5. Hands off to TEA (tdd) or Dev (trivial)
65
74
  </workflow>
66
75
 
67
76
  ### `/sprint archive <story-id> [pr-number] [--apply]`
@@ -3,5 +3,5 @@ description: Test Engineer/Architect - Test strategy and TDD
3
3
  ---
4
4
 
5
5
  ```bash
6
- d="$PWD"; while [[ ! -d "$d/.claude" ]] && [[ "$d" != "/" ]]; do d="$(dirname "$d")"; done; "$d/.pennyfarthing/scripts/core/phase-check-start.sh" "tea"
6
+ python3 -m pennyfarthing_scripts.cli agent start "tea"
7
7
  ```
@@ -3,5 +3,5 @@ description: Technical Writer - Documentation creation and maintenance
3
3
  ---
4
4
 
5
5
  ```bash
6
- d="$PWD"; while [[ ! -d "$d/.claude" ]] && [[ "$d" != "/" ]]; do d="$(dirname "$d")"; done; "$d/.pennyfarthing/scripts/core/agent-session.sh" start "tech-writer"
6
+ python3 -m pennyfarthing_scripts.cli agent start "tech-writer"
7
7
  ```
@@ -3,5 +3,5 @@ description: UX Designer - User experience design and UI patterns
3
3
  ---
4
4
 
5
5
  ```bash
6
- d="$PWD"; while [[ ! -d "$d/.claude" ]] && [[ "$d" != "/" ]]; do d="$(dirname "$d")"; done; "$d/.pennyfarthing/scripts/core/agent-session.sh" start "ux-designer"
6
+ python3 -m pennyfarthing_scripts.cli agent start "ux-designer"
7
7
  ```
@@ -7,9 +7,9 @@ description: Resume work or start new - smart entry point that picks up where yo
7
7
  <agent-activation>
8
8
  **FIRST:** Use Bash tool to run:
9
9
  ```bash
10
- d="$PWD"; while [[ ! -d "$d/.claude" ]] && [[ "$d" != "/" ]]; do d="$(dirname "$d")"; done; "$d/.pennyfarthing/scripts/core/agent-session.sh" start "sm"
10
+ python3 -m pennyfarthing_scripts.cli agent start "sm"
11
11
  ```
12
- This finds the project root and loads your persona. Adopt the character shown in the output.
12
+ This loads your persona. Adopt the character shown in the output.
13
13
  </agent-activation>
14
14
 
15
15
  <purpose>
@@ -43,12 +43,26 @@ $CLAUDE_PROJECT_DIR/.pennyfarthing/scripts/foo.sh # BROKEN!
43
43
  **Sidecar memory:** Capture learnings BEFORE spawning handoff subagent. Don't wait until exit.
44
44
  </critical>
45
45
 
46
+ <critical>
47
+ **Story completion is MANDATORY.** A story is NOT done until:
48
+ 1. Reviewer approves and merges the PR
49
+ 2. SM runs `finish-story.sh` (archive session, update Jira, clean up)
50
+
51
+ **Never** start new work while stories have open PRs. The merge gate blocks `/sprint work` if open PRs exist.
52
+
53
+ **If stuck in incomplete state:**
54
+ - Open PRs? → Run `/reviewer` to complete reviews and merge
55
+ - Merged but not finished? → Run `/sm` to trigger finish flow
56
+ </critical>
57
+
46
58
  ---
47
59
 
48
60
  ## Reference
49
61
 
50
62
  <info>
51
- **Workflow:** SM → TEA → Dev → Reviewer → SM (finish). Trivial (1-2 pts) skips TEA.
63
+ **Workflow:** SM → TEA → Dev → Reviewer (merge PR) → SM (finish). Trivial (1-2 pts) skips TEA.
64
+
65
+ **Story completion:** Reviewer merges PR on approval. SM runs `finish-story.sh` to archive and update Jira. A story is NOT complete until SM runs finish.
52
66
 
53
67
  **Confidence:** HIGH=proceed, MEDIUM=ask before risky, LOW=always ask.
54
68
 
@@ -13,7 +13,7 @@ theme:
13
13
  default_humor: subtle
14
14
  character_immersion: high
15
15
  user_title: Citizen
16
- portrait_style: ", Pompeii fresco, Roman wall painting, muted earth tones, classical antiquity, villa mural style"
16
+ portrait_style: ", ancient Roman mosaic portrait, tessera tile fragments, scratched graffito details, Pompeii wall art style, geometric earth tones, stone texture"
17
17
  tier: A
18
18
 
19
19
  zeitgeist:
@@ -23,7 +23,7 @@ zeitgeist:
23
23
  agents:
24
24
  orchestrator:
25
25
  character: Gaius Julius Caesar
26
- visual: "A balding middle-aged Roman man with sharp features and intense commanding gaze, wearing a laurel wreath crown and draped purple-bordered toga, thin lips set in calculating expression"
26
+ visual: "Clean-shaven Roman man in profile, laurel wreath, purple-bordered toga, aquiline nose, balding, imperial bearing"
27
27
  ocean:
28
28
  O: 4 # Strategic vision
29
29
  C: 5 # Roman discipline
@@ -50,7 +50,7 @@ agents:
50
50
 
51
51
  sm:
52
52
  character: Titus Pullo
53
- visual: "A burly Roman legionary with unkempt hair and battle scars, broad grinning face, wearing simple soldier's tunic and leather balteus belt, muscular arms"
53
+ visual: "Rough Roman soldier, short stubble, leather armor straps, battle-scarred face, simple military tunic, burly build"
54
54
  # JOB FAIR OPTIMIZED: Pullo moved here; Vorenus excels at dev (+6.88)
55
55
  ocean:
56
56
  O: 3 # Practical soldier
@@ -78,7 +78,7 @@ agents:
78
78
 
79
79
  tea:
80
80
  character: Atia of the Julii
81
- visual: "An elegant Roman noblewoman with elaborately curled dark hair pinned with golden ornaments, wearing rich stola with draped palla, sharp calculating eyes and knowing smile"
81
+ visual: "Roman noblewoman, elaborate curled dark hair with gold pins, draped stola and palla, arched eyebrow, patrician features"
82
82
  ocean:
83
83
  O: 3 # Street wisdom
84
84
  C: 4 # Survival discipline
@@ -105,7 +105,7 @@ agents:
105
105
 
106
106
  dev:
107
107
  character: Lucius Vorenus
108
- visual: "A stern Roman soldier in his forties with close-cropped dark hair and weathered face, wearing centurion armor with transverse horsehair crest on helmet, deep-set honorable eyes"
108
+ visual: "Flat 2D mosaic portrait, clean-shaven Roman centurion in profile, crested helmet, stern face, dark hair"
109
109
  # JOB FAIR OPTIMIZED: Vorenus scored best as dev (+6.88 over Pullo)
110
110
  ocean:
111
111
  O: 2 # Traditional soldier
@@ -133,7 +133,7 @@ agents:
133
133
 
134
134
  reviewer:
135
135
  character: Marcus Tullius Cicero
136
- visual: "An elderly Roman senator with thin white hair and deeply lined intellectual face, wearing plain white toga draped formally, holding a scroll, piercing oratorical gaze"
136
+ visual: "Flat 2D mosaic portrait, Roman senator on senate floor, white toga, holding scroll, orating to crowd, marble columns"
137
137
  ocean:
138
138
  O: 4 # Political insight
139
139
  C: 5 # Senatorial discipline
@@ -160,7 +160,7 @@ agents:
160
160
 
161
161
  architect:
162
162
  character: Gaius Octavian
163
- visual: "A young Roman man barely out of adolescence with pale complexion and calculating blue eyes, thin face with composed expression, wearing simple but fine toga, quiet dangerous intelligence"
163
+ visual: "Flat 2D mosaic portrait, young clean-shaven Roman man, simple toga, youthful face, distant expression"
164
164
  ocean:
165
165
  O: 4 # Imperial vision
166
166
  C: 5 # Strategic discipline
@@ -187,7 +187,7 @@ agents:
187
187
 
188
188
  pm:
189
189
  character: Servilia of the Junii
190
- visual: "A mature Roman noblewoman with dark hair pulled back severely, strong patrician features and bitter determined mouth, wearing widow's dark stola, eyes burning with cold purpose"
190
+ visual: "Flat 2D mosaic portrait, mature Roman widow, dark hair pulled back, dark stola, strong jaw, severe expression"
191
191
  ocean:
192
192
  O: 4 # Political survival
193
193
  C: 4 # Patrician discipline
@@ -214,7 +214,7 @@ agents:
214
214
 
215
215
  tech-writer:
216
216
  character: Posca
217
- visual: "A thin Greek man with receding hairline and intelligent tired eyes, wearing simple slave's tunic, holding wax tablet and stylus, observant meticulous expression"
217
+ visual: "Flat 2D mosaic portrait, thin Greek freedman, receding hairline, simple tunic, wax tablet, tired eyes"
218
218
  ocean:
219
219
  O: 4 # Historical perspective
220
220
  C: 4 # Scholarly discipline
@@ -241,7 +241,7 @@ agents:
241
241
 
242
242
  ux-designer:
243
243
  character: Cleopatra
244
- visual: "A striking Egyptian queen with elaborate braided black hair and golden serpent diadem, kohl-lined almond eyes, wearing flowing white linen with golden collar necklace, magnetic captivating presence"
244
+ visual: "Ancient Egyptian tomb painting style, Queen Cleopatra, black braided hair, golden uraeus crown, heavy kohl eyes, hieroglyphic border, Nile blue and gold"
245
245
  ocean:
246
246
  O: 4 # Cultural adaptation
247
247
  C: 3 # Survival skills
@@ -268,7 +268,7 @@ agents:
268
268
 
269
269
  devops:
270
270
  character: Mark Antony
271
- visual: "A powerfully built Roman general with curly dark hair and roguish handsome face, wearing military cuirass and red general's cloak, charming confident grin"
271
+ visual: "Flat 2D mosaic portrait, clean-shaven Roman general, curly dark hair, red cloak, strong jaw"
272
272
  ocean:
273
273
  O: 2 # Military engineering
274
274
  C: 5 # Legion discipline
@@ -0,0 +1,168 @@
1
+ """
2
+ Pennyfarthing CLI - Command line interface for agent orchestration.
3
+
4
+ Usage:
5
+ python -m pennyfarthing_scripts.cli [OPTIONS] COMMAND [ARGS]...
6
+ pf [OPTIONS] COMMAND [ARGS]...
7
+
8
+ This module uses lazy loading to keep startup time under 200ms.
9
+ Heavy imports (httpx, workflow modules) are deferred until needed.
10
+ """
11
+
12
+ import click
13
+
14
+ # Get version from package - this is a fast import
15
+ from pennyfarthing_scripts import __version__
16
+
17
+
18
+ @click.group()
19
+ @click.version_option(version=__version__, prog_name="pf")
20
+ def cli():
21
+ """Pennyfarthing CLI - Agent orchestration utilities.
22
+
23
+ Commands are organized into groups:
24
+
25
+ \b
26
+ workflow - Workflow state and phase management
27
+ agent - Agent session management
28
+ sprint - Sprint status and story operations
29
+ """
30
+ pass
31
+
32
+
33
+ # Import and register sprint group (lazy registration preserves startup time)
34
+ from pennyfarthing_scripts.sprint.cli import sprint
35
+
36
+ cli.add_command(sprint)
37
+
38
+
39
+ @cli.group()
40
+ def agent():
41
+ """Agent session management.
42
+
43
+ \b
44
+ Commands:
45
+ start - Start an agent session with context
46
+ """
47
+ pass
48
+
49
+
50
+ @agent.command("start")
51
+ @click.argument("name")
52
+ @click.option("--session-id", help="Use explicit session ID")
53
+ @click.option("--no-persona", is_flag=True, help="Skip persona loading")
54
+ @click.option("--json", "json_output", is_flag=True, help="Output as JSON")
55
+ @click.option("--minimal", is_flag=True, help="Skip all context (fastest)")
56
+ @click.option("--full", is_flag=True, help="Include domain docs")
57
+ def agent_start(
58
+ name: str,
59
+ session_id: str | None,
60
+ no_persona: bool,
61
+ json_output: bool,
62
+ minimal: bool,
63
+ full: bool,
64
+ ):
65
+ """Start an agent session with full context.
66
+
67
+ Loads agent definition, persona, behavior guide, sprint context,
68
+ session context, and sidecar memory.
69
+
70
+ \b
71
+ Arguments:
72
+ NAME - Agent name (sm, tea, dev, reviewer, etc.)
73
+ """
74
+ # Lazy import - only load when command is actually invoked
75
+ from pennyfarthing_scripts.prime import prime
76
+
77
+ exit_code = prime(
78
+ agent_name=name,
79
+ session_id=session_id,
80
+ no_persona=no_persona,
81
+ json_output=json_output,
82
+ minimal=minimal,
83
+ full=full,
84
+ )
85
+ raise SystemExit(exit_code)
86
+
87
+
88
+ @cli.group()
89
+ def workflow():
90
+ """Workflow state and phase management.
91
+
92
+ \b
93
+ Commands:
94
+ check - Check current workflow state
95
+ phase-check - Verify phase ownership
96
+ handoff - Emit handoff marker
97
+ """
98
+ pass
99
+
100
+
101
+ @workflow.command("check")
102
+ @click.option("--json", "output_json", is_flag=True, help="Output as JSON")
103
+ def workflow_check(output_json: bool):
104
+ """Check current workflow state.
105
+
106
+ Returns the current story ID, phase, and workflow state.
107
+ """
108
+ # Lazy import - only load when command is actually invoked
109
+ from pennyfarthing_scripts.workflow import get_workflow_state
110
+
111
+ state = get_workflow_state()
112
+
113
+ if output_json:
114
+ import json
115
+
116
+ click.echo(json.dumps(state, indent=2))
117
+ else:
118
+ click.echo(f"State: {state.get('state', 'unknown')}")
119
+ if state.get("story_id"):
120
+ click.echo(f"Story: {state['story_id']}")
121
+ if state.get("workflow"):
122
+ click.echo(f"Workflow: {state['workflow']}")
123
+ if state.get("phase"):
124
+ click.echo(f"Phase: {state['phase']}")
125
+
126
+
127
+ @workflow.command("phase-check")
128
+ @click.argument("workflow_name")
129
+ @click.argument("phase")
130
+ def workflow_phase_check(workflow_name: str, phase: str):
131
+ """Check which agent owns a workflow phase.
132
+
133
+ \b
134
+ Arguments:
135
+ WORKFLOW_NAME - The workflow type (tdd, trivial, etc.)
136
+ PHASE - The phase to check (red, implement, review, etc.)
137
+ """
138
+ # Lazy import
139
+ from pennyfarthing_scripts.workflow import get_phase_owner
140
+
141
+ owner = get_phase_owner(workflow_name, phase)
142
+ click.echo(owner)
143
+
144
+
145
+ @workflow.command("handoff")
146
+ @click.argument("next_agent")
147
+ def workflow_handoff(next_agent: str):
148
+ """Emit a handoff marker for Cyclist.
149
+
150
+ \b
151
+ Arguments:
152
+ NEXT_AGENT - The agent to hand off to (tea, dev, reviewer, etc.)
153
+ """
154
+ # Output the marker format expected by Cyclist
155
+ click.echo("---")
156
+ click.echo("AGENT_COMMAND:")
157
+ click.echo(f' marker: "<!-- CYCLIST:HANDOFF:/{next_agent} -->"')
158
+ click.echo(f' fallback: "Run `/{next_agent}` to continue"')
159
+ click.echo("---")
160
+
161
+
162
+ def main():
163
+ """Entry point for the CLI."""
164
+ cli()
165
+
166
+
167
+ if __name__ == "__main__":
168
+ main()