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 +54 -18
- package/commands/gsd/help.md +13 -12
- package/commands/gsd/pm-cycle.md +296 -0
- package/commands/gsd/pm-start.md +58 -32
- package/get-shit-done/workflows/pm-cycle.md +145 -0
- package/get-shit-done/workflows/pm-replan.md +2 -1
- package/get-shit-done/workflows/pm-sync.md +53 -8
- package/package.json +1 -1
- package/scripts/pm-loop.sh +288 -36
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
|
|
42
|
+
You think in terms of:
|
|
34
43
|
- Goals, not implementations
|
|
35
|
-
-
|
|
44
|
+
- Deliverables, not code
|
|
36
45
|
- Delegation, not execution
|
|
37
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
268
|
-
- Update changed tickets: `update_task(task_id, description=
|
|
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.
|
|
287
|
-
4. Build ticket
|
|
288
|
-
5.
|
|
289
|
-
|
|
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=
|
|
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
|
package/commands/gsd/help.md
CHANGED
|
@@ -424,16 +424,17 @@ Example config:
|
|
|
424
424
|
### Product Manager Mode
|
|
425
425
|
|
|
426
426
|
**`/gsd:pm-start <phase>`**
|
|
427
|
-
|
|
427
|
+
Start fully autonomous PM — plans, syncs, dispatches, monitors, and advances phases.
|
|
428
428
|
|
|
429
|
-
-
|
|
430
|
-
-
|
|
431
|
-
-
|
|
432
|
-
- `--
|
|
433
|
-
- `--manual`:
|
|
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
|
|
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
|
|
524
|
-
#
|
|
525
|
-
|
|
526
|
-
/gsd:pm-
|
|
527
|
-
/gsd:pm-
|
|
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>
|
package/commands/gsd/pm-start.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: gsd:pm-start
|
|
3
|
-
description:
|
|
4
|
-
argument-hint: "<phase> [--
|
|
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
|
-
|
|
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
|
-
**
|
|
29
|
+
**plan → sync tickets → dispatch workers → monitor → replan on failure → advance phases → complete milestone**
|
|
30
30
|
|
|
31
|
-
|
|
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 —
|
|
37
|
+
Phase number: $ARGUMENTS (required — starting phase)
|
|
36
38
|
|
|
37
39
|
**Flags:**
|
|
38
|
-
- `--
|
|
39
|
-
- `--
|
|
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
|
|
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
|
|
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:
|
|
132
|
-
-
|
|
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.
|
|
177
|
+
## 7. Launch PM Loop (default: foreground, live output)
|
|
172
178
|
|
|
173
|
-
### Autonomous Mode (
|
|
179
|
+
### Autonomous Mode (default) — FOREGROUND
|
|
174
180
|
|
|
175
|
-
|
|
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
|
-
|
|
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 ►
|
|
187
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
213
|
+
PM ► BACKGROUND MODE
|
|
188
214
|
|
|
189
215
|
PM loop running (PID: {pid})
|
|
190
|
-
|
|
191
|
-
|
|
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 (
|
|
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
|
|
228
|
+
PM ► MANUAL MODE
|
|
202
229
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
203
230
|
|
|
204
|
-
Workers dispatched.
|
|
231
|
+
Workers dispatched. Run these commands to manage:
|
|
205
232
|
|
|
206
|
-
/gsd:pm-
|
|
233
|
+
/gsd:pm-cycle — Run one full-brain cycle (decides + acts)
|
|
207
234
|
/gsd:pm-status — View dashboard
|
|
208
|
-
/gsd:pm-replan — Modify plans
|
|
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
|
-
-
|
|
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
|
|
65
|
+
1. Read the PLAN.md content
|
|
64
66
|
2. Extract from frontmatter: `wave`, `depends_on`, `phase`, `plan`
|
|
65
|
-
3. Extract
|
|
66
|
-
4.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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=
|
|
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
package/scripts/pm-loop.sh
CHANGED
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# pm-loop.sh —
|
|
2
|
+
# pm-loop.sh — Fully autonomous PM loop (Ralph-style)
|
|
3
3
|
#
|
|
4
|
-
# Runs an infinite loop
|
|
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
|
|
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
|
|
39
|
-
echo " PM_POLL_INTERVAL
|
|
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
|
|
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
|
-
# ───
|
|
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 "
|
|
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
|
-
# ──
|
|
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 "
|
|
306
|
+
echo "┌─────────────────────────────────────────────────────"
|
|
307
|
+
echo "│ Cycle #$CYCLE — $TIMESTAMP"
|
|
308
|
+
echo "└─────────────────────────────────────────────────────"
|
|
309
|
+
echo ""
|
|
130
310
|
|
|
131
|
-
#
|
|
311
|
+
# Build the prompt
|
|
132
312
|
if [ -n "$PHASE" ]; then
|
|
133
|
-
|
|
134
|
-
|
|
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]
|
|
352
|
+
echo "## [$TIMESTAMP] LOOP_EXIT" >> "$LOG_FILE"
|
|
137
353
|
echo "" >> "$LOG_FILE"
|
|
138
|
-
echo "-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
# ──
|
|
153
|
-
|
|
154
|
-
|
|
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
|