@pennyfarthing/core 7.6.1 → 7.8.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 (109) hide show
  1. package/README.md +109 -201
  2. package/package.json +1 -1
  3. package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
  4. package/packages/core/dist/cli/commands/doctor.js +205 -0
  5. package/packages/core/dist/cli/commands/doctor.js.map +1 -1
  6. package/packages/core/dist/cli/commands/init.js +31 -0
  7. package/packages/core/dist/cli/commands/init.js.map +1 -1
  8. package/packages/core/dist/cli/commands/update.js +31 -0
  9. package/packages/core/dist/cli/commands/update.js.map +1 -1
  10. package/pennyfarthing-dist/agents/architect.md +48 -53
  11. package/pennyfarthing-dist/agents/dev.md +74 -164
  12. package/pennyfarthing-dist/agents/devops.md +44 -39
  13. package/pennyfarthing-dist/agents/handoff.md +46 -23
  14. package/pennyfarthing-dist/agents/orchestrator.md +84 -255
  15. package/pennyfarthing-dist/agents/pm.md +40 -50
  16. package/pennyfarthing-dist/agents/reviewer-preflight.md +58 -26
  17. package/pennyfarthing-dist/agents/reviewer.md +107 -298
  18. package/pennyfarthing-dist/agents/sm-file-summary.md +51 -30
  19. package/pennyfarthing-dist/agents/sm-finish.md +59 -38
  20. package/pennyfarthing-dist/agents/sm-handoff.md +40 -33
  21. package/pennyfarthing-dist/agents/sm-setup.md +122 -45
  22. package/pennyfarthing-dist/agents/sm.md +204 -545
  23. package/pennyfarthing-dist/agents/tea.md +77 -146
  24. package/pennyfarthing-dist/agents/tech-writer.md +43 -24
  25. package/pennyfarthing-dist/agents/testing-runner.md +73 -30
  26. package/pennyfarthing-dist/agents/ux-designer.md +39 -25
  27. package/pennyfarthing-dist/agents/workflow-status-check.md +45 -17
  28. package/pennyfarthing-dist/commands/benchmark.md +19 -1
  29. package/pennyfarthing-dist/commands/continue-session.md +1 -1
  30. package/pennyfarthing-dist/commands/git-cleanup.md +43 -308
  31. package/pennyfarthing-dist/commands/solo.md +36 -0
  32. package/pennyfarthing-dist/commands/theme-maker.md +5 -5
  33. package/pennyfarthing-dist/commands/work.md +1 -1
  34. package/pennyfarthing-dist/guides/agent-behavior.md +22 -9
  35. package/pennyfarthing-dist/guides/agent-tag-taxonomy.md +432 -0
  36. package/pennyfarthing-dist/guides/patterns/approval-gates-pattern.md +27 -7
  37. package/pennyfarthing-dist/guides/scale-levels.md +114 -0
  38. package/pennyfarthing-dist/guides/xml-tags.md +335 -0
  39. package/pennyfarthing-dist/personas/themes/gilligans-island.yaml +83 -83
  40. package/pennyfarthing-dist/personas/themes/star-trek-tos.yaml +1 -1
  41. package/pennyfarthing-dist/personas/themes/the-expanse.yaml +11 -11
  42. package/pennyfarthing-dist/scripts/core/agent-session.sh +13 -7
  43. package/pennyfarthing-dist/scripts/core/check-context.sh +9 -1
  44. package/pennyfarthing-dist/scripts/core/handoff-marker.sh +13 -2
  45. package/pennyfarthing-dist/scripts/core/prime.sh +3 -132
  46. package/pennyfarthing-dist/scripts/core/run.sh +9 -0
  47. package/pennyfarthing-dist/scripts/git/create-feature-branches.sh +45 -4
  48. package/pennyfarthing-dist/scripts/git/git-status-all.sh +32 -7
  49. package/pennyfarthing-dist/scripts/hooks/__pycache__/question_reflector_check.cpython-314.pyc +0 -0
  50. package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +30 -11
  51. package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +80 -23
  52. package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +4 -4
  53. package/pennyfarthing-dist/scripts/hooks/question_reflector_check.py +499 -0
  54. package/pennyfarthing-dist/scripts/hooks/session-stop.sh +7 -0
  55. package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +94 -0
  56. package/pennyfarthing-dist/scripts/jira/README.md +10 -7
  57. package/pennyfarthing-dist/scripts/jira/jira-claim-story.sh +10 -152
  58. package/pennyfarthing-dist/scripts/jira/jira-sync-story.sh +14 -4
  59. package/pennyfarthing-dist/scripts/jira/jira-sync.sh +12 -4
  60. package/pennyfarthing-dist/scripts/jira/sync-epic-jira.sh +11 -99
  61. package/pennyfarthing-dist/scripts/lib/common.sh +55 -0
  62. package/pennyfarthing-dist/scripts/maintenance/sidecar-health.sh +97 -0
  63. package/pennyfarthing-dist/scripts/misc/add-short-names.sh +13 -0
  64. package/pennyfarthing-dist/scripts/misc/add_short_names.py +226 -0
  65. package/pennyfarthing-dist/scripts/misc/migrate-bmad-workflow.sh +6 -5
  66. package/pennyfarthing-dist/scripts/misc/migrate_bmad_workflow.py +319 -0
  67. package/pennyfarthing-dist/scripts/misc/statusline.sh +27 -22
  68. package/pennyfarthing-dist/scripts/sprint/import-epic-to-future.sh +6 -5
  69. package/pennyfarthing-dist/scripts/sprint/import_epic_to_future.py +270 -0
  70. package/pennyfarthing-dist/scripts/story/create-story.sh +14 -154
  71. package/pennyfarthing-dist/scripts/story/size-story.sh +12 -192
  72. package/pennyfarthing-dist/scripts/story/story-template.sh +12 -156
  73. package/pennyfarthing-dist/scripts/test/ensure-swebench-data.sh +59 -0
  74. package/pennyfarthing-dist/scripts/test/ground-truth-judge.py +24 -93
  75. package/pennyfarthing-dist/scripts/test/swebench-judge.py +33 -59
  76. package/pennyfarthing-dist/scripts/theme/compute-theme-tiers.sh +8 -6
  77. package/pennyfarthing-dist/scripts/theme/compute_theme_tiers.py +402 -0
  78. package/pennyfarthing-dist/scripts/validation/validate-agent-schema.sh +575 -0
  79. package/pennyfarthing-dist/scripts/workflow/check.py +502 -0
  80. package/pennyfarthing-dist/scripts/workflow/check.sh +3 -476
  81. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.py +61 -0
  82. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.sh +13 -0
  83. package/pennyfarthing-dist/skills/judge/SKILL.md +57 -0
  84. package/pennyfarthing-dist/skills/skill-registry.yaml +52 -16
  85. package/pennyfarthing-dist/skills/sprint/scripts/sync-epic-jira.sh +4 -22
  86. package/pennyfarthing-dist/skills/sprint/skill.md +1 -1
  87. package/pennyfarthing-dist/templates/settings.local.json.template +11 -0
  88. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-01-analyze.md +83 -0
  89. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-02-categorize.md +116 -0
  90. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-03-execute.md +210 -0
  91. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-04-verify.md +88 -0
  92. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-05-complete.md +71 -0
  93. package/pennyfarthing-dist/workflows/git-cleanup.yaml +59 -0
  94. package/pennyfarthing-dist/guides/XML-TAGS.md +0 -156
  95. package/pennyfarthing-dist/scripts/hooks/question-reflector-check.mjs +0 -380
  96. package/pennyfarthing-dist/scripts/hooks/tests/question-reflector.test.mjs +0 -545
  97. package/pennyfarthing-dist/scripts/jira/jira-bidirectional-sync.mjs +0 -327
  98. package/pennyfarthing-dist/scripts/jira/jira-bidirectional-sync.test.mjs +0 -503
  99. package/pennyfarthing-dist/scripts/jira/jira-lib.mjs +0 -443
  100. package/pennyfarthing-dist/scripts/jira/jira-sync-story.mjs +0 -208
  101. package/pennyfarthing-dist/scripts/jira/jira-sync.mjs +0 -198
  102. package/pennyfarthing-dist/scripts/misc/add-short-names.mjs +0 -264
  103. package/pennyfarthing-dist/scripts/misc/migrate-bmad-workflow.mjs +0 -474
  104. package/pennyfarthing-dist/scripts/sprint/import-epic-to-future.mjs +0 -377
  105. package/pennyfarthing-dist/scripts/theme/compute-theme-tiers.js +0 -492
  106. /package/pennyfarthing-dist/guides/{AGENT-COORDINATION.md → agent-coordination.md} +0 -0
  107. /package/pennyfarthing-dist/guides/{HOOKS.md → hooks.md} +0 -0
  108. /package/pennyfarthing-dist/guides/{PROMPT-PATTERNS.md → prompt-patterns.md} +0 -0
  109. /package/pennyfarthing-dist/guides/{SESSION-ARTIFACTS.md → session-artifacts.md} +0 -0
@@ -13,13 +13,13 @@ theme:
13
13
  default_humor: enabled
14
14
  character_immersion: high
15
15
  user_title: Bossmang
16
- portrait_style: ", hard sci-fi, industrial spaceship lighting, gritty textures"
16
+ portrait_style: ", russian constructivist style, bold geometric shapes, red and black color palette, propaganda poster aesthetic, angular compositions, industrial typography influence"
17
17
  tier: S
18
18
 
19
19
  agents:
20
20
  orchestrator:
21
21
  character: The Investigator
22
- visual: "A gaunt man with hollow cheeks and a 1940s detective fedora, wearing a rumpled trench coat, eyes glowing with an otherworldly blue light, knowing cryptic expression"
22
+ visual: "A gaunt man with hollow cheeks and detective fedora, trench coat silhouette, glowing blue eyes rendered as geometric shapes, stark angular shadows"
23
23
  ocean:
24
24
  O: 5
25
25
  C: 3
@@ -51,7 +51,7 @@ agents:
51
51
  shortName: Investigator
52
52
  sm:
53
53
  character: Camina Drummer
54
- visual: "A fierce Belter woman with shaved sides and mohawk, neck tattoos and industrial piercings, wearing a captain's jacket, commanding direct expression"
54
+ visual: "A fierce woman with bold mohawk silhouette, geometric tattoo patterns, captain's jacket rendered in angular blocks, commanding diagonal composition"
55
55
  # JOB FAIR OPTIMIZED: Drummer excels at sm coordination
56
56
  ocean:
57
57
  O: 3 # Belter culture
@@ -84,7 +84,7 @@ agents:
84
84
 
85
85
  tea:
86
86
  character: Amos Burton
87
- visual: "A stocky muscular man with a shaved head and calm dead eyes, wearing a mechanic's jumpsuit with grease stains, impassive practical expression"
87
+ visual: "A stocky muscular figure with shaved head, dead eyes as stark circles, mechanic's jumpsuit in bold flat shapes, industrial worker aesthetic"
88
88
  # JOB FAIR OPTIMIZED: Amos excels at testing/breaking things
89
89
  ocean:
90
90
  O: 2 # Practical focus
@@ -117,7 +117,7 @@ agents:
117
117
 
118
118
  dev:
119
119
  character: Naomi Nagata
120
- visual: "A tall Belter woman with dark curly hair and angular features, wearing an engineer's jumpsuit with tablet computer, focused analytical expression"
120
+ visual: "A tall woman with flowing curly hair as dynamic curves, angular features emphasized, engineer holding geometric tablet, diagonal thrust composition"
121
121
  # JOB FAIR OPTIMIZED: Naomi excels at implementation
122
122
  ocean:
123
123
  O: 5 # Engineering genius
@@ -150,7 +150,7 @@ agents:
150
150
 
151
151
  reviewer:
152
152
  character: Chrisjen Avasarala
153
- visual: "An elderly Indian woman with silver hair in an elegant sari and heavy jewelry, wearing flowing robes, sharp piercing expression"
153
+ visual: "An elderly Indian woman with silver hair as bold swoops, sari rendered in dramatic angular folds, geometric jewelry shapes, piercing gaze with sharp triangular shadows"
154
154
  ocean:
155
155
  O: 4
156
156
  C: 5
@@ -182,7 +182,7 @@ agents:
182
182
  shortName: Avasarala
183
183
  architect:
184
184
  character: Naomi Nagata (design mode)
185
- visual: "A tall Belter woman with dark curly hair at a holographic display, wearing a clean engineering uniform, contemplative systems-thinking expression"
185
+ visual: "A tall woman with flowing curly hair, standing at geometric holographic display, clean engineering uniform in flat shapes, contemplative expression with angular highlights"
186
186
  ocean:
187
187
  O: 5
188
188
  C: 5
@@ -214,7 +214,7 @@ agents:
214
214
  shortName: Naomi
215
215
  pm:
216
216
  character: Chrisjen Avasarala (planning mode)
217
- visual: "An elderly Indian woman with silver hair in formal UN diplomatic attire, wearing elaborate earrings, calculating strategic expression"
217
+ visual: "An elderly Indian woman with silver hair as bold swoops, formal diplomatic attire in angular blocks, geometric earrings, calculating strategic expression with stark shadows"
218
218
  ocean:
219
219
  O: 4
220
220
  C: 5
@@ -246,7 +246,7 @@ agents:
246
246
  shortName: Avasarala
247
247
  tech-writer:
248
248
  character: James Holden
249
- visual: "A rugged man in his thirties with dark wavy hair and stubble, wearing a gray Rocinante crew jumpsuit with coffee cup in hand, earnest idealistic expression"
249
+ visual: "A rugged man with wavy hair rendered as bold strokes, gray jumpsuit in flat geometric planes, coffee cup as iconic shape, idealistic upward gaze"
250
250
  # JOB FAIR OPTIMIZED: Holden excels at documentation/broadcasting
251
251
  ocean:
252
252
  O: 4 # Idealistic vision
@@ -279,7 +279,7 @@ agents:
279
279
 
280
280
  ux-designer:
281
281
  character: Alex Kamal
282
- visual: "A friendly man with a thick mustache and warm eyes, wearing a pilot's flight suit with Martian Navy patches, relaxed easygoing expression"
282
+ visual: "A friendly man with bold mustache silhouette, pilot suit with geometric military patches, warm expression through simplified shapes, circular helmet frame"
283
283
  ocean:
284
284
  O: 4
285
285
  C: 3
@@ -311,7 +311,7 @@ agents:
311
311
  shortName: Alex
312
312
  devops:
313
313
  character: Amos Burton (systems mode)
314
- visual: "A stocky muscular man with a shaved head, wearing a maintenance jumpsuit with tool belt, stoic reliable expression"
314
+ visual: "A stocky muscular figure with shaved head, maintenance jumpsuit in bold flat shapes, geometric tool belt, stoic expression with industrial worker aesthetic"
315
315
  ocean:
316
316
  O: 2
317
317
  C: 4
@@ -211,16 +211,22 @@ case "$1" in
211
211
  echo "$2" > "$AGENT_FILE"
212
212
  echo "Session: $session_id -> $2"
213
213
 
214
- # Output persona on start (unless character_voice is disabled)
215
- if is_character_voice_enabled; then
216
- output_persona "$2"
217
- fi
218
-
219
- # Auto-prime context after persona (reduces cold-start overhead)
220
- # Pass agent name so prime can load agent-specific sidecar
214
+ # Context loading order (optimized for attention):
215
+ # 1. CLAUDE.md (system prompt - already loaded)
216
+ # 2. Agent definition + behavior guide (loaded by prime.sh FIRST)
217
+ # 3. Persona (output here, AFTER agent definition)
218
+ # 4. Session summary (loaded by prime.sh)
219
+ # 5. Sidecars (loaded by prime.sh LAST)
220
+
221
+ # Auto-prime loads agent definition FIRST (highest attention zone)
221
222
  if [[ -f "$PROJECT_ROOT/.pennyfarthing/scripts/prime.sh" ]]; then
222
223
  "$PROJECT_ROOT/.pennyfarthing/scripts/prime.sh" --quiet --agent "$2"
223
224
  fi
225
+
226
+ # Output persona AFTER agent definition (character voice is supplementary)
227
+ if is_character_voice_enabled; then
228
+ output_persona "$2"
229
+ fi
224
230
  ;;
225
231
  stop)
226
232
  # Use provided session ID, fall back to SESSION_ID env var
@@ -204,9 +204,17 @@ if last_total is not None:
204
204
  # Use usable percent for status decisions (more accurate for user)
205
205
  if usable_pct > warning_threshold:
206
206
  print('CONTEXT_STATUS=HIGH')
207
- print('HANDOFF_MODE=auto')
208
207
  else:
209
208
  print('CONTEXT_STATUS=OK')
209
+
210
+ # RELAY_MODE: Output for handoff-marker.sh to use
211
+ print(f'RELAY_MODE={str(relay_mode).lower()}')
212
+
213
+ # HANDOFF_MODE: 'auto' if relay_mode enabled, 'ask' otherwise
214
+ # MSSCI-12395: relay_mode controls autohandoff independent of context level
215
+ if relay_mode:
216
+ print('HANDOFF_MODE=auto')
217
+ else:
210
218
  print('HANDOFF_MODE=ask')
211
219
 
212
220
  # TirePump: Use CONTEXT_CLEAR (clear + load next agent) when:
@@ -48,6 +48,7 @@ eval "$("$SCRIPT_DIR/check-context.sh" 2>/dev/null)" || true
48
48
  # Default values if check-context.sh failed
49
49
  IS_CYCLIST="${IS_CYCLIST:-false}"
50
50
  USE_TIREPUMP="${USE_TIREPUMP:-false}"
51
+ RELAY_MODE="${RELAY_MODE:-false}"
51
52
 
52
53
  # Generate marker based on environment
53
54
  if [[ "$IS_ERROR" == "true" ]]; then
@@ -69,8 +70,18 @@ AGENT_COMMAND:
69
70
  fallback: "Run \`/${NEXT_AGENT}\` to continue"
70
71
  ---
71
72
  EOF
73
+ elif [[ "$RELAY_MODE" != "true" ]]; then
74
+ # Cyclist + Relay OFF - emit question marker for manual handoff confirmation
75
+ cat <<EOF
76
+ ---
77
+ AGENT_COMMAND:
78
+ marker: "<!-- CYCLIST:QUESTION:yesno -->"
79
+ question: "Ready to hand off to /${NEXT_AGENT}?"
80
+ fallback: "Run \`/${NEXT_AGENT}\` to continue"
81
+ ---
82
+ EOF
72
83
  elif [[ "$USE_TIREPUMP" == "true" ]]; then
73
- # Cyclist + TirePump - context clear marker
84
+ # Cyclist + Relay ON + high context - context clear marker
74
85
  cat <<EOF
75
86
  ---
76
87
  AGENT_COMMAND:
@@ -79,7 +90,7 @@ AGENT_COMMAND:
79
90
  ---
80
91
  EOF
81
92
  else
82
- # Cyclist, no TirePump - handoff marker
93
+ # Cyclist + Relay ON + low context - handoff marker
83
94
  cat <<EOF
84
95
  ---
85
96
  AGENT_COMMAND:
@@ -1,136 +1,7 @@
1
- #!/usr/bin/env zsh
1
+ #!/usr/bin/env bash
2
2
  # prime.sh - Load essential project context at agent activation
3
3
  # Usage: prime.sh [--minimal] [--full] [--quiet] [--agent <name>]
4
4
  #
5
- # Loads context in priority order (CLAUDE.md skipped - already in system prompt):
6
- # 1. Sprint summary (current-sprint.yaml key fields)
7
- # 2. Active session (.session/*-session.md)
8
- # 3. Agent sidecar (if --agent provided)
9
- # 4. Agent behavior guide (combined protocols for all agents)
10
- # 5. Domain docs (--full only)
5
+ # Thin wrapper around python -m pennyfarthing_scripts.prime
11
6
 
12
- set -euo pipefail
13
-
14
- # Load shared functions
15
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
16
- source "$SCRIPT_DIR/../sprint/sprint-common.sh"
17
-
18
- # Defaults
19
- QUIET=false
20
- MINIMAL=false
21
- FULL=false
22
- AGENT_NAME=""
23
-
24
- # Parse arguments
25
- while [[ $# -gt 0 ]]; do
26
- case $1 in
27
- --quiet) QUIET=true; shift ;;
28
- --minimal) MINIMAL=true; shift ;;
29
- --full) FULL=true; shift ;;
30
- --agent) AGENT_NAME="$2"; shift 2 ;;
31
- -h|--help)
32
- echo "Usage: prime.sh [--minimal] [--full] [--quiet] [--agent <name>]"
33
- echo " --minimal Skip all context (fastest)"
34
- echo " --full Include domain docs from .claude/project/"
35
- echo " --quiet Suppress section headers"
36
- echo " --agent <name> Load agent's sidecar patterns"
37
- exit 0
38
- ;;
39
- *) shift ;;
40
- esac
41
- done
42
-
43
- # Helper to print headers (respects --quiet)
44
- print_header() {
45
- if [[ "$QUIET" != "true" ]]; then
46
- echo ""
47
- echo "# $1"
48
- fi
49
- }
50
-
51
- # CLAUDE.md is already loaded by Claude Code system prompt - skip it
52
-
53
- # Stop here for minimal mode
54
- if [[ "$MINIMAL" == "true" ]]; then
55
- exit 0
56
- fi
57
-
58
- # 1. Sprint summary (uses shared functions from sprint-common.sh)
59
- if [[ -f "$(get_sprint_file)" ]]; then
60
- print_header "Sprint Context"
61
-
62
- # Use shared functions for consistent output
63
- summary=$(get_sprint_summary)
64
- if [[ -n "$summary" ]]; then
65
- echo "$summary"
66
- fi
67
-
68
- progress=$(get_sprint_progress)
69
- if [[ -n "$progress" ]]; then
70
- echo "$progress"
71
- fi
72
- fi
73
-
74
- # 2. Active session (if exists) - extract header metadata + current assessment
75
- SESSION_FILE=""
76
- if [[ -d "$PROJECT_ROOT/.session" ]]; then
77
- # Find a session file (typically only one active at a time)
78
- SESSION_FILE=$(find "$PROJECT_ROOT/.session" -maxdepth 1 -name "*-session.md" -type f 2>/dev/null | head -1)
79
- fi
80
-
81
- if [[ -n "$SESSION_FILE" && -f "$SESSION_FILE" ]]; then
82
- print_header "Active Session: $(basename "$SESSION_FILE")"
83
-
84
- # Extract header (everything before first ## heading)
85
- # This includes: title, metadata fields (Phase, Workflow, Repos, Branch, etc.)
86
- awk '/^## / {exit} {print}' "$SESSION_FILE"
87
-
88
- # Find the most recent assessment section (workflow-agnostic)
89
- # Assessment sections are named: "## {Agent} Assessment" (TEA, Dev, Reviewer, SM, etc.)
90
- # Show the LAST one in the file as it represents current state
91
- last_assessment=$(grep -n '^## .*Assessment' "$SESSION_FILE" | tail -1 | cut -d: -f1)
92
-
93
- if [[ -n "$last_assessment" ]]; then
94
- echo ""
95
- echo "---"
96
- # Extract from that line to next ## or EOF
97
- awk -v start="$last_assessment" '
98
- NR >= start {
99
- if (NR > start && /^## /) exit
100
- print
101
- }
102
- ' "$SESSION_FILE"
103
- fi
104
- fi
105
-
106
- # 3. Agent sidecar (if --agent provided)
107
- if [[ -n "$AGENT_NAME" ]]; then
108
- SIDECAR_DIR="$PROJECT_ROOT/.pennyfarthing/sidecars/${AGENT_NAME}"
109
- if [[ -d "$SIDECAR_DIR" ]]; then
110
- for pattern_file in "$SIDECAR_DIR"/*.md; do
111
- if [[ -f "$pattern_file" ]]; then
112
- print_header "Agent Sidecar: $(basename "$pattern_file")"
113
- cat "$pattern_file"
114
- fi
115
- done
116
- fi
117
- fi
118
-
119
- # 4. Agent behavior guide (combined protocols for all agents)
120
- if [[ -n "$AGENT_NAME" ]]; then
121
- BEHAVIOR_GUIDE="$PROJECT_ROOT/.pennyfarthing/guides/agent-behavior.md"
122
- if [[ -f "$BEHAVIOR_GUIDE" ]]; then
123
- print_header "Agent Behavior Guide"
124
- cat "$BEHAVIOR_GUIDE"
125
- fi
126
- fi
127
-
128
- # 5. Domain docs (--full only)
129
- if [[ "$FULL" == "true" ]]; then
130
- for doc in "$PROJECT_ROOT/.claude/project"/CLAUDE-*.md; do
131
- if [[ -f "$doc" ]]; then
132
- print_header "$(basename "$doc")"
133
- cat "$doc"
134
- fi
135
- done
136
- fi
7
+ exec python3 -m pennyfarthing_scripts.prime "$@"
@@ -64,6 +64,15 @@ fi
64
64
  # Full path required - script must include category
65
65
  SCRIPT_PATH="$SCRIPTS_DIR/$SCRIPT_NAME"
66
66
 
67
+ # Python-first dispatch: prefer .py over .sh when available
68
+ # This enables transparent migration of shell scripts to Python
69
+ SCRIPT_BASE="${SCRIPT_NAME%.sh}"
70
+ PYTHON_PATH="$SCRIPTS_DIR/${SCRIPT_BASE}.py"
71
+
72
+ if [[ -f "$PYTHON_PATH" ]] && command -v python3 &>/dev/null; then
73
+ exec python3 "$PYTHON_PATH" "$@"
74
+ fi
75
+
67
76
  if [[ -f "$SCRIPT_PATH" ]]; then
68
77
  exec "$SCRIPT_PATH" "$@"
69
78
  else
@@ -143,6 +143,7 @@ echo " Branch: $BRANCH_NAME"
143
143
  echo " Repos: $REPOS"
144
144
 
145
145
  # Process repos based on selection
146
+ # When processing multiple repos, run in parallel for faster network I/O
146
147
  case "$REPOS" in
147
148
  api)
148
149
  create_or_checkout_branch "$REPO_BASE/Pennyfarthing-api" "Pennyfarthing-api"
@@ -151,8 +152,42 @@ case "$REPOS" in
151
152
  create_or_checkout_branch "$REPO_BASE/Pennyfarthing-ui" "Pennyfarthing-ui"
152
153
  ;;
153
154
  all)
154
- create_or_checkout_branch "$REPO_BASE/Pennyfarthing-api" "Pennyfarthing-api"
155
- create_or_checkout_branch "$REPO_BASE/Pennyfarthing-ui" "Pennyfarthing-ui"
155
+ # Parallel execution for both repos
156
+ tmpdir=$(mktemp -d)
157
+ trap "rm -rf '$tmpdir'" EXIT
158
+ HAD_ERRORS=false
159
+
160
+ # Run both in parallel, capturing output
161
+ # Write to separate files to avoid race condition on shared file
162
+ (
163
+ create_or_checkout_branch "$REPO_BASE/Pennyfarthing-api" "Pennyfarthing-api"
164
+ echo "$REPO_BASE/Pennyfarthing-api:Pennyfarthing-api" > "$tmpdir/api.processed"
165
+ ) > "$tmpdir/api.out" 2>&1 &
166
+ pid_api=$!
167
+
168
+ (
169
+ create_or_checkout_branch "$REPO_BASE/Pennyfarthing-ui" "Pennyfarthing-ui"
170
+ echo "$REPO_BASE/Pennyfarthing-ui:Pennyfarthing-ui" > "$tmpdir/ui.processed"
171
+ ) > "$tmpdir/ui.out" 2>&1 &
172
+ pid_ui=$!
173
+
174
+ # Wait for both and capture exit codes
175
+ wait $pid_api; rc_api=$?
176
+ wait $pid_ui; rc_ui=$?
177
+
178
+ # Show output in order
179
+ [ -f "$tmpdir/api.out" ] && cat "$tmpdir/api.out"
180
+ [ -f "$tmpdir/ui.out" ] && cat "$tmpdir/ui.out"
181
+
182
+ # Rebuild PROCESSED_REPOS from separate files (avoids race condition)
183
+ [ -f "$tmpdir/api.processed" ] && PROCESSED_REPOS+=("$(cat "$tmpdir/api.processed")")
184
+ [ -f "$tmpdir/ui.processed" ] && PROCESSED_REPOS+=("$(cat "$tmpdir/ui.processed")")
185
+
186
+ # Check for failures
187
+ if [ $rc_api -ne 0 ] || [ $rc_ui -ne 0 ]; then
188
+ echo "⚠️ Some repos had errors"
189
+ HAD_ERRORS=true
190
+ fi
156
191
  ;;
157
192
  esac
158
193
 
@@ -222,5 +257,11 @@ done
222
257
 
223
258
  echo ""
224
259
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
225
- echo " Done! All branches verified and ready."
226
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
260
+ if [ "$HAD_ERRORS" = true ]; then
261
+ echo "⚠️ Done with errors. Check output above."
262
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
263
+ exit 1
264
+ else
265
+ echo "✅ Done! All branches verified and ready."
266
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
267
+ fi
@@ -82,15 +82,26 @@ if ! $BRIEF; then
82
82
  echo ""
83
83
  fi
84
84
 
85
- # Check each repo from configuration
85
+ # Check each repo from configuration (parallelized for performance)
86
86
  repo_count=$(get_repo_count)
87
87
  if [[ "$repo_count" -eq 0 ]]; then
88
88
  # No repos configured, just show current directory
89
89
  show_repo_status "Project" "$PROJECT_ROOT"
90
90
  else
91
+ # Create temp dir for parallel output capture
92
+ tmpdir=$(mktemp -d)
93
+ trap "rm -rf '$tmpdir'" EXIT
94
+
95
+ # Launch status checks in parallel
91
96
  for repo in $(get_repos); do
92
97
  repo_path=$(get_repo_full_path "$repo")
93
- show_repo_status "$repo" "$repo_path"
98
+ (show_repo_status "$repo" "$repo_path" > "$tmpdir/$repo.out" 2>&1) &
99
+ done
100
+ wait
101
+
102
+ # Output results in order
103
+ for repo in $(get_repos); do
104
+ [ -f "$tmpdir/$repo.out" ] && cat "$tmpdir/$repo.out"
94
105
  done
95
106
  fi
96
107
 
@@ -108,14 +119,28 @@ if ! $BRIEF; then
108
119
  unpushed=$(git -C "$PROJECT_ROOT" log origin/develop..HEAD --oneline 2>/dev/null | wc -l | tr -d ' ')
109
120
  total_unpushed=$((total_unpushed + unpushed))
110
121
  else
122
+ # Parallelize summary collection
123
+ summary_tmp=$(mktemp -d)
111
124
  for repo in $(get_repos); do
112
125
  repo_path=$(get_repo_full_path "$repo")
113
- [ -d "$repo_path/.git" ] || [ -d "$repo_path" ] || continue
114
- count=$(git -C "$repo_path" status --short 2>/dev/null | wc -l | tr -d ' ')
115
- total_changes=$((total_changes + count))
116
- unpushed=$(git -C "$repo_path" log origin/develop..HEAD --oneline 2>/dev/null | wc -l | tr -d ' ')
117
- total_unpushed=$((total_unpushed + unpushed))
126
+ (
127
+ [ -d "$repo_path/.git" ] || [ -d "$repo_path" ] || exit 0
128
+ count=$(git -C "$repo_path" status --short 2>/dev/null | wc -l | tr -d ' ')
129
+ unpushed=$(git -C "$repo_path" log origin/develop..HEAD --oneline 2>/dev/null | wc -l | tr -d ' ')
130
+ echo "$count $unpushed" > "$summary_tmp/$repo.count"
131
+ ) &
132
+ done
133
+ wait
134
+
135
+ # Aggregate results
136
+ for repo in $(get_repos); do
137
+ if [ -f "$summary_tmp/$repo.count" ]; then
138
+ read count unpushed < "$summary_tmp/$repo.count"
139
+ total_changes=$((total_changes + count))
140
+ total_unpushed=$((total_unpushed + unpushed))
141
+ fi
118
142
  done
143
+ rm -rf "$summary_tmp"
119
144
  fi
120
145
 
121
146
  if [ "$total_changes" -eq 0 ] && [ "$total_unpushed" -eq 0 ]; then
@@ -8,7 +8,7 @@
8
8
  # Claude's next API call.
9
9
  #
10
10
  # Configuration files:
11
- # .pennyfarthing/bell-mode.json - { "enabled": true/false }
11
+ # .pennyfarthing/config.local.yaml - workflow.bell_mode: true/false
12
12
  # .pennyfarthing/bell-queue.json - [{ "text": "...", "images": [...] }, ...]
13
13
  #
14
14
  # Output format (when injecting):
@@ -35,15 +35,17 @@ if [[ ! -d "$PROJECT_ROOT/.pennyfarthing" ]]; then
35
35
  exit 0
36
36
  fi
37
37
 
38
- BELL_MODE_CONFIG="$PROJECT_ROOT/.pennyfarthing/bell-mode.json"
38
+ CONFIG_LOCAL_YAML="$PROJECT_ROOT/.pennyfarthing/config.local.yaml"
39
39
  BELL_QUEUE_FILE="$PROJECT_ROOT/.pennyfarthing/bell-queue.json"
40
40
 
41
- # Check if bell mode is enabled
42
- if [[ ! -f "$BELL_MODE_CONFIG" ]]; then
41
+ # Check if bell mode is enabled in config.local.yaml
42
+ if [[ ! -f "$CONFIG_LOCAL_YAML" ]]; then
43
43
  exit 0
44
44
  fi
45
45
 
46
- ENABLED=$(cat "$BELL_MODE_CONFIG" 2>/dev/null | grep -o '"enabled"[[:space:]]*:[[:space:]]*true' || true)
46
+ # Parse YAML to check workflow.bell_mode - look for "bell_mode: true"
47
+ # This handles both "bell_mode: true" and " bell_mode: true" (indented under workflow)
48
+ ENABLED=$(grep -E '^\s*bell_mode:\s*true' "$CONFIG_LOCAL_YAML" 2>/dev/null || true)
47
49
  if [[ -z "$ENABLED" ]]; then
48
50
  exit 0
49
51
  fi
@@ -67,6 +69,13 @@ if [[ -z "$FIRST_MESSAGE_TEXT" ]]; then
67
69
  exit 0
68
70
  fi
69
71
 
72
+ # Get Cyclist port (if running)
73
+ CYCLIST_PORT=""
74
+ PORT_FILE="$PROJECT_ROOT/.cyclist-port"
75
+ if [[ -f "$PORT_FILE" ]]; then
76
+ CYCLIST_PORT=$(cat "$PORT_FILE" 2>/dev/null)
77
+ fi
78
+
70
79
  # Output the hook response JSON
71
80
  cat << EOF
72
81
  {
@@ -77,11 +86,21 @@ cat << EOF
77
86
  }
78
87
  EOF
79
88
 
80
- # Remove the first message from the queue (for next invocation)
81
- # Use jq if available, otherwise leave queue management to the TypeScript side
82
- # Run in background and ignore errors to avoid blocking hook response
83
- if command -v jq &> /dev/null; then
84
- (jq 'if length > 0 then .[1:] else [] end' "$BELL_QUEUE_FILE" > "$BELL_QUEUE_FILE.tmp" 2>/dev/null && mv "$BELL_QUEUE_FILE.tmp" "$BELL_QUEUE_FILE") &
85
- fi
89
+ # Remove the first message from the queue and notify Cyclist
90
+ # Run in background to avoid blocking hook response
91
+ (
92
+ # Dequeue using jq if available
93
+ if command -v jq &> /dev/null; then
94
+ jq 'if length > 0 then .[1:] else [] end' "$BELL_QUEUE_FILE" > "$BELL_QUEUE_FILE.tmp" 2>/dev/null && mv "$BELL_QUEUE_FILE.tmp" "$BELL_QUEUE_FILE"
95
+ fi
96
+
97
+ # Notify Cyclist browser to dequeue and display the message
98
+ if [[ -n "$CYCLIST_PORT" ]] && [[ "$CYCLIST_PORT" =~ ^[0-9]+$ ]]; then
99
+ curl -s -X POST "http://localhost:$CYCLIST_PORT/api/bell-consumed" \
100
+ -H "Content-Type: application/json" \
101
+ -d "{\"text\": \"$FIRST_MESSAGE_TEXT\"}" \
102
+ >/dev/null 2>&1 || true
103
+ fi
104
+ ) &
86
105
 
87
106
  exit 0