claude-cook 1.10.2 → 1.10.4

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:**
@@ -125,12 +125,14 @@ Log to PM-LOG.md:
125
125
 
126
126
  ### Action: NEEDS_SYNC
127
127
 
128
- Run pm-sync workflow:
128
+ Run pm-sync workflow — create **Agile-style tickets** (NOT code dumps):
129
129
  1. Read all PLAN.md files in phase directory
130
- 2. For each plan, call `mcp__vibe_kanban__create_task` with:
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:
131
133
  - Title: "Phase {X} Plan {Y}: {objective}"
132
- - Description: Full PLAN.md content (this IS the worker's prompt)
133
- 3. Write TICKET-MAP.md with all mappings
134
+ - Description: Structured Agile ticket atomic, isolated, no file paths or code specifics
135
+ 5. Write TICKET-MAP.md with all mappings
134
136
 
135
137
  Update STATE.md: set phase status to "synced".
136
138
 
@@ -263,13 +265,22 @@ Run /gsd:complete-milestone to archive and plan next.
263
265
 
264
266
  ## 4. Cycle Summary
265
267
 
266
- After executing the action, output a brief cycle summary:
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.
267
269
 
268
270
  ```
269
- [{TIMESTAMP}] Cycle complete — State: {STATE} → Action: {ACTION}
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}
270
281
  ```
271
282
 
272
- This is the only user-visible output per cycle (pm-loop.sh captures it).
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.
273
284
 
274
285
  </process>
275
286
 
@@ -281,4 +292,5 @@ This is the only user-visible output per cycle (pm-loop.sh captures it).
281
292
  - **Log everything.** Every action gets a PM-LOG.md entry with timestamp.
282
293
  - **Respect wave discipline.** Never dispatch wave N+1 until all wave N tickets are done.
283
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.
284
296
  </constraints>
@@ -38,9 +38,11 @@ Phase number: $ARGUMENTS (required — starting phase)
38
38
 
39
39
  **Flags:**
40
40
  - `--manual` — Don't launch the loop, manage manually with `/gsd:pm-cycle`
41
+ - `--background` — Run loop detached (default: foreground with live output)
41
42
  - `--executor=X` — Override default executor (CLAUDE_CODE, CURSOR_AGENT, CODEX, etc.)
42
43
  - `--skip-plan` — Skip initial planning, assume plans already exist
43
44
  - `--max-iterations=N` — Safety cap on loop cycles (default: 0 = unlimited)
45
+ - `--no-notify` — Disable desktop notifications
44
46
  </context>
45
47
 
46
48
  <process>
@@ -123,15 +125,17 @@ If no plans exist OR user wants to re-plan:
123
125
 
124
126
  **Key difference from /gsd:plan-phase:** After planning, continue to sync+dispatch instead of stopping.
125
127
 
126
- ## 5. Sync Plans to Vibe Kanban Tickets
128
+ ## 5. Sync Plans to Agile Tickets
127
129
 
128
- Follow the pm-sync workflow:
130
+ Follow the pm-sync workflow — create **Agile-style tickets**, not code dumps:
129
131
 
130
132
  1. Read all PLAN.md files in the phase directory
131
- 2. For each plan:
133
+ 2. For each plan, build an Agile ticket:
134
+ - Extract: objective, acceptance criteria (must_haves), wave, dependencies
132
135
  - Build title: `"Phase {X} Plan {Y}: {objective}"`
133
- - Build description: full PLAN.md content
134
- - `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)`
135
139
  3. Write TICKET-MAP.md with all mappings
136
140
 
137
141
  Present:
@@ -170,33 +174,48 @@ Present:
170
174
  | ... | ... | ... | launched |
171
175
  ```
172
176
 
173
- ## 7. Launch PM Loop (default: autonomous)
177
+ ## 7. Launch PM Loop (default: foreground, live output)
174
178
 
175
- ### Autonomous Mode (default)
179
+ ### Autonomous Mode (default) — FOREGROUND
176
180
 
177
- Launch pm-loop.sh — the Ralph-style infinite loop that calls `/gsd:pm-cycle` each iteration. The brain handles everything from here: monitoring, wave advancement, replanning, phase progression.
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.
178
182
 
179
183
  ```bash
180
- nohup ~/.claude/scripts/pm-loop.sh --phase={X} --interval={poll_interval} --max-iterations={max_iterations} > .planning/pm-loop.log 2>&1 &
181
- echo $! > .planning/.pm-loop-pid
184
+ ~/.claude/scripts/pm-loop.sh --phase={X} --interval={poll_interval} --max-iterations={max_iterations}
182
185
  ```
183
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
+
184
210
  Present:
185
211
  ```
186
212
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
187
- PM ► AUTONOMOUS MODE ACTIVE
188
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
213
+ PM ► BACKGROUND MODE
189
214
 
190
215
  PM loop running (PID: {pid})
191
- Brain: /gsd:pm-cycle (full lifecycle per iteration)
192
- Polling every {interval}s
193
- Log: .planning/PM-LOG.md
194
-
195
- The PM will autonomously:
196
- plan → sync → dispatch → monitor → replan → advance phases
197
-
198
- Stop with: /gsd:pm-stop or touch .planning/.pm-stop
199
- Status: /gsd:pm-status
216
+ Log: tail -f .planning/pm-loop.log
217
+ Stop: /gsd:pm-stop or touch .planning/.pm-stop
218
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
200
219
  ```
201
220
 
202
221
  ### Manual Mode (--manual)
@@ -51,14 +51,15 @@ MILESTONE_COMPLETE ──stop signal──→ EXIT
51
51
 
52
52
  ### NEEDS_SYNC
53
53
 
54
- **Goal:** Create Vibe Kanban tickets for each plan.
54
+ **Goal:** Create Agile-style Vibe Kanban tickets for each plan.
55
55
 
56
56
  Follow `pm-sync.md` workflow:
57
57
  1. Read all PLAN.md files in phase directory
58
- 2. For each plan:
59
- - Extract objective, wave number from frontmatter
58
+ 2. For each plan, build an **Agile ticket** (atomic, isolated, code-agnostic):
59
+ - Extract objective, acceptance criteria (must_haves), wave, dependencies
60
60
  - Create VK ticket: `mcp__vibe_kanban__create_task(project_id, title, description)`
61
- - Description = full PLAN.md content (worker's complete prompt)
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
62
63
  3. Write TICKET-MAP.md with mapping table
63
64
  4. Update STATE.md: phase status → "synced"
64
65
  5. Log to PM-LOG.md
@@ -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.2",
3
+ "version": "1.10.4",
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"
@@ -9,6 +9,9 @@
9
9
  # The PM brain handles the ENTIRE lifecycle autonomously:
10
10
  # plan → sync tickets → dispatch workers → monitor → replan → advance phases → complete milestone
11
11
  #
12
+ # RUNS IN FOREGROUND by default — you see live progress.
13
+ # Use --background to detach (output goes to .planning/pm-loop.log).
14
+ #
12
15
  # Usage:
13
16
  # ./scripts/pm-loop.sh [--phase=N] [--interval=60] [--max-iterations=50]
14
17
  # PM_PHASE=1 PM_POLL_INTERVAL=30 ./scripts/pm-loop.sh
@@ -16,9 +19,7 @@
16
19
  # Stop:
17
20
  # touch .planning/.pm-stop
18
21
  # OR run /gsd:pm-stop
19
- #
20
- # Background:
21
- # nohup ./scripts/pm-loop.sh --phase=1 > .planning/pm-loop.log 2>&1 &
22
+ # OR Ctrl+C (foreground mode)
22
23
 
23
24
  set -euo pipefail
24
25
 
@@ -27,6 +28,8 @@ set -euo pipefail
27
28
  PHASE="${PM_PHASE:-}"
28
29
  INTERVAL="${PM_POLL_INTERVAL:-60}"
29
30
  MAX_ITERS="${PM_MAX_ITERATIONS:-0}" # 0 = unlimited
31
+ BACKGROUND=false
32
+ NOTIFY=true
30
33
 
31
34
  for arg in "$@"; do
32
35
  case $arg in
@@ -39,6 +42,12 @@ for arg in "$@"; do
39
42
  --max-iterations=*)
40
43
  MAX_ITERS="${arg#*=}"
41
44
  ;;
45
+ --background)
46
+ BACKGROUND=true
47
+ ;;
48
+ --no-notify)
49
+ NOTIFY=false
50
+ ;;
42
51
  --help|-h)
43
52
  echo "Usage: pm-loop.sh [--phase=N] [--interval=60] [--max-iterations=0]"
44
53
  echo ""
@@ -46,17 +55,21 @@ for arg in "$@"; do
46
55
  echo "reads state and decides what to do: plan, sync, dispatch, monitor,"
47
56
  echo "replan, advance phases, or complete milestone."
48
57
  echo ""
58
+ echo "Runs in FOREGROUND by default — you see live progress."
59
+ echo ""
49
60
  echo "Options:"
50
61
  echo " --phase=N Starting phase number (auto-detected if omitted)"
51
62
  echo " --interval=N Seconds between cycles (default: 60)"
52
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"
53
66
  echo ""
54
67
  echo "Environment variables:"
55
68
  echo " PM_PHASE Phase number"
56
69
  echo " PM_POLL_INTERVAL Seconds between cycles (default: 60)"
57
70
  echo " PM_MAX_ITERATIONS Max cycles (default: 0 = unlimited)"
58
71
  echo ""
59
- echo "Stop: touch .planning/.pm-stop OR /gsd:pm-stop"
72
+ echo "Stop: touch .planning/.pm-stop OR /gsd:pm-stop OR Ctrl+C"
60
73
  exit 0
61
74
  ;;
62
75
  *)
@@ -81,12 +94,112 @@ if ! command -v claude &> /dev/null; then
81
94
  exit 1
82
95
  fi
83
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
+
84
195
  # ─── Configuration ────────────────────────────────────────────────
85
196
 
86
197
  STOP_FILE=".planning/.pm-stop"
87
198
  PID_FILE=".planning/.pm-loop-pid"
88
199
  LOG_FILE=".planning/PM-LOG.md"
89
200
  CYCLE=0
201
+ ERROR_STREAK=0
202
+ MAX_ERROR_STREAK=5
90
203
 
91
204
  # Clean any stale stop signal
92
205
  rm -f "$STOP_FILE"
@@ -121,28 +234,50 @@ echo "- Phase: ${PHASE:-auto-detect}" >> "$LOG_FILE"
121
234
  echo "- Interval: ${INTERVAL}s" >> "$LOG_FILE"
122
235
  echo "- Max iterations: ${MAX_ITERS:-unlimited}" >> "$LOG_FILE"
123
236
 
124
- # ─── Main loop ────────────────────────────────────────────────────
237
+ # ─── Startup banner ──────────────────────────────────────────────
125
238
 
126
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
127
- echo " PM Loop — Phase: ${PHASE:-auto} | Interval: ${INTERVAL}s"
128
- echo " PID: $$ | Stop: touch $STOP_FILE"
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: $$"
129
247
  if [ "$MAX_ITERS" -gt 0 ] 2>/dev/null; then
130
- echo " Max iterations: $MAX_ITERS"
248
+ echo " Max iterations: $MAX_ITERS"
131
249
  fi
132
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
133
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 ────────────────────────────────────────────────────
134
262
 
135
263
  while true; do
136
264
  # ── Check stop signal ──
137
265
  if [ -f "$STOP_FILE" ]; then
138
266
  TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
139
- 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 ""
140
274
  echo "" >> "$LOG_FILE"
141
275
  echo "## [$TIMESTAMP] STOP" >> "$LOG_FILE"
142
276
  echo "" >> "$LOG_FILE"
143
277
  echo "- PM loop stopped by stop file" >> "$LOG_FILE"
144
278
  echo "- Completed $CYCLE cycles" >> "$LOG_FILE"
145
279
  rm -f "$STOP_FILE" "$PID_FILE"
280
+ notify "GSD PM" "PM loop stopped after $CYCLE cycles"
146
281
  exit 0
147
282
  fi
148
283
 
@@ -150,62 +285,125 @@ while true; do
150
285
  CYCLE=$((CYCLE + 1))
151
286
  if [ "$MAX_ITERS" -gt 0 ] 2>/dev/null && [ "$CYCLE" -gt "$MAX_ITERS" ]; then
152
287
  TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
153
- echo "[$TIMESTAMP] Max iterations ($MAX_ITERS) reached. Stopping."
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 ""
154
295
  echo "" >> "$LOG_FILE"
155
296
  echo "## [$TIMESTAMP] MAX_ITERATIONS" >> "$LOG_FILE"
156
297
  echo "" >> "$LOG_FILE"
157
298
  echo "- Reached max iterations: $MAX_ITERS" >> "$LOG_FILE"
158
299
  rm -f "$PID_FILE"
300
+ notify "GSD PM" "PM loop reached max iterations ($MAX_ITERS)" "normal"
159
301
  exit 0
160
302
  fi
161
303
 
162
- # ── Run one brain cycle ──
304
+ # ── Cycle header ──
163
305
  TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
164
- echo "[$TIMESTAMP] Cycle #$CYCLE — brain thinking..."
306
+ echo "┌─────────────────────────────────────────────────────"
307
+ echo "│ Cycle #$CYCLE — $TIMESTAMP"
308
+ echo "└─────────────────────────────────────────────────────"
309
+ echo ""
165
310
 
166
311
  # Build the prompt
167
- if [ -n "$PHASE" ]; then
312
+ # Only pass phase hint on cycle 1 — after that, pm-cycle auto-detects
313
+ # from STATE.md so it can advance through phases autonomously.
314
+ if [ "$CYCLE" -eq 1 ] && [ -n "$PHASE" ]; then
168
315
  PROMPT="/gsd:pm-cycle $PHASE"
169
316
  else
170
317
  PROMPT="/gsd:pm-cycle"
171
318
  fi
172
319
 
173
320
  # Fresh Claude invocation — full brain, fresh 200k context
321
+ # Output streams directly to terminal (foreground mode)
322
+ CYCLE_START=$(date +%s)
174
323
  claude -p --dangerously-skip-permissions "$PROMPT" 2>&1
175
324
  EXIT_CODE=$?
325
+ CYCLE_END=$(date +%s)
326
+ CYCLE_DURATION=$((CYCLE_END - CYCLE_START))
327
+
328
+ echo ""
176
329
 
177
330
  # ── Handle exit codes ──
178
331
  case $EXIT_CODE in
179
332
  0)
180
- # Normal — more work to do, continue loop
333
+ # Normal — more work to do
334
+ ERROR_STREAK=0
335
+ print_progress
336
+ echo ""
337
+ echo " Cycle #$CYCLE completed in ${CYCLE_DURATION}s. Next in ${INTERVAL}s..."
338
+ echo ""
181
339
  ;;
182
340
  42)
183
- # Milestone complete — all done
341
+ # Milestone complete — all done!
184
342
  TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
185
343
  echo ""
186
- echo "[$TIMESTAMP] Milestone complete! PM loop exiting."
344
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
345
+ echo " PM ► MILESTONE COMPLETE"
346
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
347
+ echo ""
348
+ echo " All phases complete after $CYCLE cycles."
349
+ echo " Run /gsd:complete-milestone to archive."
350
+ echo ""
351
+ print_progress
352
+ echo ""
187
353
  echo "" >> "$LOG_FILE"
188
354
  echo "## [$TIMESTAMP] LOOP_EXIT" >> "$LOG_FILE"
189
355
  echo "" >> "$LOG_FILE"
190
356
  echo "- Milestone complete after $CYCLE cycles" >> "$LOG_FILE"
191
357
  rm -f "$PID_FILE"
358
+ notify "GSD PM" "Milestone complete! All phases done after $CYCLE cycles." "critical"
192
359
  exit 0
193
360
  ;;
194
361
  *)
195
362
  # Error — log and continue (don't crash the loop)
363
+ ERROR_STREAK=$((ERROR_STREAK + 1))
196
364
  TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
197
- echo "[$TIMESTAMP] WARNING: claude exited with code $EXIT_CODE (cycle #$CYCLE)"
365
+ echo " Cycle #$CYCLE failed (exit code $EXIT_CODE, streak: $ERROR_STREAK/$MAX_ERROR_STREAK)"
198
366
  echo "" >> "$LOG_FILE"
199
367
  echo "## [$TIMESTAMP] ERROR" >> "$LOG_FILE"
200
368
  echo "" >> "$LOG_FILE"
201
369
  echo "- Claude exited with code $EXIT_CODE on cycle #$CYCLE" >> "$LOG_FILE"
370
+ echo "- Error streak: $ERROR_STREAK/$MAX_ERROR_STREAK" >> "$LOG_FILE"
371
+
372
+ # Notify on errors
373
+ notify "GSD PM" "Cycle #$CYCLE failed (code $EXIT_CODE)" "critical"
374
+
375
+ # Stop if too many consecutive errors
376
+ if [ "$ERROR_STREAK" -ge "$MAX_ERROR_STREAK" ]; then
377
+ echo ""
378
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
379
+ echo " PM ► ERROR — TOO MANY FAILURES"
380
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
381
+ echo ""
382
+ echo " $ERROR_STREAK consecutive errors. Stopping to prevent waste."
383
+ echo " Check .planning/PM-LOG.md for details."
384
+ echo " Fix the issue, then restart with /gsd:pm-start"
385
+ echo ""
386
+ echo "" >> "$LOG_FILE"
387
+ echo "## [$TIMESTAMP] ERROR_STREAK_HALT" >> "$LOG_FILE"
388
+ echo "" >> "$LOG_FILE"
389
+ echo "- Halted after $ERROR_STREAK consecutive errors" >> "$LOG_FILE"
390
+ rm -f "$PID_FILE"
391
+ notify "GSD PM" "PM halted: $ERROR_STREAK consecutive errors" "critical"
392
+ exit 1
393
+ fi
202
394
  ;;
203
395
  esac
204
396
 
205
- echo ""
206
-
207
- # ── Sleep until next cycle ──
208
- TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
209
- echo "[$TIMESTAMP] Next cycle in ${INTERVAL}s..."
210
- sleep "$INTERVAL"
397
+ # ── Countdown to next cycle ──
398
+ for ((remaining=INTERVAL; remaining>0; remaining--)); do
399
+ # Check stop signal during sleep too
400
+ if [ -f "$STOP_FILE" ]; then
401
+ break
402
+ fi
403
+ # Show countdown every 15 seconds
404
+ if [ $((remaining % 15)) -eq 0 ] && [ "$remaining" -ne "$INTERVAL" ]; then
405
+ echo " ... next cycle in ${remaining}s"
406
+ fi
407
+ sleep 1
408
+ done
211
409
  done