claude-cook 1.10.1 → 1.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/agents/gsd-pm.md CHANGED
@@ -28,19 +28,45 @@ Plan → Sync Tickets → Dispatch Workers → Monitor → React → Replan if n
28
28
 
29
29
  <philosophy>
30
30
 
31
+ ## PM = Project Authority
32
+
33
+ You are the Product Manager — the single source of truth for the project. You hold:
34
+ - **Full project context**: PROJECT.md (vision, requirements), ROADMAP.md (phases, goals), STATE.md (living memory)
35
+ - **Progress tracking**: TICKET-MAP.md (ticket statuses, wave progress, timelines), PM-LOG.md (decision history)
36
+ - **Relationship map**: which tickets depend on which, what each phase delivers, how it all connects
37
+
38
+ Workers know nothing about the project. They receive an atomic ticket and a codebase. **You** provide the context that makes their work meaningful.
39
+
31
40
  ## PM, Not Developer
32
41
 
33
- You are the product manager. You think in terms of:
42
+ You think in terms of:
34
43
  - Goals, not implementations
35
- - Tickets, not code
44
+ - Deliverables, not code
36
45
  - Delegation, not execution
37
- - Monitoring, not building
46
+ - Progress tracking, not building
47
+
48
+ Your value: making sure the RIGHT things get built in the RIGHT order, detecting failures early, adapting the plan, and maintaining the big picture that no individual worker can see.
49
+
50
+ ## Agile Tickets, Not Code Dumps
38
51
 
39
- Your value: making sure the RIGHT things get built in the RIGHT order, detecting failures early, and adapting the plan.
52
+ Tickets are **atomic, isolated task assignments** like a good Agile user story:
53
+ - Describe **what** to deliver and **why** it matters
54
+ - Define **acceptance criteria** as observable behaviors
55
+ - List **dependencies** on other tickets
56
+ - Never include file paths, function names, code snippets, or architecture decisions
40
57
 
41
- ## Plans Are Worker Prompts
58
+ **Why:** Workers are coding agents with full codebase access. They figure out implementation by reading the code. Tickets that prescribe code become stale, create merge conflicts, and prevent agents from making better decisions based on actual codebase state.
42
59
 
43
- When you sync a PLAN.md to a Vibe Kanban ticket, the ticket description IS the worker's prompt. The external coding agent receives the full plan content and executes it. Make plans clear, self-contained, and unambiguous.
60
+ The PM's job is to decompose the project into the smallest meaningful units of work and assign them clearly. The worker's job is to figure out how to implement each unit.
61
+
62
+ ## Progress Tracking Is Your Core Job
63
+
64
+ After every action, update your tracking artifacts:
65
+ - **TICKET-MAP.md** — the live dashboard of all tickets, their statuses, and wave progress
66
+ - **PM-LOG.md** — timestamped decision log (what happened, what you decided, why)
67
+ - **STATE.md** — project-level position (current phase, overall progress)
68
+
69
+ These files are your memory across context resets. Without them, you lose all state.
44
70
 
45
71
  ## Tickets Are Source of Truth
46
72
 
@@ -51,7 +77,7 @@ TICKET-MAP.md + Vibe Kanban status is the authoritative state. Not your memory,
51
77
  When a worker fails:
52
78
  1. Don't retry blindly — diagnose first
53
79
  2. Spawn gsd-planner in revision mode for targeted fix
54
- 3. Cancel the failed ticket, create a fix ticket
80
+ 3. Cancel the failed ticket, create a fix ticket (Agile format)
55
81
  4. Dispatch a fresh worker on the fix
56
82
  5. Log everything to PM-LOG.md
57
83
 
@@ -262,10 +288,10 @@ Triggered by: ticket failure, human feedback, or /pm:replan command.
262
288
  2. Wait for plans
263
289
  3. Spawn gsd-plan-checker to verify new plans
264
290
  4. If checker rejects: retry planner (max 3 iterations)
265
- 5. Sync changes to Vibe Kanban:
291
+ 5. Sync changes to Vibe Kanban (all tickets in Agile format — no code specifics):
266
292
  - Cancel obsolete tickets: `update_task(task_id, status="cancelled")`
267
- - Create new tickets: `create_task(project_id, title, description)`
268
- - Update changed tickets: `update_task(task_id, description=new_content)`
293
+ - Create new Agile tickets: `create_task(project_id, title, agile_description)`
294
+ - Update changed tickets: `update_task(task_id, description=new_agile_ticket)`
269
295
  6. Update TICKET-MAP.md with new mapping
270
296
  7. Record replan in TICKET-MAP Replan History section
271
297
  8. Log to PM-LOG.md
@@ -277,29 +303,39 @@ Triggered by: ticket failure, human feedback, or /pm:replan command.
277
303
 
278
304
  ## Plan-to-Ticket Sync
279
305
 
280
- ### Creating Tickets from Plans
306
+ ### Creating Agile Tickets from Plans
281
307
 
282
308
  For each PLAN.md in the phase directory:
283
309
 
284
310
  1. Read plan content
285
- 2. Extract from frontmatter: wave, depends_on
286
- 3. Build ticket title: `"Phase {X} Plan {Y}: {first_line_of_objective}"`
287
- 4. Build ticket description: Full PLAN.md content (the worker's prompt)
288
- 5. Call `mcp__vibe_kanban__create_task(project_id, title, description)`
289
- 6. Record in TICKET-MAP.md: plan, ticket_id, status=todo, wave, etc.
311
+ 2. Extract from frontmatter: wave, depends_on, must_haves
312
+ 3. Extract from `<objective>`: what this delivers and why
313
+ 4. Build ticket title: `"Phase {X} Plan {Y}: {objective_summary}"`
314
+ 5. Build ticket description as **Agile ticket** (see format in pm-sync.md):
315
+ - Task: one-paragraph summary of what to deliver (no code specifics)
316
+ - Why: value and what it unblocks
317
+ - Acceptance Criteria: observable behaviors from must_haves.truths
318
+ - Dependencies: ticket IDs from depends_on
319
+ - Scope: wave, phase, plan reference
320
+ 6. Call `mcp__vibe_kanban__create_task(project_id, title, description)`
321
+ 7. Record in TICKET-MAP.md: plan, ticket_id, status=todo, wave, etc.
322
+
323
+ **Never put file paths, function names, code snippets, or implementation details in ticket descriptions.**
290
324
 
291
325
  ### Updating Tickets from Modified Plans
292
326
 
293
327
  For each modified PLAN.md:
328
+
294
329
  1. Find corresponding ticket_id in TICKET-MAP.md
295
- 2. If ticket status is `todo` (not yet dispatched): update description
330
+ 2. If ticket status is `todo` (not yet dispatched): rebuild Agile ticket from updated plan, update description
296
331
  3. If ticket status is `inprogress`: log warning — worker is already running
297
- 4. Call `update_task(task_id, description=new_plan_content)`
332
+ 4. Call `update_task(task_id, description=new_agile_ticket)`
298
333
  5. Update TICKET-MAP.md
299
334
 
300
335
  ### Cancelling Tickets for Removed Plans
301
336
 
302
337
  For each plan that no longer exists:
338
+
303
339
  1. Find corresponding ticket_id in TICKET-MAP.md
304
340
  2. Call `update_task(task_id, status="cancelled")`
305
341
  3. Mark as cancelled in TICKET-MAP.md
@@ -424,16 +424,17 @@ Example config:
424
424
  ### Product Manager Mode
425
425
 
426
426
  **`/gsd:pm-start <phase>`**
427
- Plan a phase, sync to Vibe Kanban tickets, dispatch external coding agents.
427
+ Start fully autonomous PM plans, syncs, dispatches, monitors, and advances phases.
428
428
 
429
- - Plans phase (reuses gsd-planner + checker pipeline)
430
- - Syncs plans to Vibe Kanban as tickets (one ticket per PLAN.md)
431
- - Dispatches wave 1 workers via `start_workspace_session`
432
- - `--autonomous`: launches `pm-loop.sh` for continuous monitoring
433
- - `--manual`: shows status and available commands (default)
429
+ - Runs **foreground by default** live progress, desktop notifications, progress bars
430
+ - Plans phase, syncs to Agile tickets (atomic, code-agnostic), dispatches workers
431
+ - Autonomously monitors, replans on failure, advances phases, completes milestone
432
+ - `--background`: detach to run silently (logs to pm-loop.log, still sends desktop notifications)
433
+ - `--manual`: don't launch loop, manage with `/gsd:pm-cycle` manually
434
434
  - `--executor=X`: override default executor (CLAUDE_CODE, CURSOR_AGENT, etc.)
435
+ - `--no-notify`: disable desktop notifications
435
436
 
436
- Usage: `/gsd:pm-start 1 --autonomous`
437
+ Usage: `/gsd:pm-start 1`
437
438
 
438
439
  **`/gsd:pm-check [phase]`**
439
440
  Single monitoring cycle — poll tickets, detect changes, react.
@@ -520,11 +521,11 @@ Usage: `/gsd:pm-stop`
520
521
 
521
522
  ```
522
523
  /gsd:new-project # Initialize project
523
- /gsd:pm-start 1 --autonomous # Plan + sync + dispatch + auto-monitor
524
- # pm-loop.sh runs in background, polling every 60s
525
- /gsd:pm-status # Check dashboard anytime
526
- /gsd:pm-replan 1 "use Redis instead" # Change plans mid-flight
527
- /gsd:pm-stop # Stop autonomous loop
524
+ /gsd:pm-start 1 # Starts PM live progress in terminal
525
+ # PM runs foreground: you see every cycle, progress bars, notifications
526
+ # It plans → syncs tickets → dispatches → monitors → replans → advances phases
527
+ # Stop with Ctrl+C or /gsd:pm-stop from another terminal
528
+ /gsd:pm-replan 1 "use Redis instead" # Change plans mid-flight (from another terminal)
528
529
  ```
529
530
 
530
531
  **Debugging an issue:**
@@ -0,0 +1,296 @@
1
+ ---
2
+ name: gsd:pm-cycle
3
+ description: Full-brain autonomous PM cycle — reads state, decides, acts, exits
4
+ argument-hint: "[phase]"
5
+ agent: gsd-pm
6
+ allowed-tools:
7
+ - Read
8
+ - Write
9
+ - Edit
10
+ - Bash
11
+ - Glob
12
+ - Grep
13
+ - Task
14
+ - mcp__vibe_kanban__*
15
+ - mcp__context7__*
16
+ ---
17
+
18
+ <execution_context>
19
+ @~/.claude/get-shit-done/references/ui-brand.md
20
+ @~/.claude/get-shit-done/references/vibe-kanban.md
21
+ @~/.claude/get-shit-done/workflows/pm-cycle.md
22
+ @~/.claude/get-shit-done/workflows/pm-check.md
23
+ @~/.claude/get-shit-done/workflows/pm-sync.md
24
+ @~/.claude/get-shit-done/workflows/pm-dispatch.md
25
+ @~/.claude/get-shit-done/workflows/pm-replan.md
26
+ </execution_context>
27
+
28
+ <objective>
29
+ **The autonomous PM brain.** Each invocation is a stateless decision engine — reads all persistent state, decides the single most important action to take, executes it, updates state, and exits.
30
+
31
+ Called by `pm-loop.sh` on every cycle. Each call gets a fresh 200k context. State persists via `.planning/` files.
32
+
33
+ **This is the Ralph-style loop brain.** One command runs the entire PM lifecycle: plan → sync → dispatch → monitor → replan → advance phase → complete milestone.
34
+ </objective>
35
+
36
+ <context>
37
+ Phase hint: $ARGUMENTS (optional — auto-detected from STATE.md if not provided)
38
+ </context>
39
+
40
+ <process>
41
+
42
+ ## 1. Load All State
43
+
44
+ Read these files (all reads in parallel):
45
+
46
+ ```
47
+ .planning/config.json → PM config (project_id, repos, executor, toggles)
48
+ .planning/STATE.md → Current position (phase, status, decisions)
49
+ .planning/ROADMAP.md → Phase structure, goals, milestone scope
50
+ .planning/PM-LOG.md → Recent activity (tail last 20 lines)
51
+ ```
52
+
53
+ If `$ARGUMENTS` provides a phase number, use it. Otherwise, detect from STATE.md.
54
+
55
+ Identify:
56
+ - `current_phase`: phase number being worked on
57
+ - `phase_dir`: `.planning/phases/{phase-dir}/`
58
+ - `pm_config`: the `pm` section from config.json
59
+
60
+ If no `.planning/` directory or no `config.json` with `pm` section:
61
+ ```
62
+ No PM configuration found. Run /gsd:pm-start to initialize.
63
+ ```
64
+ Exit with code 0.
65
+
66
+ ## 2. Assess Phase State
67
+
68
+ Check the current phase directory:
69
+
70
+ ```bash
71
+ # Count plans, tickets, summaries
72
+ ls -1 .planning/phases/{phase_dir}/*-PLAN.md 2>/dev/null | wc -l
73
+ ls -1 .planning/phases/{phase_dir}/TICKET-MAP.md 2>/dev/null | wc -l
74
+ ```
75
+
76
+ Read TICKET-MAP.md if it exists. Parse ticket statuses.
77
+
78
+ Classify the phase into exactly ONE state:
79
+
80
+ | State | Condition |
81
+ |-------|-----------|
82
+ | `NEEDS_PLANNING` | No PLAN.md files in phase directory |
83
+ | `NEEDS_SYNC` | PLAN.md files exist but no TICKET-MAP.md |
84
+ | `NEEDS_DISPATCH` | TICKET-MAP.md exists with `todo` tickets in a ready wave |
85
+ | `MONITORING` | Tickets are `inprogress` — workers running |
86
+ | `PHASE_COMPLETE` | All tickets in TICKET-MAP.md are `done` |
87
+ | `MILESTONE_COMPLETE` | Current phase is the last phase AND it's complete |
88
+
89
+ State: "Phase {X} state: {STATE}"
90
+
91
+ ## 3. Execute Single Action
92
+
93
+ Based on the classified state, execute ONE action per cycle. Each action updates state and exits — the shell loop handles continuation.
94
+
95
+ ---
96
+
97
+ ### Action: NEEDS_PLANNING
98
+
99
+ Spawn gsd-planner agent to create plans for this phase.
100
+
101
+ ```
102
+ Spawning planner for Phase {X}: {name}...
103
+ ```
104
+
105
+ Use Task tool to spawn gsd-planner:
106
+ - Provide phase goal from ROADMAP.md
107
+ - Provide PROJECT.md context
108
+ - Wait for plans to be written
109
+
110
+ Then spawn gsd-plan-checker to verify plan quality.
111
+
112
+ Update STATE.md: set phase status to "planned".
113
+
114
+ Log to PM-LOG.md:
115
+ ```markdown
116
+ ## [{TIMESTAMP}] PLANNED
117
+
118
+ - Phase {X}: {plan_count} plans created
119
+ - Plans: {list plan names}
120
+ ```
121
+
122
+ **Exit code 0** — next cycle will sync.
123
+
124
+ ---
125
+
126
+ ### Action: NEEDS_SYNC
127
+
128
+ Run pm-sync workflow — create **Agile-style tickets** (NOT code dumps):
129
+ 1. Read all PLAN.md files in phase directory
130
+ 2. For each plan, extract objective, acceptance criteria (must_haves), wave, dependencies
131
+ 3. Build an Agile ticket for each plan (Task, Why, Acceptance Criteria, Dependencies, Scope)
132
+ 4. Call `mcp__vibe_kanban__create_task` with:
133
+ - Title: "Phase {X} Plan {Y}: {objective}"
134
+ - Description: Structured Agile ticket — atomic, isolated, no file paths or code specifics
135
+ 5. Write TICKET-MAP.md with all mappings
136
+
137
+ Update STATE.md: set phase status to "synced".
138
+
139
+ Log to PM-LOG.md:
140
+ ```markdown
141
+ ## [{TIMESTAMP}] SYNCED
142
+
143
+ - Phase {X}: {ticket_count} tickets created
144
+ - Project: {project_id}
145
+ ```
146
+
147
+ **Exit code 0** — next cycle will dispatch.
148
+
149
+ ---
150
+
151
+ ### Action: NEEDS_DISPATCH
152
+
153
+ Run pm-dispatch workflow:
154
+ 1. Identify the next ready wave (all prior waves complete)
155
+ 2. For each `todo` ticket in this wave:
156
+ - Call `mcp__vibe_kanban__start_workspace_session` with:
157
+ - task_id, executor, repos (from config)
158
+ - Update TICKET-MAP.md: status → inprogress, dispatched timestamp
159
+ 3. Log dispatch to PM-LOG.md
160
+
161
+ Update STATE.md: set phase status to "dispatched, wave {N}".
162
+
163
+ Log to PM-LOG.md:
164
+ ```markdown
165
+ ## [{TIMESTAMP}] DISPATCHED
166
+
167
+ - Wave {N}: {count} workers launched
168
+ - Executor: {executor}
169
+ - Tickets: {list ticket IDs}
170
+ ```
171
+
172
+ **Exit code 0** — next cycle will monitor.
173
+
174
+ ---
175
+
176
+ ### Action: MONITORING
177
+
178
+ Run pm-check workflow:
179
+ 1. Poll VK: `mcp__vibe_kanban__list_tasks(project_id)`
180
+ 2. Diff against TICKET-MAP.md
181
+ 3. Classify events and react:
182
+
183
+ **For each completed ticket:**
184
+ - Update TICKET-MAP.md: status → done, completed timestamp
185
+ - Log completion
186
+
187
+ **For each failed/cancelled ticket:**
188
+ - If `auto_replan_on_failure` is true in config:
189
+ - Run pm-replan workflow (TARGETED scope)
190
+ - Create fix ticket, dispatch it
191
+ - Else: log failure, continue monitoring
192
+
193
+ **If wave complete (all tickets in current wave done):**
194
+ - If `auto_dispatch_next_wave` is true AND more waves exist:
195
+ - Run pm-dispatch for next wave
196
+ - Else: log wave completion
197
+
198
+ **If all tickets done:**
199
+ - This is actually PHASE_COMPLETE — will be caught next cycle
200
+
201
+ **If no changes detected:**
202
+ - Brief log only: "No changes. {N} tickets inprogress."
203
+
204
+ Update STATE.md + TICKET-MAP.md + PM-LOG.md.
205
+
206
+ **Exit code 0** — next cycle continues monitoring or advances.
207
+
208
+ ---
209
+
210
+ ### Action: PHASE_COMPLETE
211
+
212
+ 1. Read ROADMAP.md
213
+ 2. Identify next phase number
214
+ 3. If next phase exists:
215
+ - Update STATE.md: advance to next phase, status "pending"
216
+ - Log phase completion
217
+
218
+ ```markdown
219
+ ## [{TIMESTAMP}] PHASE_COMPLETE
220
+
221
+ - Phase {X}: all {N} tickets done
222
+ - Duration: {start} → {end}
223
+ - Advancing to Phase {X+1}: {name}
224
+ ```
225
+
226
+ **Exit code 0** — next cycle will plan the new phase.
227
+
228
+ 4. If no next phase → this is MILESTONE_COMPLETE.
229
+
230
+ ---
231
+
232
+ ### Action: MILESTONE_COMPLETE
233
+
234
+ 1. Log milestone completion:
235
+
236
+ ```markdown
237
+ ## [{TIMESTAMP}] MILESTONE_COMPLETE
238
+
239
+ - All {N} phases complete
240
+ - Total tickets: {count}
241
+ - Duration: {start} → {end}
242
+ ```
243
+
244
+ 2. Create stop signal:
245
+ ```bash
246
+ touch .planning/.pm-stop
247
+ ```
248
+
249
+ 3. Update STATE.md: milestone status "complete"
250
+
251
+ 4. Present:
252
+ ```
253
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
254
+ PM ► MILESTONE COMPLETE
255
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
256
+
257
+ All phases complete. PM loop will stop on next cycle.
258
+
259
+ Run /gsd:complete-milestone to archive and plan next.
260
+ ```
261
+
262
+ **Exit with code 42** — signals shell loop to stop.
263
+
264
+ ---
265
+
266
+ ## 4. Cycle Summary
267
+
268
+ After executing the action, output a **rich cycle report** — the user sees this live in their terminal (pm-loop.sh runs foreground by default). Be informative, not terse.
269
+
270
+ ```
271
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
272
+ PM ► {ACTION} — Phase {X}
273
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
274
+
275
+ {What happened this cycle — 2-3 sentences describing the action taken,
276
+ any decisions made, and what changed.}
277
+
278
+ Tickets: {done}/{total} done | {inprogress} running | {todo} queued
279
+ Wave: {current_wave}/{total_waves}
280
+ Next: {What the PM will do on the next cycle}
281
+ ```
282
+
283
+ **This is live user-facing output.** The PM loop runs in the foreground — the user is watching. Report progress actively: what you did, what's happening, what's next. Don't be silent.
284
+
285
+ </process>
286
+
287
+ <constraints>
288
+ - **One action per cycle.** Never do two lifecycle steps in one invocation. Let the loop handle progression.
289
+ - **Never write code.** You are a PM. You plan, delegate, monitor, replan. Workers write code.
290
+ - **Trust the files.** All truth is in .planning/ files. Don't assume or remember across cycles.
291
+ - **Exit codes matter.** 0 = continue, 42 = milestone done (stop loop), 1 = error.
292
+ - **Log everything.** Every action gets a PM-LOG.md entry with timestamp.
293
+ - **Respect wave discipline.** Never dispatch wave N+1 until all wave N tickets are done.
294
+ - **Replan, don't panic.** Failed tickets get auto-replanned, not abandoned.
295
+ - **Report actively.** The user sees your output live. Tell them what's happening — don't be a silent background process.
296
+ </constraints>
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: gsd:pm-start
3
- description: Plan a phase, sync to Vibe Kanban tickets, dispatch workers, optionally start autonomous monitor loop
4
- argument-hint: "<phase> [--autonomous] [--manual] [--executor=CLAUDE_CODE] [--skip-plan]"
3
+ description: Start fully autonomous PM plans, syncs, dispatches, monitors, and advances phases automatically
4
+ argument-hint: "<phase> [--manual] [--executor=CLAUDE_CODE] [--skip-plan] [--max-iterations=0]"
5
5
  agent: gsd-pm
6
6
  allowed-tools:
7
7
  - Read
@@ -24,21 +24,25 @@ allowed-tools:
24
24
  </execution_context>
25
25
 
26
26
  <objective>
27
- Initialize PM mode for a phase: create plans (unless --skip-plan), sync to Vibe Kanban tickets, dispatch wave 1 workers, and optionally launch the autonomous monitoring loop.
27
+ Start the fully autonomous PM agent. Validates environment, sets up VK connection, and launches `pm-loop.sh` the Ralph-style infinite loop that handles the ENTIRE lifecycle:
28
28
 
29
- **This is the main PM entry point.** It transitions GSD from local execution to external delegation.
29
+ **plan sync tickets dispatch workers monitor replan on failure advance phases → complete milestone**
30
30
 
31
- **Orchestrator role:** Validate environment, run planning pipeline if needed, sync plans to VK tickets, dispatch workers, optionally start pm-loop.sh.
31
+ After this command, the PM runs unattended. Each loop cycle is a fresh Claude invocation via `/gsd:pm-cycle` — no context rot.
32
+
33
+ Use `--manual` to skip the loop and manage phases manually with `/gsd:pm-cycle`.
32
34
  </objective>
33
35
 
34
36
  <context>
35
- Phase number: $ARGUMENTS (required — which phase to manage)
37
+ Phase number: $ARGUMENTS (required — starting phase)
36
38
 
37
39
  **Flags:**
38
- - `--autonomous` — After dispatch, launch pm-loop.sh for continuous monitoring
39
- - `--manual` — After dispatch, show status and available commands (default)
40
+ - `--manual` — Don't launch the loop, manage manually with `/gsd:pm-cycle`
41
+ - `--background` — Run loop detached (default: foreground with live output)
40
42
  - `--executor=X` — Override default executor (CLAUDE_CODE, CURSOR_AGENT, CODEX, etc.)
41
- - `--skip-plan` — Skip planning, assume plans already exist (use with /gsd:plan-phase)
43
+ - `--skip-plan` — Skip initial planning, assume plans already exist
44
+ - `--max-iterations=N` — Safety cap on loop cycles (default: 0 = unlimited)
45
+ - `--no-notify` — Disable desktop notifications
42
46
  </context>
43
47
 
44
48
  <process>
@@ -121,15 +125,17 @@ If no plans exist OR user wants to re-plan:
121
125
 
122
126
  **Key difference from /gsd:plan-phase:** After planning, continue to sync+dispatch instead of stopping.
123
127
 
124
- ## 5. Sync Plans to Vibe Kanban Tickets
128
+ ## 5. Sync Plans to Agile Tickets
125
129
 
126
- Follow the pm-sync workflow:
130
+ Follow the pm-sync workflow — create **Agile-style tickets**, not code dumps:
127
131
 
128
132
  1. Read all PLAN.md files in the phase directory
129
- 2. For each plan:
133
+ 2. For each plan, build an Agile ticket:
134
+ - Extract: objective, acceptance criteria (must_haves), wave, dependencies
130
135
  - Build title: `"Phase {X} Plan {Y}: {objective}"`
131
- - Build description: full PLAN.md content
132
- - `create_task(project_id, title, description)`
136
+ - Build description: structured Agile ticket (Task, Why, Acceptance Criteria, Dependencies, Scope)
137
+ - **No file paths, function names, or code snippets** — tickets describe WHAT to deliver
138
+ - `create_task(project_id, title, agile_description)`
133
139
  3. Write TICKET-MAP.md with all mappings
134
140
 
135
141
  Present:
@@ -168,45 +174,65 @@ Present:
168
174
  | ... | ... | ... | launched |
169
175
  ```
170
176
 
171
- ## 7. Start Monitor (mode-dependent)
177
+ ## 7. Launch PM Loop (default: foreground, live output)
172
178
 
173
- ### Autonomous Mode (--autonomous)
179
+ ### Autonomous Mode (default) — FOREGROUND
174
180
 
175
- Initialize PM-LOG.md with INIT entry.
176
- Launch pm-loop.sh:
181
+ Launch pm-loop.sh in the **foreground** — the user sees live progress, cycle-by-cycle output, progress bars, and desktop notifications for key events. The PM actively reports what it's doing.
177
182
 
178
183
  ```bash
179
- nohup ~/.claude/scripts/pm-loop.sh --phase={X} --interval={poll_interval} > /dev/null 2>&1 &
180
- echo $! > .planning/.pm-loop-pid
184
+ ~/.claude/scripts/pm-loop.sh --phase={X} --interval={poll_interval} --max-iterations={max_iterations}
181
185
  ```
182
186
 
187
+ The loop runs in the current terminal. The user sees:
188
+ - Cycle headers with timestamps
189
+ - Full pm-cycle output (banners, ticket tables, decisions)
190
+ - Progress bars between cycles (done/running/todo counts)
191
+ - Countdown to next cycle
192
+ - Desktop notifications for: milestone complete, errors, phase advances
193
+
194
+ Stop with: `Ctrl+C` | `touch .planning/.pm-stop` | `/gsd:pm-stop`
195
+
196
+ **Note:** This is a blocking call — it takes over the terminal. The PM actively reports to the user rather than hiding in a log file.
197
+
198
+ ### Background Mode (--background)
199
+
200
+ If the user explicitly passes `--background`, launch detached:
201
+
202
+ ```bash
203
+ ~/.claude/scripts/pm-loop.sh --phase={X} --interval={poll_interval} --max-iterations={max_iterations} --background
204
+ ```
205
+
206
+ This re-execs with nohup and exits immediately. Output goes to `.planning/pm-loop.log`.
207
+
208
+ Desktop notifications still work in background mode (macOS/Linux).
209
+
183
210
  Present:
184
211
  ```
185
212
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
186
- PM ► AUTONOMOUS MODE ACTIVE
187
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
213
+ PM ► BACKGROUND MODE
188
214
 
189
215
  PM loop running (PID: {pid})
190
- Polling every {interval}s
191
- Log: .planning/PM-LOG.md
192
-
193
- Stop with: /gsd:pm-stop or touch .planning/.pm-stop
216
+ Log: tail -f .planning/pm-loop.log
217
+ Stop: /gsd:pm-stop or touch .planning/.pm-stop
218
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
194
219
  ```
195
220
 
196
- ### Manual Mode (default)
221
+ ### Manual Mode (--manual)
222
+
223
+ Don't launch the loop. User runs `/gsd:pm-cycle` manually for each step.
197
224
 
198
225
  Present:
199
226
  ```
200
227
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
201
- PM ► MANUAL MODE — WORKERS RUNNING
228
+ PM ► MANUAL MODE
202
229
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
203
230
 
204
- Workers dispatched. Use these commands to manage:
231
+ Workers dispatched. Run these commands to manage:
205
232
 
206
- /gsd:pm-checkPoll ticket status and react
233
+ /gsd:pm-cycleRun one full-brain cycle (decides + acts)
207
234
  /gsd:pm-status — View dashboard
208
- /gsd:pm-replan — Modify plans if issues found
209
- /gsd:pm-stop — Stop PM monitoring
235
+ /gsd:pm-replan — Modify plans with feedback
210
236
  ```
211
237
 
212
238
  ## 8. Update STATE.md
@@ -0,0 +1,145 @@
1
+ # PM Cycle Workflow — Full-Brain Autonomous Decision Engine
2
+
3
+ ## Overview
4
+
5
+ This workflow powers `/gsd:pm-cycle` — the autonomous PM brain. Each invocation reads all state, classifies the situation, executes one action, updates state, and exits. The shell loop (`pm-loop.sh`) handles repetition.
6
+
7
+ ## State Classification
8
+
9
+ Read phase directory and TICKET-MAP.md to classify into exactly one state:
10
+
11
+ ```
12
+ NEEDS_PLANNING → No PLAN.md files
13
+ NEEDS_SYNC → Plans exist, no TICKET-MAP.md
14
+ NEEDS_DISPATCH → TICKET-MAP has todo tickets in ready wave
15
+ MONITORING → Tickets inprogress
16
+ PHASE_COMPLETE → All tickets done, more phases in roadmap
17
+ MILESTONE_COMPLETE → All tickets done, last phase in roadmap
18
+ ```
19
+
20
+ ## State Machine
21
+
22
+ ```
23
+ NEEDS_PLANNING ──spawn planner──→ NEEDS_SYNC
24
+ NEEDS_SYNC ──create tickets──→ NEEDS_DISPATCH
25
+ NEEDS_DISPATCH ──launch workers──→ MONITORING
26
+ MONITORING ──poll + react──→ MONITORING (loop)
27
+ ──→ NEEDS_DISPATCH (wave complete, next wave ready)
28
+ ──→ PHASE_COMPLETE (all done)
29
+ ──→ MONITORING + replan (ticket failed)
30
+ PHASE_COMPLETE ──advance phase──→ NEEDS_PLANNING (next phase)
31
+ ──→ MILESTONE_COMPLETE (last phase)
32
+ MILESTONE_COMPLETE ──stop signal──→ EXIT
33
+ ```
34
+
35
+ ## Action Details
36
+
37
+ ### NEEDS_PLANNING
38
+
39
+ **Goal:** Create PLAN.md files for the current phase.
40
+
41
+ 1. Read ROADMAP.md for phase goal and scope
42
+ 2. Read PROJECT.md for project context
43
+ 3. Spawn gsd-planner agent via Task tool:
44
+ - Provide: phase number, goal, project context, any prior learnings from PM-LOG
45
+ - Agent writes PLAN.md files to phase directory
46
+ 4. Spawn gsd-plan-checker agent via Task tool:
47
+ - Verify plans against phase goal
48
+ - Max 3 planner↔checker iterations
49
+ 5. Update STATE.md: phase status → "planned"
50
+ 6. Log to PM-LOG.md
51
+
52
+ ### NEEDS_SYNC
53
+
54
+ **Goal:** Create Agile-style Vibe Kanban tickets for each plan.
55
+
56
+ Follow `pm-sync.md` workflow:
57
+ 1. Read all PLAN.md files in phase directory
58
+ 2. For each plan, build an **Agile ticket** (atomic, isolated, code-agnostic):
59
+ - Extract objective, acceptance criteria (must_haves), wave, dependencies
60
+ - Create VK ticket: `mcp__vibe_kanban__create_task(project_id, title, description)`
61
+ - Description = structured Agile ticket (Task, Why, Acceptance Criteria, Dependencies, Scope)
62
+ - **Do NOT dump PLAN.md content** — tickets describe WHAT to deliver, not HOW to code it
63
+ 3. Write TICKET-MAP.md with mapping table
64
+ 4. Update STATE.md: phase status → "synced"
65
+ 5. Log to PM-LOG.md
66
+
67
+ ### NEEDS_DISPATCH
68
+
69
+ **Goal:** Launch worker agents for the next ready wave.
70
+
71
+ Follow `pm-dispatch.md` workflow:
72
+ 1. Parse TICKET-MAP.md for wave structure
73
+ 2. Identify next undispatched wave where all prior waves are done
74
+ 3. For each `todo` ticket in target wave:
75
+ - Call `mcp__vibe_kanban__start_workspace_session`:
76
+ - task_id: from TICKET-MAP
77
+ - executor: from config (default: CLAUDE_CODE)
78
+ - repos: from config (repo_id + base_branch)
79
+ - Update TICKET-MAP: status → inprogress, dispatched timestamp
80
+ 4. Update STATE.md: phase status → "dispatched, wave {N}"
81
+ 5. Log to PM-LOG.md
82
+
83
+ ### MONITORING
84
+
85
+ **Goal:** Poll ticket status, react to changes.
86
+
87
+ Follow `pm-check.md` workflow:
88
+ 1. Call `mcp__vibe_kanban__list_tasks(project_id)`
89
+ 2. Diff current status against TICKET-MAP.md last-known status
90
+ 3. For each change:
91
+
92
+ | Event | Reaction |
93
+ |-------|----------|
94
+ | Ticket: todo → inprogress | Update TICKET-MAP (worker started) |
95
+ | Ticket: inprogress → done | Update TICKET-MAP, check wave completion |
96
+ | Ticket: inprogress → inreview | Update TICKET-MAP (awaiting review) |
97
+ | Ticket: any → cancelled | If auto_replan: run pm-replan (TARGETED). Else: log warning |
98
+ | Wave complete | If auto_dispatch_next_wave: dispatch next wave. Else: log |
99
+ | All tickets done | Mark as PHASE_COMPLETE (caught next cycle) |
100
+ | No changes | Log: "No changes. {N} inprogress, {M} todo." |
101
+
102
+ 4. Update TICKET-MAP.md + STATE.md + PM-LOG.md
103
+
104
+ **Stuck detection:** If a ticket has been `inprogress` for longer than 30 minutes (configurable), log a warning. After 60 minutes, consider re-dispatching with a different executor.
105
+
106
+ ### PHASE_COMPLETE
107
+
108
+ **Goal:** Advance to next phase in roadmap.
109
+
110
+ 1. Read ROADMAP.md
111
+ 2. Find next phase after current
112
+ 3. If next phase exists:
113
+ - Update STATE.md: current_phase → next phase, status → "pending"
114
+ - Log: "Phase {X} complete. Advancing to Phase {X+1}: {name}"
115
+ 4. If no next phase:
116
+ - This is MILESTONE_COMPLETE
117
+
118
+ ### MILESTONE_COMPLETE
119
+
120
+ **Goal:** Signal completion, stop loop.
121
+
122
+ 1. Log: "Milestone complete. All {N} phases done."
123
+ 2. Write `.planning/.pm-stop` file (signals pm-loop.sh to exit)
124
+ 3. Update STATE.md: milestone_status → "complete"
125
+ 4. Exit with code 42
126
+
127
+ ## PM-LOG Entry Format
128
+
129
+ Every action writes a timestamped entry:
130
+
131
+ ```markdown
132
+ ## [{YYYY-MM-DD HH:MM:SS}] {ACTION}
133
+
134
+ - {detail 1}
135
+ - {detail 2}
136
+ - State: {previous} → {new}
137
+ ```
138
+
139
+ ## Error Handling
140
+
141
+ - VK API errors: Log error, exit with code 1 (shell loop retries next cycle)
142
+ - Planner spawn failure: Log error, exit with code 1
143
+ - Dispatch failure (single ticket): Skip that ticket, continue with others, log
144
+ - All dispatches fail: Log error, exit with code 1
145
+ - State file missing: Log error, suggest `/gsd:pm-start`
@@ -87,7 +87,8 @@ Action: Cancel all undone tickets, re-plan from phase goal.
87
87
 
88
88
  6. Sync to Vibe Kanban:
89
89
  - `update_task(failed_ticket_id, status="cancelled")`
90
- - `create_task(project_id, fix_title, fix_plan_content)` new ticket
90
+ - Build Agile ticket from fix plan (Task, Why, Acceptance Criteria, Dependencies, Scope — no code specifics)
91
+ - `create_task(project_id, fix_title, agile_ticket_description)` → new ticket
91
92
  - Record in TICKET-MAP.md
92
93
 
93
94
  7. Dispatch fix ticket if autonomous mode
@@ -58,20 +58,63 @@ Build three lists:
58
58
 
59
59
  ## 3. Create Tickets for New Plans
60
60
 
61
+ **Ticket philosophy:** Tickets are Agile-style task assignments — atomic, isolated, and code-agnostic. They describe **what** to deliver and **why**, not **how** to implement it. The worker agent reads the codebase to figure out implementation. This makes tickets robust to codebase changes and readable by any stakeholder.
62
+
61
63
  For each new plan:
62
64
 
63
- 1. Read the full PLAN.md content
65
+ 1. Read the PLAN.md content
64
66
  2. Extract from frontmatter: `wave`, `depends_on`, `phase`, `plan`
65
- 3. Extract objective from the `<objective>` section (first line)
66
- 4. Build ticket:
67
- - **title:** `"Phase {phase} Plan {plan}: {objective_first_line}"`
68
- - **description:** Full PLAN.md content as-is (this is the worker's prompt)
69
- 5. Call `mcp__vibe_kanban__create_task(project_id, title, description)`
70
- 6. Record in TICKET-MAP.md:
67
+ 3. Extract from `<objective>` section: what this plan accomplishes and why
68
+ 4. Extract from `must_haves` frontmatter: truths (observable behaviors), artifacts (deliverables)
69
+ 5. Build an **Agile ticket** (NOT a code dump):
70
+
71
+ - **title:** `"Phase {phase} Plan {plan}: {objective_summary}"`
72
+ - **description:** Structured Agile ticket (see format below)
73
+
74
+ 6. Call `mcp__vibe_kanban__create_task(project_id, title, description)`
75
+ 7. Record in TICKET-MAP.md:
71
76
  ```
72
77
  | {plan} | {task_summary} | {ticket_uuid} | todo | — | {wave} | — | — | new |
73
78
  ```
74
79
 
80
+ ### Agile Ticket Format
81
+
82
+ ```markdown
83
+ ## Task
84
+
85
+ {One-paragraph summary of what to deliver. Written as a user story or task statement.
86
+ Focus on the deliverable, not implementation details. No file paths, no function names, no code snippets.}
87
+
88
+ ## Why
89
+
90
+ {Why this task matters for the project. What value it adds. What it unblocks.}
91
+
92
+ ## Acceptance Criteria
93
+
94
+ - [ ] {Observable behavior 1 — from must_haves.truths}
95
+ - [ ] {Observable behavior 2}
96
+ - [ ] {Deliverable exists — from must_haves.artifacts, described generically}
97
+
98
+ ## Dependencies
99
+
100
+ - {List ticket IDs this depends on, from depends_on field, or "None"}
101
+
102
+ ## Scope
103
+
104
+ - Wave: {N}
105
+ - Phase: {phase_name}
106
+ - Plan ref: {plan_id} (see .planning/ for implementation details)
107
+ ```
108
+
109
+ **What NOT to include in tickets:**
110
+ - File paths or directory structures
111
+ - Function/class/method names
112
+ - Code snippets or pseudocode
113
+ - Specific library API calls
114
+ - Internal architecture decisions
115
+
116
+ **Why:** Workers are coding agents with full codebase access. They figure out implementation from context. Tickets that prescribe code become stale, create merge conflicts, and prevent the agent from making better decisions based on actual codebase state.
117
+
75
118
  </step>
76
119
 
77
120
  <step name="sync_modified">
@@ -82,11 +125,13 @@ For each modified plan:
82
125
 
83
126
  1. Find ticket_id from TICKET-MAP.md
84
127
  2. Check current ticket status:
85
- - If `todo`: safe to update → `update_task(task_id, description=new_content)`
128
+ - If `todo`: safe to update → rebuild Agile ticket from updated PLAN.md, then `update_task(task_id, description=new_agile_ticket)`
86
129
  - If `inprogress`: **WARNING** — worker is running. Log warning, do NOT update.
87
130
  - If `done`/`cancelled`: skip (no point updating)
88
131
  3. Update TICKET-MAP.md with notes
89
132
 
133
+ **Important:** Always regenerate the Agile ticket format from the updated PLAN.md. Never dump raw PLAN.md content into the ticket description.
134
+
90
135
  </step>
91
136
 
92
137
  <step name="sync_removed">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-cook",
3
- "version": "1.10.1",
3
+ "version": "1.10.3",
4
4
  "description": "A meta-prompting, context engineering and spec-driven development system for Claude Code, OpenCode and Gemini by TÂCHES.",
5
5
  "bin": {
6
6
  "claude-cook": "bin/install.js"
@@ -1,20 +1,25 @@
1
1
  #!/bin/bash
2
- # pm-loop.sh — Autonomous PM monitor loop
2
+ # pm-loop.sh — Fully autonomous PM loop (Ralph-style)
3
3
  #
4
- # Runs an infinite loop that invokes claude CLI with /gsd:pm-check on each cycle.
4
+ # Runs an infinite loop. Each iteration invokes claude CLI with /gsd:pm-cycle
5
+ # the full-brain PM that reads state, decides what to do, acts, and exits.
5
6
  # Each invocation is a fresh 200k context — no context rot.
6
- # State persists via .planning/ files (TICKET-MAP.md, STATE.md, PM-LOG.md).
7
+ # State persists via .planning/ files.
8
+ #
9
+ # The PM brain handles the ENTIRE lifecycle autonomously:
10
+ # plan → sync tickets → dispatch workers → monitor → replan → advance phases → complete milestone
11
+ #
12
+ # RUNS IN FOREGROUND by default — you see live progress.
13
+ # Use --background to detach (output goes to .planning/pm-loop.log).
7
14
  #
8
15
  # Usage:
9
- # ./scripts/pm-loop.sh [--phase=N] [--interval=60]
16
+ # ./scripts/pm-loop.sh [--phase=N] [--interval=60] [--max-iterations=50]
10
17
  # PM_PHASE=1 PM_POLL_INTERVAL=30 ./scripts/pm-loop.sh
11
18
  #
12
19
  # Stop:
13
20
  # touch .planning/.pm-stop
14
21
  # OR run /gsd:pm-stop
15
- #
16
- # Background:
17
- # nohup ./scripts/pm-loop.sh --phase=1 > /dev/null 2>&1 &
22
+ # OR Ctrl+C (foreground mode)
18
23
 
19
24
  set -euo pipefail
20
25
 
@@ -22,6 +27,9 @@ set -euo pipefail
22
27
 
23
28
  PHASE="${PM_PHASE:-}"
24
29
  INTERVAL="${PM_POLL_INTERVAL:-60}"
30
+ MAX_ITERS="${PM_MAX_ITERATIONS:-0}" # 0 = unlimited
31
+ BACKGROUND=false
32
+ NOTIFY=true
25
33
 
26
34
  for arg in "$@"; do
27
35
  case $arg in
@@ -31,14 +39,37 @@ for arg in "$@"; do
31
39
  --interval=*)
32
40
  INTERVAL="${arg#*=}"
33
41
  ;;
42
+ --max-iterations=*)
43
+ MAX_ITERS="${arg#*=}"
44
+ ;;
45
+ --background)
46
+ BACKGROUND=true
47
+ ;;
48
+ --no-notify)
49
+ NOTIFY=false
50
+ ;;
34
51
  --help|-h)
35
- echo "Usage: pm-loop.sh [--phase=N] [--interval=60]"
52
+ echo "Usage: pm-loop.sh [--phase=N] [--interval=60] [--max-iterations=0]"
53
+ echo ""
54
+ echo "Fully autonomous PM loop. Each cycle invokes /gsd:pm-cycle which"
55
+ echo "reads state and decides what to do: plan, sync, dispatch, monitor,"
56
+ echo "replan, advance phases, or complete milestone."
57
+ echo ""
58
+ echo "Runs in FOREGROUND by default — you see live progress."
59
+ echo ""
60
+ echo "Options:"
61
+ echo " --phase=N Starting phase number (auto-detected if omitted)"
62
+ echo " --interval=N Seconds between cycles (default: 60)"
63
+ echo " --max-iterations=N Safety cap on cycles (default: 0 = unlimited)"
64
+ echo " --background Detach and run in background (logs to pm-loop.log)"
65
+ echo " --no-notify Disable desktop notifications"
36
66
  echo ""
37
67
  echo "Environment variables:"
38
- echo " PM_PHASE Phase number to monitor"
39
- echo " PM_POLL_INTERVAL Seconds between poll cycles (default: 60)"
68
+ echo " PM_PHASE Phase number"
69
+ echo " PM_POLL_INTERVAL Seconds between cycles (default: 60)"
70
+ echo " PM_MAX_ITERATIONS Max cycles (default: 0 = unlimited)"
40
71
  echo ""
41
- echo "Stop: touch .planning/.pm-stop"
72
+ echo "Stop: touch .planning/.pm-stop OR /gsd:pm-stop OR Ctrl+C"
42
73
  exit 0
43
74
  ;;
44
75
  *)
@@ -63,17 +94,117 @@ if ! command -v claude &> /dev/null; then
63
94
  exit 1
64
95
  fi
65
96
 
97
+ # ─── Background mode: re-exec detached ───────────────────────────
98
+
99
+ if [ "$BACKGROUND" = true ]; then
100
+ # Strip --background from args, re-exec with nohup
101
+ ARGS=()
102
+ for arg in "$@"; do
103
+ [ "$arg" != "--background" ] && ARGS+=("$arg")
104
+ done
105
+ nohup "$0" "${ARGS[@]}" > .planning/pm-loop.log 2>&1 &
106
+ BG_PID=$!
107
+ echo "$BG_PID" > .planning/.pm-loop-pid
108
+ echo "PM loop launched in background (PID: $BG_PID)"
109
+ echo "Log: tail -f .planning/pm-loop.log"
110
+ echo "Stop: touch .planning/.pm-stop"
111
+ exit 0
112
+ fi
113
+
114
+ # ─── Desktop notification helper ─────────────────────────────────
115
+
116
+ notify() {
117
+ local title="$1"
118
+ local message="$2"
119
+ local urgency="${3:-normal}" # normal, critical
120
+
121
+ if [ "$NOTIFY" = false ]; then
122
+ return
123
+ fi
124
+
125
+ # macOS
126
+ if command -v osascript &> /dev/null; then
127
+ osascript -e "display notification \"$message\" with title \"$title\"" 2>/dev/null || true
128
+ # Sound for critical events
129
+ if [ "$urgency" = "critical" ]; then
130
+ afplay /System/Library/Sounds/Glass.aiff 2>/dev/null &
131
+ fi
132
+ return
133
+ fi
134
+
135
+ # Linux
136
+ if command -v notify-send &> /dev/null; then
137
+ local urgency_flag="normal"
138
+ [ "$urgency" = "critical" ] && urgency_flag="critical"
139
+ notify-send -u "$urgency_flag" "$title" "$message" 2>/dev/null || true
140
+ return
141
+ fi
142
+ }
143
+
144
+ # ─── Progress summary helper ─────────────────────────────────────
145
+
146
+ print_progress() {
147
+ # Read TICKET-MAP.md for quick progress snapshot
148
+ local ticket_map=""
149
+ ticket_map=$(find .planning/phases/ -name "TICKET-MAP.md" -print -quit 2>/dev/null || true)
150
+
151
+ if [ -n "$ticket_map" ] && [ -f "$ticket_map" ]; then
152
+ local done_count=$(grep -c "| done " "$ticket_map" 2>/dev/null || echo "0")
153
+ local inprogress_count=$(grep -c "| inprogress " "$ticket_map" 2>/dev/null || echo "0")
154
+ local todo_count=$(grep -c "| todo " "$ticket_map" 2>/dev/null || echo "0")
155
+ local total=$((done_count + inprogress_count + todo_count))
156
+
157
+ if [ "$total" -gt 0 ]; then
158
+ # Build progress bar
159
+ local pct=0
160
+ if [ "$total" -gt 0 ]; then
161
+ pct=$((done_count * 100 / total))
162
+ fi
163
+ local bar_len=20
164
+ local filled=$((pct * bar_len / 100))
165
+ local empty=$((bar_len - filled))
166
+ local bar=""
167
+ for ((i=0; i<filled; i++)); do bar+="█"; done
168
+ for ((i=0; i<empty; i++)); do bar+="░"; done
169
+
170
+ echo " ┌─────────────────────────────────────────┐"
171
+ echo " │ Progress: [$bar] ${pct}%"
172
+ echo " │ Done: $done_count Running: $inprogress_count Todo: $todo_count Total: $total"
173
+ echo " └─────────────────────────────────────────┘"
174
+ fi
175
+ fi
176
+ }
177
+
178
+ # ─── Graceful Ctrl+C handling ────────────────────────────────────
179
+
180
+ cleanup() {
181
+ TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
182
+ echo ""
183
+ echo "[$TIMESTAMP] PM loop interrupted (Ctrl+C). Cleaning up..."
184
+ echo "" >> "$LOG_FILE"
185
+ echo "## [$TIMESTAMP] INTERRUPTED" >> "$LOG_FILE"
186
+ echo "" >> "$LOG_FILE"
187
+ echo "- PM loop interrupted by user" >> "$LOG_FILE"
188
+ echo "- Completed $CYCLE cycles" >> "$LOG_FILE"
189
+ rm -f "$PID_FILE"
190
+ notify "GSD PM" "PM loop stopped by user after $CYCLE cycles"
191
+ exit 0
192
+ }
193
+ trap cleanup SIGINT SIGTERM
194
+
66
195
  # ─── Configuration ────────────────────────────────────────────────
67
196
 
68
197
  STOP_FILE=".planning/.pm-stop"
69
198
  PID_FILE=".planning/.pm-loop-pid"
70
199
  LOG_FILE=".planning/PM-LOG.md"
71
200
  CYCLE=0
201
+ ERROR_STREAK=0
202
+ MAX_ERROR_STREAK=5
72
203
 
73
204
  # Clean any stale stop signal
74
205
  rm -f "$STOP_FILE"
75
206
 
76
- # Record PID for /pm:stop
207
+ # Record PID for /gsd:pm-stop
77
208
  echo $$ > "$PID_FILE"
78
209
 
79
210
  # ─── Init log ─────────────────────────────────────────────────────
@@ -88,6 +219,7 @@ Started: $TIMESTAMP
88
219
  Mode: autonomous
89
220
  Poll interval: ${INTERVAL}s
90
221
  Phase: ${PHASE:-auto}
222
+ Max iterations: ${MAX_ITERS:-unlimited}
91
223
 
92
224
  ---
93
225
 
@@ -100,56 +232,176 @@ echo "" >> "$LOG_FILE"
100
232
  echo "- PM loop started (PID: $$)" >> "$LOG_FILE"
101
233
  echo "- Phase: ${PHASE:-auto-detect}" >> "$LOG_FILE"
102
234
  echo "- Interval: ${INTERVAL}s" >> "$LOG_FILE"
235
+ echo "- Max iterations: ${MAX_ITERS:-unlimited}" >> "$LOG_FILE"
103
236
 
104
- # ─── Main loop ────────────────────────────────────────────────────
237
+ # ─── Startup banner ──────────────────────────────────────────────
105
238
 
106
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
107
- echo " PM Loop — Phase: ${PHASE:-auto} | Interval: ${INTERVAL}s"
108
- echo " PID: $$ | Stop: touch $STOP_FILE"
109
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
110
239
  echo ""
240
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
241
+ echo " PM ► AUTONOMOUS MODE ACTIVE"
242
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
243
+ echo ""
244
+ echo " Phase: ${PHASE:-auto-detect}"
245
+ echo " Interval: ${INTERVAL}s"
246
+ echo " PID: $$"
247
+ if [ "$MAX_ITERS" -gt 0 ] 2>/dev/null; then
248
+ echo " Max iterations: $MAX_ITERS"
249
+ fi
250
+ echo ""
251
+ echo " Stop: Ctrl+C | touch .planning/.pm-stop | /gsd:pm-stop"
252
+ echo ""
253
+ echo " The PM will autonomously:"
254
+ echo " plan → sync → dispatch → monitor → replan → advance phases"
255
+ echo ""
256
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
257
+ echo ""
258
+
259
+ notify "GSD PM" "Autonomous PM started for Phase ${PHASE:-auto}" "normal"
260
+
261
+ # ─── Main loop ────────────────────────────────────────────────────
111
262
 
112
263
  while true; do
113
264
  # ── Check stop signal ──
114
265
  if [ -f "$STOP_FILE" ]; then
115
266
  TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
116
- echo "[$TIMESTAMP] Stop signal received. Exiting."
267
+ echo ""
268
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
269
+ echo " PM ► STOPPED"
270
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
271
+ echo ""
272
+ echo " [$TIMESTAMP] Stop signal received after $CYCLE cycles."
273
+ echo ""
117
274
  echo "" >> "$LOG_FILE"
118
275
  echo "## [$TIMESTAMP] STOP" >> "$LOG_FILE"
119
276
  echo "" >> "$LOG_FILE"
120
277
  echo "- PM loop stopped by stop file" >> "$LOG_FILE"
121
278
  echo "- Completed $CYCLE cycles" >> "$LOG_FILE"
122
279
  rm -f "$STOP_FILE" "$PID_FILE"
280
+ notify "GSD PM" "PM loop stopped after $CYCLE cycles"
123
281
  exit 0
124
282
  fi
125
283
 
126
- # ── Run one check cycle ──
284
+ # ── Max iterations guard ──
127
285
  CYCLE=$((CYCLE + 1))
286
+ if [ "$MAX_ITERS" -gt 0 ] 2>/dev/null && [ "$CYCLE" -gt "$MAX_ITERS" ]; then
287
+ TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
288
+ echo ""
289
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
290
+ echo " PM ► MAX ITERATIONS REACHED"
291
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
292
+ echo ""
293
+ echo " Completed $((CYCLE - 1)) cycles. Safety cap: $MAX_ITERS."
294
+ echo ""
295
+ echo "" >> "$LOG_FILE"
296
+ echo "## [$TIMESTAMP] MAX_ITERATIONS" >> "$LOG_FILE"
297
+ echo "" >> "$LOG_FILE"
298
+ echo "- Reached max iterations: $MAX_ITERS" >> "$LOG_FILE"
299
+ rm -f "$PID_FILE"
300
+ notify "GSD PM" "PM loop reached max iterations ($MAX_ITERS)" "normal"
301
+ exit 0
302
+ fi
303
+
304
+ # ── Cycle header ──
128
305
  TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
129
- echo "[$TIMESTAMP] Cycle #$CYCLE — polling..."
306
+ echo "┌─────────────────────────────────────────────────────"
307
+ echo "│ Cycle #$CYCLE — $TIMESTAMP"
308
+ echo "└─────────────────────────────────────────────────────"
309
+ echo ""
130
310
 
131
- # Fresh Claude invocation each cycle = no context rot
311
+ # Build the prompt
132
312
  if [ -n "$PHASE" ]; then
133
- claude --print --dangerously-skip-permissions --command "/gsd:pm-check $PHASE" 2>&1 || {
134
- echo "[$TIMESTAMP] WARNING: claude exited with error (cycle #$CYCLE)"
313
+ PROMPT="/gsd:pm-cycle $PHASE"
314
+ else
315
+ PROMPT="/gsd:pm-cycle"
316
+ fi
317
+
318
+ # Fresh Claude invocation — full brain, fresh 200k context
319
+ # Output streams directly to terminal (foreground mode)
320
+ CYCLE_START=$(date +%s)
321
+ claude -p --dangerously-skip-permissions "$PROMPT" 2>&1
322
+ EXIT_CODE=$?
323
+ CYCLE_END=$(date +%s)
324
+ CYCLE_DURATION=$((CYCLE_END - CYCLE_START))
325
+
326
+ echo ""
327
+
328
+ # ── Handle exit codes ──
329
+ case $EXIT_CODE in
330
+ 0)
331
+ # Normal — more work to do
332
+ ERROR_STREAK=0
333
+ print_progress
334
+ echo ""
335
+ echo " Cycle #$CYCLE completed in ${CYCLE_DURATION}s. Next in ${INTERVAL}s..."
336
+ echo ""
337
+ ;;
338
+ 42)
339
+ # Milestone complete — all done!
340
+ TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
341
+ echo ""
342
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
343
+ echo " PM ► MILESTONE COMPLETE"
344
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
345
+ echo ""
346
+ echo " All phases complete after $CYCLE cycles."
347
+ echo " Run /gsd:complete-milestone to archive."
348
+ echo ""
349
+ print_progress
350
+ echo ""
135
351
  echo "" >> "$LOG_FILE"
136
- echo "## [$TIMESTAMP] ERROR" >> "$LOG_FILE"
352
+ echo "## [$TIMESTAMP] LOOP_EXIT" >> "$LOG_FILE"
137
353
  echo "" >> "$LOG_FILE"
138
- echo "- Claude exited with error on cycle #$CYCLE" >> "$LOG_FILE"
139
- }
140
- else
141
- claude --print --dangerously-skip-permissions --command "/gsd:pm-check" 2>&1 || {
142
- echo "[$TIMESTAMP] WARNING: claude exited with error (cycle #$CYCLE)"
354
+ echo "- Milestone complete after $CYCLE cycles" >> "$LOG_FILE"
355
+ rm -f "$PID_FILE"
356
+ notify "GSD PM" "Milestone complete! All phases done after $CYCLE cycles." "critical"
357
+ exit 0
358
+ ;;
359
+ *)
360
+ # Error — log and continue (don't crash the loop)
361
+ ERROR_STREAK=$((ERROR_STREAK + 1))
362
+ TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
363
+ echo " ⚠ Cycle #$CYCLE failed (exit code $EXIT_CODE, streak: $ERROR_STREAK/$MAX_ERROR_STREAK)"
143
364
  echo "" >> "$LOG_FILE"
144
365
  echo "## [$TIMESTAMP] ERROR" >> "$LOG_FILE"
145
366
  echo "" >> "$LOG_FILE"
146
- echo "- Claude exited with error on cycle #$CYCLE" >> "$LOG_FILE"
147
- }
148
- fi
367
+ echo "- Claude exited with code $EXIT_CODE on cycle #$CYCLE" >> "$LOG_FILE"
368
+ echo "- Error streak: $ERROR_STREAK/$MAX_ERROR_STREAK" >> "$LOG_FILE"
149
369
 
150
- echo ""
370
+ # Notify on errors
371
+ notify "GSD PM" "Cycle #$CYCLE failed (code $EXIT_CODE)" "critical"
372
+
373
+ # Stop if too many consecutive errors
374
+ if [ "$ERROR_STREAK" -ge "$MAX_ERROR_STREAK" ]; then
375
+ echo ""
376
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
377
+ echo " PM ► ERROR — TOO MANY FAILURES"
378
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
379
+ echo ""
380
+ echo " $ERROR_STREAK consecutive errors. Stopping to prevent waste."
381
+ echo " Check .planning/PM-LOG.md for details."
382
+ echo " Fix the issue, then restart with /gsd:pm-start"
383
+ echo ""
384
+ echo "" >> "$LOG_FILE"
385
+ echo "## [$TIMESTAMP] ERROR_STREAK_HALT" >> "$LOG_FILE"
386
+ echo "" >> "$LOG_FILE"
387
+ echo "- Halted after $ERROR_STREAK consecutive errors" >> "$LOG_FILE"
388
+ rm -f "$PID_FILE"
389
+ notify "GSD PM" "PM halted: $ERROR_STREAK consecutive errors" "critical"
390
+ exit 1
391
+ fi
392
+ ;;
393
+ esac
151
394
 
152
- # ── Sleep until next cycle ──
153
- echo "[$TIMESTAMP] Next check in ${INTERVAL}s..."
154
- sleep "$INTERVAL"
395
+ # ── Countdown to next cycle ──
396
+ for ((remaining=INTERVAL; remaining>0; remaining--)); do
397
+ # Check stop signal during sleep too
398
+ if [ -f "$STOP_FILE" ]; then
399
+ break
400
+ fi
401
+ # Show countdown every 15 seconds
402
+ if [ $((remaining % 15)) -eq 0 ] && [ "$remaining" -ne "$INTERVAL" ]; then
403
+ echo " ... next cycle in ${remaining}s"
404
+ fi
405
+ sleep 1
406
+ done
155
407
  done