@pennyfarthing/core 11.1.1 → 11.2.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 (160) hide show
  1. package/README.md +8 -8
  2. package/package.json +1 -1
  3. package/packages/core/dist/server/otlp-receiver.d.ts +16 -11
  4. package/packages/core/dist/server/otlp-receiver.d.ts.map +1 -1
  5. package/packages/core/dist/server/otlp-receiver.js +185 -24
  6. package/packages/core/dist/server/otlp-receiver.js.map +1 -1
  7. package/packages/core/dist/server/otlp-receiver.test.d.ts +21 -0
  8. package/packages/core/dist/server/otlp-receiver.test.d.ts.map +1 -0
  9. package/packages/core/dist/server/otlp-receiver.test.js +446 -0
  10. package/packages/core/dist/server/otlp-receiver.test.js.map +1 -0
  11. package/packages/core/dist/shared/portrait-resolver.d.ts +9 -0
  12. package/packages/core/dist/shared/portrait-resolver.d.ts.map +1 -1
  13. package/packages/core/dist/shared/portrait-resolver.js +27 -0
  14. package/packages/core/dist/shared/portrait-resolver.js.map +1 -1
  15. package/packages/core/dist/shared/portrait-resolver.test.js +47 -1
  16. package/packages/core/dist/shared/portrait-resolver.test.js.map +1 -1
  17. package/packages/core/dist/shared/tandem-portrait-inventory.test.d.ts +13 -0
  18. package/packages/core/dist/shared/tandem-portrait-inventory.test.d.ts.map +1 -0
  19. package/packages/core/dist/shared/tandem-portrait-inventory.test.js +126 -0
  20. package/packages/core/dist/shared/tandem-portrait-inventory.test.js.map +1 -0
  21. package/pennyfarthing-dist/agents/dev.md +1 -1
  22. package/pennyfarthing-dist/agents/reviewer.md +1 -1
  23. package/pennyfarthing-dist/agents/sm-setup.md +1 -1
  24. package/pennyfarthing-dist/agents/sm.md +2 -2
  25. package/pennyfarthing-dist/agents/tea.md +1 -1
  26. package/pennyfarthing-dist/agents/testing-runner.md +2 -1
  27. package/pennyfarthing-dist/commands/pf-chore.md +2 -2
  28. package/pennyfarthing-dist/commands/pf-standalone.md +7 -2
  29. package/pennyfarthing-dist/guides/agent-behavior.md +1 -1
  30. package/pennyfarthing-dist/guides/agent-tag-taxonomy.md +1 -1
  31. package/pennyfarthing-dist/guides/bikerack.md +3 -3
  32. package/pennyfarthing-dist/guides/hooks.md +1 -1
  33. package/pennyfarthing-dist/guides/worktree-mode.md +3 -3
  34. package/pennyfarthing-dist/guides/xml-tags.md +2 -2
  35. package/pennyfarthing-dist/scripts/README.md +1 -1
  36. package/pennyfarthing-dist/scripts/core/check-context.sh +1 -1
  37. package/pennyfarthing-dist/scripts/git/README.md +24 -14
  38. package/pennyfarthing-dist/scripts/git/create-feature-branches.sh +5 -266
  39. package/pennyfarthing-dist/scripts/git/git-status-all.sh +5 -151
  40. package/pennyfarthing-dist/scripts/git/install-git-hooks.sh +6 -144
  41. package/pennyfarthing-dist/scripts/git/worktree-manager.sh +5 -496
  42. package/pennyfarthing-dist/scripts/hooks/README.md +1 -1
  43. package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +1 -1
  44. package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +9 -11
  45. package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +1 -1
  46. package/pennyfarthing-dist/scripts/portraits/generate-tandem-portraits.sh +76 -0
  47. package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +4 -221
  48. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.sh +5 -13
  49. package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +4 -123
  50. package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +4 -33
  51. package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +4 -156
  52. package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +4 -131
  53. package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +4 -249
  54. package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +4 -160
  55. package/pennyfarthing-dist/skills/pf-bc/usage.md +1 -1
  56. package/pennyfarthing-dist/skills/pf-jira/examples.md +5 -2
  57. package/pennyfarthing-dist/skills/pf-workflow/examples.md +27 -16
  58. package/pennyfarthing-dist/skills/pf-workflow/skill.md +9 -12
  59. package/pennyfarthing-dist/skills/pf-workflow/usage.md +33 -8
  60. package/pennyfarthing-dist/workflows/bdd-tandem.yaml +18 -6
  61. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-01-analyze.md +1 -1
  62. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-04-verify.md +1 -1
  63. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-05-complete.md +1 -1
  64. package/pennyfarthing-dist/workflows/review-tandem.yaml +65 -0
  65. package/pennyfarthing-dist/workflows/tdd-tandem.yaml +16 -8
  66. package/pennyfarthing_scripts/CLAUDE.md +26 -4
  67. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  68. package/pennyfarthing_scripts/__pycache__/hooks.cpython-314.pyc +0 -0
  69. package/pennyfarthing_scripts/__pycache__/pretooluse_hook.cpython-314.pyc +0 -0
  70. package/pennyfarthing_scripts/__pycache__/session_start_hook.cpython-314.pyc +0 -0
  71. package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-314.pyc +0 -0
  72. package/pennyfarthing_scripts/bc/cli.py +3 -5
  73. package/pennyfarthing_scripts/bikerack/__pycache__/background_panel.cpython-314.pyc +0 -0
  74. package/pennyfarthing_scripts/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
  75. package/pennyfarthing_scripts/bikerack/__pycache__/changed_panel.cpython-314.pyc +0 -0
  76. package/pennyfarthing_scripts/bikerack/__pycache__/cli.cpython-314.pyc +0 -0
  77. package/pennyfarthing_scripts/bikerack/__pycache__/debug_panel.cpython-314.pyc +0 -0
  78. package/pennyfarthing_scripts/bikerack/__pycache__/diffs_panel.cpython-314.pyc +0 -0
  79. package/pennyfarthing_scripts/bikerack/__pycache__/git_panel.cpython-314.pyc +0 -0
  80. package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
  81. package/pennyfarthing_scripts/bikerack/__pycache__/portrait.cpython-314.pyc +0 -0
  82. package/pennyfarthing_scripts/bikerack/__pycache__/progress_panel.cpython-314.pyc +0 -0
  83. package/pennyfarthing_scripts/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
  84. package/pennyfarthing_scripts/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
  85. package/pennyfarthing_scripts/bikerack/__pycache__/ws_client.cpython-314.pyc +0 -0
  86. package/pennyfarthing_scripts/bikerack/background_panel.py +86 -5
  87. package/pennyfarthing_scripts/bikerack/base_panel.py +62 -0
  88. package/pennyfarthing_scripts/bikerack/changed_panel.py +32 -28
  89. package/pennyfarthing_scripts/bikerack/debug_panel.py +31 -1
  90. package/pennyfarthing_scripts/bikerack/diffs_panel.py +74 -17
  91. package/pennyfarthing_scripts/bikerack/git_panel.py +103 -33
  92. package/pennyfarthing_scripts/bikerack/launcher.py +15 -15
  93. package/pennyfarthing_scripts/bikerack/progress_panel.py +315 -0
  94. package/pennyfarthing_scripts/bikerack/sprint_panel.py +158 -26
  95. package/pennyfarthing_scripts/bikerack/tui.py +336 -30
  96. package/pennyfarthing_scripts/bikerack/ws_client.py +2 -2
  97. package/pennyfarthing_scripts/cli.py +37 -65
  98. package/pennyfarthing_scripts/consultation/__init__.py +1 -0
  99. package/pennyfarthing_scripts/consultation/__pycache__/__init__.cpython-314.pyc +0 -0
  100. package/pennyfarthing_scripts/consultation/__pycache__/cli.cpython-314.pyc +0 -0
  101. package/pennyfarthing_scripts/consultation/cli.py +149 -0
  102. package/pennyfarthing_scripts/consultation/dialogue_manager.py +417 -0
  103. package/pennyfarthing_scripts/context.py +3 -3
  104. package/pennyfarthing_scripts/epic/__pycache__/__init__.cpython-314.pyc +0 -0
  105. package/pennyfarthing_scripts/epic/__pycache__/cli.cpython-314.pyc +0 -0
  106. package/pennyfarthing_scripts/git/__init__.py +12 -1
  107. package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
  108. package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
  109. package/pennyfarthing_scripts/git/__pycache__/hooks_installer.cpython-314.pyc +0 -0
  110. package/pennyfarthing_scripts/git/__pycache__/repos.cpython-314.pyc +0 -0
  111. package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
  112. package/pennyfarthing_scripts/git/__pycache__/worktree.cpython-314.pyc +0 -0
  113. package/pennyfarthing_scripts/git/create_branches.py +3 -4
  114. package/pennyfarthing_scripts/git/hooks_installer.py +152 -0
  115. package/pennyfarthing_scripts/git/repos.py +196 -0
  116. package/pennyfarthing_scripts/git/status_all.py +27 -11
  117. package/pennyfarthing_scripts/git/worktree.py +302 -0
  118. package/pennyfarthing_scripts/git_group/__pycache__/__init__.cpython-314.pyc +0 -0
  119. package/pennyfarthing_scripts/git_group/__pycache__/cli.cpython-314.pyc +0 -0
  120. package/pennyfarthing_scripts/git_group/cli.py +143 -40
  121. package/pennyfarthing_scripts/handoff/__pycache__/__init__.cpython-314.pyc +0 -0
  122. package/pennyfarthing_scripts/handoff/__pycache__/cli.cpython-314.pyc +0 -0
  123. package/pennyfarthing_scripts/handoff/__pycache__/complete_phase.cpython-314.pyc +0 -0
  124. package/pennyfarthing_scripts/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
  125. package/pennyfarthing_scripts/handoff/complete_phase.py +12 -0
  126. package/pennyfarthing_scripts/handoff/resolve_gate.py +5 -14
  127. package/pennyfarthing_scripts/hooks.py +3 -17
  128. package/pennyfarthing_scripts/pretooluse_hook.py +1 -1
  129. package/pennyfarthing_scripts/prime/__pycache__/heatmap.cpython-314.pyc +0 -0
  130. package/pennyfarthing_scripts/prime/heatmap.py +655 -0
  131. package/pennyfarthing_scripts/session/__pycache__/__init__.cpython-314.pyc +0 -0
  132. package/pennyfarthing_scripts/session/__pycache__/cli.cpython-314.pyc +0 -0
  133. package/pennyfarthing_scripts/session_start_hook.py +1 -1
  134. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  135. package/pennyfarthing_scripts/sprint/loader.py +15 -1
  136. package/pennyfarthing_scripts/tests/__pycache__/test_handoff_cli.cpython-314-pytest-9.0.2.pyc +0 -0
  137. package/pennyfarthing_scripts/tests/__pycache__/test_handoff_e2e.cpython-314-pytest-9.0.2.pyc +0 -0
  138. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
  139. package/pennyfarthing_scripts/tests/test_bikerack.py +51 -51
  140. package/pennyfarthing_scripts/tests/test_dialogue_manager.py +811 -0
  141. package/pennyfarthing_scripts/tests/test_handoff_cli.py +16 -11
  142. package/pennyfarthing_scripts/tests/test_workflow_check.py +2 -3
  143. package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-314.pyc +0 -0
  144. package/pennyfarthing_scripts/validate/adapters/tandem_awareness.py +254 -0
  145. package/pennyfarthing_scripts/validate/cli.py +17 -5
  146. package/pennyfarthing_scripts/workflow/__init__.py +40 -0
  147. package/pennyfarthing_scripts/workflow/__pycache__/__init__.cpython-314.pyc +0 -0
  148. package/pennyfarthing_scripts/workflow/__pycache__/cli.cpython-314.pyc +0 -0
  149. package/pennyfarthing_scripts/workflow/__pycache__/helpers.cpython-314.pyc +0 -0
  150. package/pennyfarthing_scripts/workflow/__pycache__/scale.cpython-314.pyc +0 -0
  151. package/pennyfarthing_scripts/workflow/__pycache__/state.cpython-314.pyc +0 -0
  152. package/pennyfarthing_scripts/workflow/cli.py +1099 -0
  153. package/pennyfarthing_scripts/workflow/helpers.py +241 -0
  154. package/pennyfarthing_scripts/{workflow.py → workflow/scale.py} +0 -104
  155. package/pennyfarthing_scripts/workflow/state.py +112 -0
  156. package/pennyfarthing-dist/skills/pf-workflow/scripts/list-workflows.sh +0 -91
  157. package/pennyfarthing-dist/skills/pf-workflow/scripts/resume-workflow.sh +0 -163
  158. package/pennyfarthing-dist/skills/pf-workflow/scripts/show-workflow.sh +0 -138
  159. package/pennyfarthing-dist/skills/pf-workflow/scripts/start-workflow.sh +0 -273
  160. package/pennyfarthing-dist/skills/pf-workflow/scripts/workflow-status.sh +0 -167
@@ -11,25 +11,22 @@ Pennyfarthing uses YAML-defined workflows to control agent sequences. The defaul
11
11
 
12
12
  ## Quick Reference
13
13
 
14
- ### Shell Script Commands
15
-
16
- | Command | Script | Purpose |
17
- |---------|--------|---------|
18
- | `/pf-workflow` or `/pf-workflow list` | `.pennyfarthing/scripts/workflow/list-workflows.sh` | List all workflows |
19
- | `/pf-workflow show [name]` | `.pennyfarthing/scripts/workflow/show-workflow.sh [name]` | Show workflow details |
20
- | `/pf-workflow set <name>` | Edit session file `**Workflow:**` line | Switch workflow mid-session |
21
- | `/pf-workflow start <name>` | `.pennyfarthing/scripts/workflow/start-workflow.sh <name> [--mode M]` | Start stepped workflow |
22
- | `/pf-workflow resume [name]` | `.pennyfarthing/scripts/workflow/resume-workflow.sh [name]` | Resume interrupted workflow |
23
- | `/pf-workflow status` | `.pennyfarthing/scripts/workflow/workflow-status.sh` | Show stepped workflow progress |
24
- | `/pf-workflow fix-phase <id> <phase>` | `.pennyfarthing/scripts/workflow/fix-session-phase.sh <id> <phase> [--dry-run]` | Repair session phase |
25
-
26
14
  ### Python CLI Commands
27
15
 
28
16
  | Command | CLI | Purpose |
29
17
  |---------|-----|---------|
18
+ | `/pf-workflow` or `/pf-workflow list` | `pf workflow list` | List all workflows |
19
+ | `/pf-workflow show [name]` | `pf workflow show [name]` | Show workflow details |
20
+ | `/pf-workflow set <name>` | Edit session file `**Workflow:**` line | Switch workflow mid-session |
21
+ | `/pf-workflow start <name>` | `pf workflow start <name> [--mode M]` | Start stepped workflow |
22
+ | `/pf-workflow resume [name]` | `pf workflow resume [name]` | Resume interrupted workflow |
23
+ | `/pf-workflow status` | `pf workflow status` | Show stepped workflow progress |
24
+ | `/pf-workflow fix-phase <id> <phase>` | `pf workflow fix-phase <id> <phase> [--dry-run]` | Repair session phase |
30
25
  | Check workflow state | `pf workflow check [--json]` | Current story, phase, state |
31
26
  | Check phase owner | `pf workflow phase-check <workflow> <phase>` | Which agent owns a phase |
27
+ | Get workflow type | `pf workflow type <workflow>` | phased/stepped/procedural |
32
28
  | Emit handoff marker | `pf workflow handoff <next-agent>` | CYCLIST handoff marker |
29
+ | Complete step | `pf workflow complete-step [name] [--step N]` | Advance stepped workflow |
33
30
 
34
31
  ### Built-in Workflows
35
32
 
@@ -1,11 +1,11 @@
1
1
  # Workflow — Detailed Usage
2
2
 
3
- ## Shell Script Commands
3
+ ## Commands
4
4
 
5
5
  ### List Workflows
6
6
 
7
7
  ```bash
8
- .pennyfarthing/scripts/workflow/list-workflows.sh
8
+ pf workflow list
9
9
  ```
10
10
 
11
11
  No arguments. Returns table of workflows with type, phases/steps count, modes, default flag, description.
@@ -13,7 +13,7 @@ No arguments. Returns table of workflows with type, phases/steps count, modes, d
13
13
  ### Show Workflow
14
14
 
15
15
  ```bash
16
- .pennyfarthing/scripts/workflow/show-workflow.sh [NAME]
16
+ pf workflow show [NAME]
17
17
  ```
18
18
 
19
19
  | Arg | Required | Description |
@@ -31,7 +31,7 @@ Warning: Only do this when story requirements have fundamentally changed. Switch
31
31
  ### Start Stepped Workflow
32
32
 
33
33
  ```bash
34
- .pennyfarthing/scripts/workflow/start-workflow.sh <NAME> [--mode <MODE>]
34
+ pf workflow start <NAME> [--mode <MODE>]
35
35
  ```
36
36
 
37
37
  | Arg/Option | Required | Description |
@@ -44,7 +44,7 @@ Creates a new session and begins at step 1.
44
44
  ### Resume Stepped Workflow
45
45
 
46
46
  ```bash
47
- .pennyfarthing/scripts/workflow/resume-workflow.sh [NAME]
47
+ pf workflow resume [NAME]
48
48
  ```
49
49
 
50
50
  | Arg | Required | Description |
@@ -54,7 +54,7 @@ Creates a new session and begins at step 1.
54
54
  ### Workflow Status
55
55
 
56
56
  ```bash
57
- .pennyfarthing/scripts/workflow/workflow-status.sh
57
+ pf workflow status
58
58
  ```
59
59
 
60
60
  No arguments. Shows current step, progress percentage, mode.
@@ -62,7 +62,7 @@ No arguments. Shows current step, progress percentage, mode.
62
62
  ### Fix Session Phase
63
63
 
64
64
  ```bash
65
- .pennyfarthing/scripts/workflow/fix-session-phase.sh <STORY_ID> <TARGET_PHASE> [--dry-run]
65
+ pf workflow fix-phase <STORY_ID> <TARGET_PHASE> [--dry-run]
66
66
  ```
67
67
 
68
68
  | Arg/Option | Required | Description |
@@ -80,9 +80,22 @@ Valid phases by workflow:
80
80
 
81
81
  Updates `**Phase:**` field, `**Phase Started:**` timestamp, and handoff history table.
82
82
 
83
+ ### Complete Step
84
+
85
+ ```bash
86
+ pf workflow complete-step [NAME] [--step N]
87
+ ```
88
+
89
+ | Arg/Option | Required | Description |
90
+ |------------|----------|-------------|
91
+ | `NAME` | No | Workflow name (auto-detects from session) |
92
+ | `--step N` | No | Complete specific step number instead of current |
93
+
94
+ Advances session: increments step, updates completed list, recalculates percentage.
95
+
83
96
  ---
84
97
 
85
- ## Python CLI Commands
98
+ ## State & Phase Commands
86
99
 
87
100
  ### Check Workflow State
88
101
 
@@ -109,6 +122,18 @@ pf workflow phase-check <WORKFLOW_NAME> <PHASE>
109
122
 
110
123
  Returns the agent name that owns the phase.
111
124
 
125
+ ### Get Workflow Type
126
+
127
+ ```bash
128
+ pf workflow type <WORKFLOW_NAME>
129
+ ```
130
+
131
+ | Arg | Required | Description |
132
+ |-----|----------|-------------|
133
+ | `WORKFLOW_NAME` | Yes | Workflow name (e.g., `tdd`, `architecture`) |
134
+
135
+ Returns `phased`, `stepped`, or `procedural`.
136
+
112
137
  ### Emit Handoff Marker
113
138
 
114
139
  ```bash
@@ -1,14 +1,23 @@
1
1
  # BDD Tandem Workflow - BDD with Full Tandem Chain
2
- # Extends standard BDD with tandem observers:
3
- # - Architect backseats UX-Designer during design
4
- # - UX-Designer backseats Dev during implementation
5
- # - PM backseats Reviewer during review
6
2
  #
7
- # Flow: SM UX-Designer (+Architect) TEA → Dev (+UX-Designer) → Reviewer (+PM) → SM
3
+ # When to use: Best for UI/UX-heavy stories where designer-architect pairing
4
+ # shapes implementation. Use this instead of base bdd when the story involves
5
+ # complex user flows, new component patterns, or cross-domain UI integration.
6
+ #
7
+ # Tandem pairings:
8
+ # - DESIGN phase: UX-Designer + Architect (file-watch) — Architect validates technical feasibility
9
+ # - RED phase: TEA + Dev (file-watch) — Dev provides implementation context for test strategy
10
+ # - GREEN phase: Dev + UX-Designer (file-watch) — UX-Designer ensures design fidelity
11
+ # - REVIEW phase: Reviewer + PM (file-watch) — PM validates business requirements
12
+ #
13
+ # Compared to base bdd: Adds tandem observers on every working phase.
14
+ # Designer-developer pairing ensures UX intent survives implementation.
15
+ #
16
+ # Flow: SM → UX-Designer (+Architect) → TEA (+Dev) → Dev (+UX-Designer) → Reviewer (+PM) → SM
8
17
 
9
18
  workflow:
10
19
  name: bdd-tandem
11
- description: BDD with full tandem chain across design, implementation, and review
20
+ description: BDD with full tandem chain designer and architect consultation across phases
12
21
  version: "1.0.0"
13
22
 
14
23
  phases:
@@ -36,6 +45,9 @@ workflow:
36
45
  file: gates/tests-fail
37
46
  type: tests_fail
38
47
  condition: Behavior scenarios have test coverage
48
+ tandem:
49
+ partner: dev
50
+ scope: file-watch
39
51
 
40
52
  - name: green
41
53
  agent: dev
@@ -32,7 +32,7 @@ Build a complete picture of uncommitted changes across ALL repos defined in `.cl
32
32
  **CRITICAL: Use the multi-repo script, not plain `git status`.**
33
33
 
34
34
  ```bash
35
- .pennyfarthing/scripts/git/git-status-all.sh
35
+ pf git status
36
36
  ```
37
37
 
38
38
  This shows branch, staged/unstaged changes, and unpushed commits for **all repos** defined in the project configuration.
@@ -34,7 +34,7 @@ Git cleanup summary report including:
34
34
  ### 4.1 Final Git Status (All Repos)
35
35
 
36
36
  ```bash
37
- .pennyfarthing/scripts/git/git-status-all.sh
37
+ pf git status
38
38
  ```
39
39
 
40
40
  Expected: Clean working directory in all repos, or only intentionally skipped files.
@@ -68,7 +68,7 @@ To run git-cleanup again:
68
68
  Or for a quick status check across all repos:
69
69
 
70
70
  ```bash
71
- .pennyfarthing/scripts/git/git-status-all.sh
71
+ pf git status
72
72
  ```
73
73
 
74
74
  ---
@@ -0,0 +1,65 @@
1
+ # Review Tandem Workflow - TDD with Focused Architectural Review
2
+ #
3
+ # When to use: Best for stories that primarily need architectural review during
4
+ # the review phase. Use this for large refactors (10+ pts), cross-cutting
5
+ # infrastructure changes, or stories where an architect audit catches issues
6
+ # that a standard code review might miss.
7
+ #
8
+ # Tandem pairings:
9
+ # - REVIEW phase: Reviewer + Architect (file-watch) — Architect provides
10
+ # focused architectural audit alongside standard code review
11
+ #
12
+ # Compared to base tdd: Same phase flow, but adds architect consultation
13
+ # during review only. Lighter than tdd-tandem (no tandem on red/green phases).
14
+ #
15
+ # Flow: SM → TEA → Dev → Reviewer (+Architect) → SM
16
+
17
+ workflow:
18
+ name: review-tandem
19
+ description: TDD with tandem architect consultation on review phase
20
+ version: "1.0.0"
21
+
22
+ phases:
23
+ - name: setup
24
+ agent: sm
25
+ output: [session_file, branches, story_context]
26
+
27
+ - name: red
28
+ agent: tea
29
+ input: [session_file, story_context]
30
+ output: [failing_tests]
31
+ gate:
32
+ file: gates/tests-fail
33
+ type: tests_fail
34
+ condition: All acceptance criteria have test coverage
35
+
36
+ - name: green
37
+ agent: dev
38
+ input: [failing_tests, story_context]
39
+ output: [implementation, passing_tests]
40
+ gate:
41
+ file: gates/tests-pass
42
+ type: tests_pass
43
+ condition: All tests passing, no skipped tests
44
+
45
+ - name: review
46
+ agent: reviewer
47
+ input: [implementation, passing_tests]
48
+ output: [approval]
49
+ gate:
50
+ file: gates/approval
51
+ type: approval
52
+ condition: Code review approved, architecture validated
53
+ tandem:
54
+ partner: architect
55
+ scope: file-watch
56
+
57
+ - name: finish
58
+ agent: sm
59
+ input: [approval]
60
+ output: [archived_session, story_summary]
61
+
62
+ triggers:
63
+ tags: [review-tandem, tandem]
64
+ points:
65
+ min: 8
@@ -1,14 +1,22 @@
1
1
  # TDD Tandem Workflow - TDD with Full Tandem Chain
2
- # Extends standard TDD with tandem observers on every phase:
3
- # - Architect backseats TEA during red phase
4
- # - TEA backseats Dev during green phase
5
- # - PM backseats Reviewer during review phase
6
2
  #
7
- # Flow: SM TEA (+Architect) Dev (+TEA) Reviewer (+PM) → SM
3
+ # When to use: Best for 5+ point feature stories where architectural alignment
4
+ # during implementation provides high value. Use this instead of base tdd when
5
+ # the story involves cross-cutting concerns, new patterns, or complex integrations.
6
+ #
7
+ # Tandem pairings:
8
+ # - RED phase: TEA + Architect (file-watch) — Architect reviews test strategy
9
+ # - GREEN phase: Dev + Architect (file-watch) — Architect guides implementation patterns
10
+ # - REVIEW phase: Reviewer + PM (file-watch) — PM validates business requirements
11
+ #
12
+ # Compared to base tdd: Adds tandem observers on every working phase.
13
+ # The extra context from partner agents helps catch architectural drift early.
14
+ #
15
+ # Flow: SM → TEA (+Architect) → Dev (+Architect) → Reviewer (+PM) → SM
8
16
 
9
17
  workflow:
10
18
  name: tdd-tandem
11
- description: TDD with full tandem chain across all phases
19
+ description: TDD with full tandem chain — architect and PM consultation across phases
12
20
  version: "2.0.0"
13
21
 
14
22
  phases:
@@ -37,7 +45,7 @@ workflow:
37
45
  type: tests_pass
38
46
  condition: All tests passing, no skipped tests
39
47
  tandem:
40
- partner: tea
48
+ partner: architect
41
49
  scope: file-watch
42
50
 
43
51
  - name: review
@@ -60,5 +68,5 @@ workflow:
60
68
  triggers:
61
69
  tags: [tandem]
62
70
  points:
63
- min: 3
71
+ min: 5
64
72
  default: false
@@ -12,8 +12,9 @@ Python Click CLI for Pennyfarthing. Entry point: `pennyfarthing_scripts/cli.py`.
12
12
  | `pf bc` | Panel focus and layout management | `bc/cli.py` |
13
13
  | `pf validate` | Project validators | `validate/cli.py` |
14
14
  | `pf agent` | Agent session management | `cli.py` (inline) |
15
- | `pf workflow` | Workflow state and phase management | `cli.py` (inline) |
15
+ | `pf workflow` | Workflow state and phase management | `workflow/cli.py` |
16
16
  | `pf bikerack` | BikeRack dashboard launcher | `bikerack/cli.py` |
17
+ | `pf git` | Repository operations (status, branches, worktree, hooks) | `git_group/cli.py` |
17
18
  | `pf debug` | Analysis tools (hotspots, deadcode, healthscore) | `cli.py` (inline group) |
18
19
 
19
20
  ### Sugar Shortcuts
@@ -139,9 +140,17 @@ Options: `--session-id`, `--no-persona`, `--json`, `--minimal`, `--full`, `--qui
139
140
 
140
141
  | Command | Description | Source |
141
142
  |---------|-------------|--------|
142
- | `pf workflow check` | Current workflow state | `workflow.py` |
143
- | `pf workflow phase-check WORKFLOW PHASE` | Check phase owner | `workflow.py` |
144
- | `pf workflow handoff AGENT` | Emit handoff marker | `cli.py` (inline) |
143
+ | `pf workflow check [--json]` | Current workflow state | `workflow/cli.py` |
144
+ | `pf workflow phase-check WORKFLOW PHASE` | Check phase owner | `workflow/cli.py` |
145
+ | `pf workflow handoff AGENT` | Emit handoff marker | `workflow/cli.py` |
146
+ | `pf workflow type WORKFLOW` | Get workflow type (phased/stepped/procedural) | `workflow/cli.py` |
147
+ | `pf workflow list` | List all available workflows | `workflow/cli.py` |
148
+ | `pf workflow show [NAME]` | Show workflow details | `workflow/cli.py` |
149
+ | `pf workflow start NAME [--mode M]` | Start stepped workflow | `workflow/cli.py` |
150
+ | `pf workflow resume [NAME]` | Resume interrupted workflow | `workflow/cli.py` |
151
+ | `pf workflow status [NAME]` | Show stepped workflow progress | `workflow/cli.py` |
152
+ | `pf workflow fix-phase ID PHASE [--dry-run]` | Repair session phase | `workflow/cli.py` |
153
+ | `pf workflow complete-step [NAME] [--step N]` | Complete current step | `workflow/cli.py` |
145
154
 
146
155
  ## pf bikerack
147
156
 
@@ -151,6 +160,19 @@ Options: `--session-id`, `--no-persona`, `--json`, `--minimal`, `--full`, `--qui
151
160
  | `pf bikerack stop` | Stop running instance | `bikerack/cli.py` |
152
161
  | `pf bikerack status` | Show running state | `bikerack/cli.py` |
153
162
 
163
+ ## pf git
164
+
165
+ | Command | Description | Source |
166
+ |---------|-------------|--------|
167
+ | `pf git status [--brief]` | Check git status of all repos | `git/status_all.py` |
168
+ | `pf git branches BRANCH [--repos all\|api\|ui]` | Create feature branches | `git/create_branches.py` |
169
+ | `pf git cleanup` | Start git-cleanup workflow | `git_group/cli.py` |
170
+ | `pf git worktree create NAME BRANCH` | Create worktree(s) | `git/worktree.py` |
171
+ | `pf git worktree remove NAME` | Remove worktree | `git/worktree.py` |
172
+ | `pf git worktree list` | List active worktrees | `git/worktree.py` |
173
+ | `pf git worktree status` | Show worktree status | `git/worktree.py` |
174
+ | `pf git install-hooks` | Install git hooks with .d/ dispatcher | `git/hooks_installer.py` |
175
+
154
176
  ## pf debug
155
177
 
156
178
  | Command | Description | Source |
@@ -34,8 +34,8 @@ from pennyfarthing_scripts.bc.focus import (
34
34
  def _get_current_layout() -> dict | None:
35
35
  """Fetch the current layout from a running Cyclist or BikeRack server.
36
36
 
37
- Checks .bikerack-port first, then .cyclist-port, and fetches the
38
- appropriate layout endpoint.
37
+ Reads .wheelhub-port (shared by both Cyclist and BikeRack) and fetches
38
+ the layout endpoint.
39
39
 
40
40
  Returns:
41
41
  Layout dict, or None if no server is running or fetch fails.
@@ -44,10 +44,8 @@ def _get_current_layout() -> dict | None:
44
44
 
45
45
  root = _get_root()
46
46
 
47
- # Try BikeRack first (bikerack-layout), then Cyclist (layout)
48
47
  candidates = [
49
- (root / ".bikerack-port", "/api/settings/bikerack-layout"),
50
- (root / ".cyclist-port", "/api/settings/bikerack-layout"),
48
+ (root / ".wheelhub-port", "/api/settings/bikerack-layout"),
51
49
  ]
52
50
 
53
51
  for port_file, endpoint in candidates:
@@ -6,12 +6,13 @@ list with status indicators (running, completed, failed).
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
+ import time
9
10
  from typing import Any
10
11
 
11
12
  from rich.console import Group
12
13
  from rich.text import Text
13
14
 
14
- from pennyfarthing_scripts.bikerack.base_panel import PANEL_ICONS, BasePanel
15
+ from pennyfarthing_scripts.bikerack.base_panel import PANEL_ICONS, BasePanel, format_duration
15
16
 
16
17
 
17
18
  class BackgroundPanel(BasePanel):
@@ -25,12 +26,62 @@ class BackgroundPanel(BasePanel):
25
26
  panel_name: str = "Background"
26
27
  icon: str = PANEL_ICONS["background"][0]
27
28
 
29
+ def __init__(self, client=None, **kwargs):
30
+ super().__init__(client=client, **kwargs)
31
+ self._timer = None
32
+
33
+ def on_mount(self) -> None:
34
+ """Subscribe to channel and start elapsed timer."""
35
+ super().on_mount()
36
+ self._timer = self.set_interval(1, self._tick)
37
+
38
+ def on_unmount(self) -> None:
39
+ """Stop timer and cleanup."""
40
+ if self._timer is not None:
41
+ self._timer.stop()
42
+ super().on_unmount()
43
+
44
+ def _tick(self) -> None:
45
+ """Re-render every second to update elapsed times."""
46
+ if self._last_payload is not None:
47
+ rendered = self.render_panel(self._last_payload)
48
+ try:
49
+ self.update(rendered)
50
+ except Exception:
51
+ pass
52
+
28
53
  def render_panel(self, payload: dict[str, Any]) -> Any:
29
54
  """Render background task data from WebSocket payload."""
30
55
  tasks = payload.get("tasks", [])
31
56
  if not isinstance(tasks, list) or not tasks:
32
57
  return Text("No background tasks", style="dim italic")
33
58
 
59
+ running = 0
60
+ done = 0
61
+ failed = 0
62
+ for task in tasks:
63
+ if not isinstance(task, dict):
64
+ continue
65
+ if task.get("completedAt") is not None:
66
+ if task.get("success"):
67
+ done += 1
68
+ else:
69
+ failed += 1
70
+ else:
71
+ running += 1
72
+
73
+ # Summary header
74
+ summary = Text()
75
+ summary.append(f"{running} running", style="yellow")
76
+ summary.append(" ")
77
+ summary.append(f"{done} done", style="green")
78
+ if failed > 0:
79
+ summary.append(" ")
80
+ summary.append(f"{failed} failed", style="red")
81
+
82
+ separator = Text("─" * 30, style="dim")
83
+
84
+ # Task list
34
85
  parts: list[Any] = []
35
86
  for task in tasks:
36
87
  if not isinstance(task, dict):
@@ -40,7 +91,7 @@ class BackgroundPanel(BasePanel):
40
91
  if not parts:
41
92
  return Text("No background tasks", style="dim italic")
42
93
 
43
- return Group(*parts)
94
+ return Group(summary, separator, *parts)
44
95
 
45
96
 
46
97
  def _render_task(task: dict[str, Any]) -> Text:
@@ -48,6 +99,7 @@ def _render_task(task: dict[str, Any]) -> Text:
48
99
  description = task.get("description", "Unknown task")
49
100
  subagent_type = task.get("subagentType", "")
50
101
  completed_at = task.get("completedAt")
102
+ started_at = task.get("startedAt")
51
103
  success = task.get("success")
52
104
  result = task.get("result", "")
53
105
  error = task.get("error", "")
@@ -60,7 +112,12 @@ def _render_task(task: dict[str, Any]) -> Text:
60
112
  line.append(description)
61
113
  if subagent_type:
62
114
  line.append(f" [{subagent_type}]", style="dim")
63
- line.append(" done", style="green")
115
+ # Show duration if timestamps available
116
+ duration = _calc_duration(started_at, completed_at)
117
+ if duration:
118
+ line.append(f" — {duration}", style="green")
119
+ else:
120
+ line.append(" — done", style="green")
64
121
  if result:
65
122
  line.append(f" ({result})", style="dim green")
66
123
  else:
@@ -68,7 +125,11 @@ def _render_task(task: dict[str, Any]) -> Text:
68
125
  line.append(description)
69
126
  if subagent_type:
70
127
  line.append(f" [{subagent_type}]", style="dim")
71
- line.append(" failed", style="red")
128
+ duration = _calc_duration(started_at, completed_at)
129
+ if duration:
130
+ line.append(f" — {duration}", style="red")
131
+ else:
132
+ line.append(" — failed", style="red")
72
133
  if error:
73
134
  line.append(f"\n {error}", style="red")
74
135
  else:
@@ -76,6 +137,26 @@ def _render_task(task: dict[str, Any]) -> Text:
76
137
  line.append(description)
77
138
  if subagent_type:
78
139
  line.append(f" [{subagent_type}]", style="dim")
79
- line.append(" running", style="yellow")
140
+ # Live elapsed time for running tasks
141
+ if started_at:
142
+ try:
143
+ elapsed = time.time() - float(started_at) / 1000 # startedAt is typically ms
144
+ line.append(f" — {format_duration(elapsed)}", style="yellow")
145
+ except (ValueError, TypeError):
146
+ line.append(" — running", style="yellow")
147
+ else:
148
+ line.append(" — running", style="yellow")
80
149
 
81
150
  return line
151
+
152
+
153
+ def _calc_duration(started_at, completed_at) -> str:
154
+ """Calculate duration string from timestamps."""
155
+ if started_at is None or completed_at is None:
156
+ return ""
157
+ try:
158
+ start = float(started_at) / 1000 # ms to seconds
159
+ end = float(completed_at) / 1000
160
+ return format_duration(end - start)
161
+ except (ValueError, TypeError):
162
+ return ""
@@ -9,6 +9,7 @@ from __future__ import annotations
9
9
 
10
10
  from typing import Any
11
11
 
12
+ from rich.text import Text
12
13
  from textual.widgets import Static
13
14
 
14
15
  # Nerd Font icon registry: panel_name → (nerd_font_icon, ascii_fallback)
@@ -25,6 +26,7 @@ PANEL_ICONS: dict[str, tuple[str, str]] = {
25
26
  "debug": ("\uf188", "d"), # nf-fa-bug
26
27
  "settings": ("\uf013", "S"), # nf-fa-gear
27
28
  "tty": ("\uf120", ">"), # nf-fa-terminal
29
+ "progress": ("\uf200", "P"), # nf-fa-pie_chart
28
30
  }
29
31
 
30
32
 
@@ -44,6 +46,66 @@ def get_panel_icon(panel_name: str, use_nerd_font: bool = True) -> str:
44
46
  return entry[0] if use_nerd_font else entry[1]
45
47
 
46
48
 
49
+ def render_progress_bar(percent: int | float, width: int = 20, warn_high: bool = False) -> Text:
50
+ """Render a Unicode progress bar with color based on percentage.
51
+
52
+ Args:
53
+ percent: Value 0-100.
54
+ width: Number of bar characters (default 20).
55
+ warn_high: If True, use red at high values (for resource usage).
56
+ If False (default), use blue at 100% (for completion).
57
+
58
+ Returns:
59
+ Rich Text like ``[████████░░░░░░░░░░░░] 22%``
60
+ """
61
+ percent = max(0, min(100, int(percent)))
62
+ filled = round(width * percent / 100)
63
+ empty = width - filled
64
+
65
+ if warn_high:
66
+ if percent < 50:
67
+ style = "green"
68
+ elif percent <= 80:
69
+ style = "yellow"
70
+ else:
71
+ style = "red"
72
+ else:
73
+ style = "blue"
74
+
75
+ bar = Text()
76
+ bar.append("[")
77
+ bar.append("█" * filled, style=style)
78
+ bar.append("░" * empty, style="dim")
79
+ bar.append(f"] {percent}%")
80
+ return bar
81
+
82
+
83
+ def format_duration(seconds: int | float) -> str:
84
+ """Format seconds into human-friendly duration string.
85
+
86
+ Returns:
87
+ ``47s``, ``2m 14s``, ``1h 5m``.
88
+ """
89
+ seconds = max(0, int(seconds))
90
+ if seconds < 60:
91
+ return f"{seconds}s"
92
+ minutes, secs = divmod(seconds, 60)
93
+ if minutes < 60:
94
+ return f"{minutes}m {secs}s"
95
+ hours, mins = divmod(minutes, 60)
96
+ return f"{hours}h {mins}m"
97
+
98
+
99
+ def humanize_theme(slug: str) -> str:
100
+ """Convert a theme slug to a display name.
101
+
102
+ ``princess-bride`` → ``Princess Bride``
103
+ """
104
+ if not slug:
105
+ return ""
106
+ return slug.replace("-", " ").replace("_", " ").title()
107
+
108
+
47
109
  class BasePanel(Static):
48
110
  """Base class for BikeRack TUI panels.
49
111