get-shit-done-cc 1.5.28 → 1.5.29

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.
@@ -244,8 +244,9 @@ issue:
244
244
  Gather verification context from the phase directory and project state.
245
245
 
246
246
  ```bash
247
- # Phase directory (provided in prompt)
248
- PHASE_DIR=".planning/phases/${PHASE_ARG}"
247
+ # Normalize phase and find directory
248
+ PADDED_PHASE=$(printf "%02d" ${PHASE_ARG} 2>/dev/null || echo "${PHASE_ARG}")
249
+ PHASE_DIR=$(ls -d .planning/phases/${PADDED_PHASE}-* .planning/phases/${PHASE_ARG}-* 2>/dev/null | head -1)
249
250
 
250
251
  # List all PLAN.md files
251
252
  ls "$PHASE_DIR"/*-PLAN.md 2>/dev/null
package/bin/install.js CHANGED
@@ -55,8 +55,6 @@ function parseConfigDirArg() {
55
55
  const explicitConfigDir = parseConfigDirArg();
56
56
  const hasHelp = args.includes('--help') || args.includes('-h');
57
57
  const forceStatusline = args.includes('--force-statusline');
58
- const forceNotify = args.includes('--force-notify');
59
- const noNotify = args.includes('--no-notify');
60
58
 
61
59
  console.log(banner);
62
60
 
@@ -70,8 +68,6 @@ if (hasHelp) {
70
68
  ${cyan}-c, --config-dir <path>${reset} Specify custom Claude config directory
71
69
  ${cyan}-h, --help${reset} Show this help message
72
70
  ${cyan}--force-statusline${reset} Replace existing statusline config
73
- ${cyan}--force-notify${reset} Replace existing notification hook
74
- ${cyan}--no-notify${reset} Skip notification hook installation
75
71
 
76
72
  ${yellow}Examples:${reset}
77
73
  ${dim}# Install to default ~/.claude directory${reset}
@@ -221,10 +217,6 @@ function install(isGlobal) {
221
217
  const srcFile = path.join(hooksSrc, entry);
222
218
  const destFile = path.join(hooksDest, entry);
223
219
  fs.copyFileSync(srcFile, destFile);
224
- // Make shell scripts executable
225
- if (entry.endsWith('.sh')) {
226
- fs.chmodSync(destFile, 0o755);
227
- }
228
220
  }
229
221
  console.log(` ${green}✓${reset} Installed hooks`);
230
222
  }
@@ -233,14 +225,11 @@ function install(isGlobal) {
233
225
  const settingsPath = path.join(claudeDir, 'settings.json');
234
226
  const settings = readSettings(settingsPath);
235
227
  const statuslineCommand = isGlobal
236
- ? '$HOME/.claude/hooks/statusline.sh'
237
- : '.claude/hooks/statusline.sh';
228
+ ? 'node "$HOME/.claude/hooks/statusline.js"'
229
+ : 'node .claude/hooks/statusline.js';
238
230
  const updateCheckCommand = isGlobal
239
- ? '$HOME/.claude/hooks/gsd-check-update.sh'
240
- : '.claude/hooks/gsd-check-update.sh';
241
- const notifyCommand = isGlobal
242
- ? '$HOME/.claude/hooks/gsd-notify.sh'
243
- : '.claude/hooks/gsd-notify.sh';
231
+ ? 'node "$HOME/.claude/hooks/gsd-check-update.js"'
232
+ : 'node .claude/hooks/gsd-check-update.js';
244
233
 
245
234
  // Configure SessionStart hook for update checking
246
235
  if (!settings.hooks) {
@@ -267,13 +256,13 @@ function install(isGlobal) {
267
256
  console.log(` ${green}✓${reset} Configured update check hook`);
268
257
  }
269
258
 
270
- return { settingsPath, settings, statuslineCommand, notifyCommand };
259
+ return { settingsPath, settings, statuslineCommand };
271
260
  }
272
261
 
273
262
  /**
274
- * Apply statusline and notification config, then print completion message
263
+ * Apply statusline config, then print completion message
275
264
  */
276
- function finishInstall(settingsPath, settings, statuslineCommand, notifyCommand, shouldInstallStatusline, shouldInstallNotify) {
265
+ function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallStatusline) {
277
266
  if (shouldInstallStatusline) {
278
267
  settings.statusLine = {
279
268
  type: 'command',
@@ -282,25 +271,6 @@ function finishInstall(settingsPath, settings, statuslineCommand, notifyCommand,
282
271
  console.log(` ${green}✓${reset} Configured statusline`);
283
272
  }
284
273
 
285
- if (shouldInstallNotify) {
286
- if (!settings.hooks.Stop) {
287
- settings.hooks.Stop = [];
288
- }
289
- // Remove any existing GSD notify hook first
290
- settings.hooks.Stop = settings.hooks.Stop.filter(entry =>
291
- !(entry.hooks && entry.hooks.some(h => h.command && h.command.includes('gsd-notify')))
292
- );
293
- settings.hooks.Stop.push({
294
- hooks: [
295
- {
296
- type: 'command',
297
- command: notifyCommand
298
- }
299
- ]
300
- });
301
- console.log(` ${green}✓${reset} Configured completion notifications`);
302
- }
303
-
304
274
  // Always write settings (hooks were already configured in install())
305
275
  writeSettings(settingsPath, settings);
306
276
 
@@ -365,66 +335,6 @@ function handleStatusline(settings, isInteractive, callback) {
365
335
  });
366
336
  }
367
337
 
368
- /**
369
- * Handle notification hook configuration with optional prompt
370
- */
371
- function handleNotifications(settings, isInteractive, callback) {
372
- // Check if --no-notify flag was passed
373
- if (noNotify) {
374
- callback(false);
375
- return;
376
- }
377
-
378
- // Check if GSD notify hook already exists
379
- const hasExisting = settings.hooks?.Stop?.some(entry =>
380
- entry.hooks && entry.hooks.some(h => h.command && h.command.includes('gsd-notify'))
381
- );
382
-
383
- // No existing - just install it
384
- if (!hasExisting) {
385
- callback(true);
386
- return;
387
- }
388
-
389
- // Has existing and --force-notify flag
390
- if (forceNotify) {
391
- callback(true);
392
- return;
393
- }
394
-
395
- // Has existing, non-interactive mode - skip
396
- if (!isInteractive) {
397
- console.log(` ${yellow}⚠${reset} Skipping notifications (already configured)`);
398
- console.log(` Use ${cyan}--force-notify${reset} to replace\n`);
399
- callback(false);
400
- return;
401
- }
402
-
403
- // Has existing, interactive mode - prompt user
404
- const rl = readline.createInterface({
405
- input: process.stdin,
406
- output: process.stdout
407
- });
408
-
409
- console.log(`
410
- ${yellow}⚠${reset} Existing notification hook detected
411
-
412
- GSD includes completion notifications that alert you when:
413
- • A phase completes planning or execution
414
- • Claude stops and needs your input
415
- • Works on Mac, Linux, and Windows
416
-
417
- ${cyan}1${reset}) Keep existing
418
- ${cyan}2${reset}) Replace with GSD notifications
419
- `);
420
-
421
- rl.question(` Choice ${dim}[1]${reset}: `, (answer) => {
422
- rl.close();
423
- const choice = answer.trim() || '1';
424
- callback(choice === '2');
425
- });
426
- }
427
-
428
338
  /**
429
339
  * Prompt for install location
430
340
  */
@@ -448,12 +358,10 @@ function promptLocation() {
448
358
  rl.close();
449
359
  const choice = answer.trim() || '1';
450
360
  const isGlobal = choice !== '2';
451
- const { settingsPath, settings, statuslineCommand, notifyCommand } = install(isGlobal);
361
+ const { settingsPath, settings, statuslineCommand } = install(isGlobal);
452
362
  // Interactive mode - prompt for optional features
453
363
  handleStatusline(settings, true, (shouldInstallStatusline) => {
454
- handleNotifications(settings, true, (shouldInstallNotify) => {
455
- finishInstall(settingsPath, settings, statuslineCommand, notifyCommand, shouldInstallStatusline, shouldInstallNotify);
456
- });
364
+ finishInstall(settingsPath, settings, statuslineCommand, shouldInstallStatusline);
457
365
  });
458
366
  });
459
367
  }
@@ -466,20 +374,16 @@ if (hasGlobal && hasLocal) {
466
374
  console.error(` ${yellow}Cannot use --config-dir with --local${reset}`);
467
375
  process.exit(1);
468
376
  } else if (hasGlobal) {
469
- const { settingsPath, settings, statuslineCommand, notifyCommand } = install(true);
377
+ const { settingsPath, settings, statuslineCommand } = install(true);
470
378
  // Non-interactive - respect flags
471
379
  handleStatusline(settings, false, (shouldInstallStatusline) => {
472
- handleNotifications(settings, false, (shouldInstallNotify) => {
473
- finishInstall(settingsPath, settings, statuslineCommand, notifyCommand, shouldInstallStatusline, shouldInstallNotify);
474
- });
380
+ finishInstall(settingsPath, settings, statuslineCommand, shouldInstallStatusline);
475
381
  });
476
382
  } else if (hasLocal) {
477
- const { settingsPath, settings, statuslineCommand, notifyCommand } = install(false);
383
+ const { settingsPath, settings, statuslineCommand } = install(false);
478
384
  // Non-interactive - respect flags
479
385
  handleStatusline(settings, false, (shouldInstallStatusline) => {
480
- handleNotifications(settings, false, (shouldInstallNotify) => {
481
- finishInstall(settingsPath, settings, statuslineCommand, notifyCommand, shouldInstallStatusline, shouldInstallNotify);
482
- });
386
+ finishInstall(settingsPath, settings, statuslineCommand, shouldInstallStatusline);
483
387
  });
484
388
  } else {
485
389
  promptLocation();
@@ -35,10 +35,10 @@ Phase number: $ARGUMENTS (required)
35
35
  <process>
36
36
  1. Validate phase number (error if missing or not in roadmap)
37
37
  2. Check if CONTEXT.md exists (offer update/view/skip if yes)
38
- 3. **Analyze phase** — Identify domain boundary and gray areas by category
39
- 4. **Present gray areas** — Multi-select AskUserQuestion: which to discuss?
40
- 5. **Deep-dive each area** — Loop per area until user says "move on"
41
- 6. **Write CONTEXT.md** — Structured by decisions made
38
+ 3. **Analyze phase** — Identify domain and generate phase-specific gray areas
39
+ 4. **Present gray areas** — Multi-select: which to discuss? (NO skip option)
40
+ 5. **Deep-dive each area** — 4 questions per area, then offer more/next
41
+ 6. **Write CONTEXT.md** — Sections match areas discussed
42
42
  7. Offer next steps (research or plan)
43
43
 
44
44
  **CRITICAL: Scope guardrail**
@@ -47,18 +47,27 @@ Phase number: $ARGUMENTS (required)
47
47
  - If user suggests new capabilities: "That's its own phase. I'll note it for later."
48
48
  - Capture deferred ideas — don't lose them, don't act on them
49
49
 
50
- **Gray area categories (use what's relevant):**
51
- - **UI** Layout, visual presentation, information density
52
- - **UX** Interactions, flows, feedback
53
- - **Behavior** Runtime behavior, state changes
54
- - **Empty/Edge States** What shows in unusual situations
55
- - **Content** What information is shown/hidden
50
+ **Domain-aware gray areas:**
51
+ Gray areas depend on what's being built. Analyze the phase goal:
52
+ - Something users SEE → layout, density, interactions, states
53
+ - Something users CALL → responses, errors, auth, versioning
54
+ - Something users RUN output format, flags, modes, error handling
55
+ - Something users READ structure, tone, depth, flow
56
+ - Something being ORGANIZED → criteria, grouping, naming, exceptions
56
57
 
57
- **Do NOT ask about (downstream agents handle these):**
58
- - Technical implementation (researcher investigates)
59
- - Architecture choices (planner decides)
60
- - Performance concerns (researcher/planner handle)
61
- - Scope expansion (roadmap defines scope)
58
+ Generate 3-4 **phase-specific** gray areas, not generic categories.
59
+
60
+ **Probing depth:**
61
+ - Ask 4 questions per area before checking
62
+ - "More questions about [area], or move to next?"
63
+ - If more → ask 4 more, check again
64
+ - After all areas → "Ready to create context?"
65
+
66
+ **Do NOT ask about (Claude handles these):**
67
+ - Technical implementation
68
+ - Architecture choices
69
+ - Performance concerns
70
+ - Scope expansion
62
71
  </process>
63
72
 
64
73
  <success_criteria>
@@ -37,13 +37,7 @@ Phase number: $ARGUMENTS (optional - auto-detects next unplanned phase if not pr
37
37
  - `--gaps` — Gap closure mode (reads VERIFICATION.md, skips research)
38
38
  - `--skip-verify` — Skip planner → checker verification loop
39
39
 
40
- Check for existing research and plans:
41
-
42
- ```bash
43
- ls .planning/phases/${PHASE}-*/*-RESEARCH.md 2>/dev/null
44
- ls .planning/phases/${PHASE}-*/*-PLAN.md 2>/dev/null
45
- ```
46
-
40
+ Normalize phase input in step 2 before any directory lookups.
47
41
  </context>
48
42
 
49
43
  <process>
@@ -56,7 +50,7 @@ ls .planning/ 2>/dev/null
56
50
 
57
51
  **If not found:** Error - user should run `/gsd:new-project` first.
58
52
 
59
- ## 2. Parse Arguments
53
+ ## 2. Parse and Normalize Arguments
60
54
 
61
55
  Extract from $ARGUMENTS:
62
56
 
@@ -68,6 +62,24 @@ Extract from $ARGUMENTS:
68
62
 
69
63
  **If no phase number:** Detect next unplanned phase from roadmap.
70
64
 
65
+ **Normalize phase to zero-padded format:**
66
+
67
+ ```bash
68
+ # Normalize phase number (8 → 08, but preserve decimals like 2.1 → 02.1)
69
+ if [[ "$PHASE" =~ ^[0-9]+$ ]]; then
70
+ PHASE=$(printf "%02d" "$PHASE")
71
+ elif [[ "$PHASE" =~ ^([0-9]+)\.([0-9]+)$ ]]; then
72
+ PHASE=$(printf "%02d.%s" "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}")
73
+ fi
74
+ ```
75
+
76
+ **Check for existing research and plans:**
77
+
78
+ ```bash
79
+ ls .planning/phases/${PHASE}-*/*-RESEARCH.md 2>/dev/null
80
+ ls .planning/phases/${PHASE}-*/*-PLAN.md 2>/dev/null
81
+ ```
82
+
71
83
  ## 3. Validate Phase
72
84
 
73
85
  ```bash
@@ -79,14 +91,13 @@ grep -A5 "Phase ${PHASE}:" .planning/ROADMAP.md 2>/dev/null
79
91
  ## 4. Ensure Phase Directory Exists
80
92
 
81
93
  ```bash
82
- # Match both zero-padded (05-*) and unpadded (5-*) folders
83
- PADDED_PHASE=$(printf "%02d" ${PHASE})
84
- PHASE_DIR=$(ls -d .planning/phases/${PADDED_PHASE}-* .planning/phases/${PHASE}-* 2>/dev/null | head -1)
94
+ # PHASE is already normalized (08, 02.1, etc.) from step 2
95
+ PHASE_DIR=$(ls -d .planning/phases/${PHASE}-* 2>/dev/null | head -1)
85
96
  if [ -z "$PHASE_DIR" ]; then
86
- # Create phase directory from roadmap name with zero-padded phase number
97
+ # Create phase directory from roadmap name
87
98
  PHASE_NAME=$(grep "Phase ${PHASE}:" .planning/ROADMAP.md | sed 's/.*Phase [0-9]*: //' | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
88
- mkdir -p ".planning/phases/${PADDED_PHASE}-${PHASE_NAME}"
89
- PHASE_DIR=".planning/phases/${PADDED_PHASE}-${PHASE_NAME}"
99
+ mkdir -p ".planning/phases/${PHASE}-${PHASE_NAME}"
100
+ PHASE_DIR=".planning/phases/${PHASE}-${PHASE_NAME}"
90
101
  fi
91
102
  ```
92
103
 
@@ -26,17 +26,23 @@ Research how to implement a phase. Spawns gsd-phase-researcher agent with phase
26
26
  <context>
27
27
  Phase number: $ARGUMENTS (required)
28
28
 
29
- Check for existing research:
30
- ```bash
31
- ls .planning/phases/${PHASE}-*/*RESEARCH.md 2>/dev/null
32
- ```
29
+ Normalize phase input in step 1 before any directory lookups.
33
30
  </context>
34
31
 
35
32
  <process>
36
33
 
37
- ## 1. Parse and Validate Phase
34
+ ## 1. Normalize and Validate Phase
38
35
 
39
36
  ```bash
37
+ # Normalize phase number (8 → 08, but preserve decimals like 2.1 → 02.1)
38
+ if [[ "$ARGUMENTS" =~ ^[0-9]+$ ]]; then
39
+ PHASE=$(printf "%02d" "$ARGUMENTS")
40
+ elif [[ "$ARGUMENTS" =~ ^([0-9]+)\.([0-9]+)$ ]]; then
41
+ PHASE=$(printf "%02d.%s" "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}")
42
+ else
43
+ PHASE="$ARGUMENTS"
44
+ fi
45
+
40
46
  grep -A5 "Phase ${PHASE}:" .planning/ROADMAP.md 2>/dev/null
41
47
  ```
42
48
 
@@ -118,7 +124,7 @@ Before declaring complete, verify:
118
124
  </quality_gate>
119
125
 
120
126
  <output>
121
- Write to: .planning/phases/{phase}-{slug}/{phase}-RESEARCH.md
127
+ Write to: .planning/phases/${PHASE}-{slug}/${PHASE}-RESEARCH.md
122
128
  </output>
123
129
  ```
124
130
 
@@ -146,7 +152,7 @@ Continue research for Phase {phase_number}: {phase_name}
146
152
  </objective>
147
153
 
148
154
  <prior_state>
149
- Research file: @.planning/phases/{phase}-{slug}/{phase}-RESEARCH.md
155
+ Research file: @.planning/phases/${PHASE}-{slug}/${PHASE}-RESEARCH.md
150
156
  </prior_state>
151
157
 
152
158
  <checkpoint_response>
@@ -4,6 +4,8 @@ Template for `.planning/phases/XX-name/{phase}-CONTEXT.md` - captures implementa
4
4
 
5
5
  **Purpose:** Document decisions that downstream agents need. Researcher uses this to know WHAT to investigate. Planner uses this to know WHAT choices are locked vs flexible.
6
6
 
7
+ **Key principle:** Categories are NOT predefined. They emerge from what was actually discussed for THIS phase. A CLI phase has CLI-relevant sections, a UI phase has UI-relevant sections.
8
+
7
9
  **Downstream consumers:**
8
10
  - `gsd-phase-researcher` — Reads decisions to focus research (e.g., "card layout" → research card component patterns)
9
11
  - `gsd-planner` — Reads decisions to create specific tasks (e.g., "infinite scroll" → task includes virtualization)
@@ -28,11 +30,14 @@ Template for `.planning/phases/XX-name/{phase}-CONTEXT.md` - captures implementa
28
30
  <decisions>
29
31
  ## Implementation Decisions
30
32
 
31
- ### [Category discussed, e.g., UI]
33
+ ### [Area 1 that was discussed]
32
34
  - [Specific decision made]
33
35
  - [Another decision if applicable]
34
36
 
35
- ### [Category discussed, e.g., Behavior]
37
+ ### [Area 2 that was discussed]
38
+ - [Specific decision made]
39
+
40
+ ### [Area 3 that was discussed]
36
41
  - [Specific decision made]
37
42
 
38
43
  ### Claude's Discretion
@@ -65,6 +70,9 @@ Template for `.planning/phases/XX-name/{phase}-CONTEXT.md` - captures implementa
65
70
  ```
66
71
 
67
72
  <good_examples>
73
+
74
+ **Example 1: Visual feature (Post Feed)**
75
+
68
76
  ```markdown
69
77
  # Phase 3: Post Feed - Context
70
78
 
@@ -81,18 +89,17 @@ Display posts from followed users in a scrollable feed. Users can view posts and
81
89
  <decisions>
82
90
  ## Implementation Decisions
83
91
 
84
- ### UI
92
+ ### Layout style
85
93
  - Card-based layout, not timeline or list
86
94
  - Each card shows: author avatar, name, timestamp, full post content, reaction counts
87
95
  - Cards have subtle shadows, rounded corners — modern feel
88
- - Show 10 posts initially, load more on scroll
89
96
 
90
- ### Behavior
97
+ ### Loading behavior
91
98
  - Infinite scroll, not pagination
92
99
  - Pull-to-refresh on mobile
93
100
  - New posts indicator at top ("3 new posts") rather than auto-inserting
94
101
 
95
- ### Empty State
102
+ ### Empty state
96
103
  - Friendly illustration + "Follow people to see posts here"
97
104
  - Suggest 3-5 accounts to follow based on interests
98
105
 
@@ -108,7 +115,6 @@ Display posts from followed users in a scrollable feed. Users can view posts and
108
115
 
109
116
  - "I like how Twitter shows the new posts indicator without disrupting your scroll position"
110
117
  - Cards should feel like Linear's issue cards — clean, not cluttered
111
- - No infinite scroll fatigue — maybe show "You're all caught up" after ~50 posts
112
118
 
113
119
  </specifics>
114
120
 
@@ -116,7 +122,6 @@ Display posts from followed users in a scrollable feed. Users can view posts and
116
122
  ## Deferred Ideas
117
123
 
118
124
  - Commenting on posts — Phase 5
119
- - Reaction picker (not just counts) — Phase 5
120
125
  - Bookmarking posts — add to backlog
121
126
 
122
127
  </deferred>
@@ -126,6 +131,131 @@ Display posts from followed users in a scrollable feed. Users can view posts and
126
131
  *Phase: 03-post-feed*
127
132
  *Context gathered: 2025-01-20*
128
133
  ```
134
+
135
+ **Example 2: CLI tool (Database backup)**
136
+
137
+ ```markdown
138
+ # Phase 2: Backup Command - Context
139
+
140
+ **Gathered:** 2025-01-20
141
+ **Status:** Ready for planning
142
+
143
+ <domain>
144
+ ## Phase Boundary
145
+
146
+ CLI command to backup database to local file or S3. Supports full and incremental backups. Restore command is a separate phase.
147
+
148
+ </domain>
149
+
150
+ <decisions>
151
+ ## Implementation Decisions
152
+
153
+ ### Output format
154
+ - JSON for programmatic use, table format for humans
155
+ - Default to table, --json flag for JSON
156
+ - Verbose mode (-v) shows progress, silent by default
157
+
158
+ ### Flag design
159
+ - Short flags for common options: -o (output), -v (verbose), -f (force)
160
+ - Long flags for clarity: --incremental, --compress, --encrypt
161
+ - Required: database connection string (positional or --db)
162
+
163
+ ### Error recovery
164
+ - Retry 3 times on network failure, then fail with clear message
165
+ - --no-retry flag to fail fast
166
+ - Partial backups are deleted on failure (no corrupt files)
167
+
168
+ ### Claude's Discretion
169
+ - Exact progress bar implementation
170
+ - Compression algorithm choice
171
+ - Temp file handling
172
+
173
+ </decisions>
174
+
175
+ <specifics>
176
+ ## Specific Ideas
177
+
178
+ - "I want it to feel like pg_dump — familiar to database people"
179
+ - Should work in CI pipelines (exit codes, no interactive prompts)
180
+
181
+ </specifics>
182
+
183
+ <deferred>
184
+ ## Deferred Ideas
185
+
186
+ - Scheduled backups — separate phase
187
+ - Backup rotation/retention — add to backlog
188
+
189
+ </deferred>
190
+
191
+ ---
192
+
193
+ *Phase: 02-backup-command*
194
+ *Context gathered: 2025-01-20*
195
+ ```
196
+
197
+ **Example 3: Organization task (Photo library)**
198
+
199
+ ```markdown
200
+ # Phase 1: Photo Organization - Context
201
+
202
+ **Gathered:** 2025-01-20
203
+ **Status:** Ready for planning
204
+
205
+ <domain>
206
+ ## Phase Boundary
207
+
208
+ Organize existing photo library into structured folders. Handle duplicates and apply consistent naming. Tagging and search are separate phases.
209
+
210
+ </domain>
211
+
212
+ <decisions>
213
+ ## Implementation Decisions
214
+
215
+ ### Grouping criteria
216
+ - Primary grouping by year, then by month
217
+ - Events detected by time clustering (photos within 2 hours = same event)
218
+ - Event folders named by date + location if available
219
+
220
+ ### Duplicate handling
221
+ - Keep highest resolution version
222
+ - Move duplicates to _duplicates folder (don't delete)
223
+ - Log all duplicate decisions for review
224
+
225
+ ### Naming convention
226
+ - Format: YYYY-MM-DD_HH-MM-SS_originalname.ext
227
+ - Preserve original filename as suffix for searchability
228
+ - Handle name collisions with incrementing suffix
229
+
230
+ ### Claude's Discretion
231
+ - Exact clustering algorithm
232
+ - How to handle photos with no EXIF data
233
+ - Folder emoji usage
234
+
235
+ </decisions>
236
+
237
+ <specifics>
238
+ ## Specific Ideas
239
+
240
+ - "I want to be able to find photos by roughly when they were taken"
241
+ - Don't delete anything — worst case, move to a review folder
242
+
243
+ </specifics>
244
+
245
+ <deferred>
246
+ ## Deferred Ideas
247
+
248
+ - Face detection grouping — future phase
249
+ - Cloud sync — out of scope for now
250
+
251
+ </deferred>
252
+
253
+ ---
254
+
255
+ *Phase: 01-photo-organization*
256
+ *Context gathered: 2025-01-20*
257
+ ```
258
+
129
259
  </good_examples>
130
260
 
131
261
  <guidelines>
@@ -133,11 +263,11 @@ Display posts from followed users in a scrollable feed. Users can view posts and
133
263
 
134
264
  The output should answer: "What does the researcher need to investigate? What choices are locked for the planner?"
135
265
 
136
- **Good content:**
266
+ **Good content (concrete decisions):**
137
267
  - "Card-based layout, not timeline"
138
- - "Infinite scroll with pull-to-refresh"
139
- - "Show 10 posts initially"
140
- - "New posts indicator rather than auto-insert"
268
+ - "Retry 3 times on network failure, then fail"
269
+ - "Group by year, then by month"
270
+ - "JSON for programmatic use, table for humans"
141
271
 
142
272
  **Bad content (too vague):**
143
273
  - "Should feel modern and clean"
@@ -148,7 +278,7 @@ The output should answer: "What does the researcher need to investigate? What ch
148
278
  **Sections explained:**
149
279
 
150
280
  - **Domain** — The scope anchor. Copied/derived from ROADMAP.md. Fixed boundary.
151
- - **Decisions** — Organized by category (UI, UX, Behavior, etc.). Actual choices made.
281
+ - **Decisions** — Organized by areas discussed (NOT predefined categories). Section headers come from the actual discussion — "Layout style", "Flag design", "Grouping criteria", etc.
152
282
  - **Claude's Discretion** — Explicit acknowledgment of what Claude can decide during implementation.
153
283
  - **Specifics** — Product references, examples, "like X but..." statements.
154
284
  - **Deferred** — Ideas captured but explicitly out of scope. Prevents scope creep while preserving good ideas.
@@ -66,23 +66,44 @@ For now, let's focus on [phase domain]."
66
66
  Capture the idea in a "Deferred Ideas" section. Don't lose it, don't act on it.
67
67
  </scope_guardrail>
68
68
 
69
- <gray_area_categories>
70
- Use these categories when analyzing a phase. Not all apply to every phase.
71
-
72
- | Category | What it clarifies | Example questions |
73
- |----------|-------------------|-------------------|
74
- | **UI** | Visual presentation, layout, information density | "Card-based or list view?" "What info shows on each item?" |
75
- | **UX** | Interactions, flows, feedback | "How does loading work?" "What happens when you tap X?" |
76
- | **Behavior** | Runtime behavior, state changes | "Auto-refresh or manual?" "How does pagination work?" |
77
- | **Empty/Edge States** | What shows in unusual situations | "What appears with no data?" "How do errors display?" |
78
- | **Content** | What information is shown/hidden | "Show timestamps?" "How much preview text?" |
79
-
80
- **Categories to AVOID:**
81
- - **Scope**The roadmap defines scope, not discussion
82
- - **Technical** — You figure out implementation
83
- - **Architecture** You decide patterns
84
- - **Performance** — You handle optimization
85
- </gray_area_categories>
69
+ <gray_area_identification>
70
+ Gray areas are **implementation decisions the user cares about** things that could go multiple ways and would change the result.
71
+
72
+ **How to identify gray areas:**
73
+
74
+ 1. **Read the phase goal** from ROADMAP.md
75
+ 2. **Understand the domain** What kind of thing is being built?
76
+ - Something users SEE visual presentation, interactions, states matter
77
+ - Something users CALL interface contracts, responses, errors matter
78
+ - Something users RUN invocation, output, behavior modes matter
79
+ - Something users READ → structure, tone, depth, flow matter
80
+ - Something being ORGANIZED → criteria, grouping, handling exceptions matter
81
+ 3. **Generate phase-specific gray areas** — Not generic categories, but concrete decisions for THIS phase
82
+
83
+ **Don't use generic category labels** (UI, UX, Behavior). Generate specific gray areas:
84
+
85
+ ```
86
+ Phase: "User authentication"
87
+ → Session handling, Error responses, Multi-device policy, Recovery flow
88
+
89
+ Phase: "Organize photo library"
90
+ → Grouping criteria, Duplicate handling, Naming convention, Folder structure
91
+
92
+ Phase: "CLI for database backups"
93
+ → Output format, Flag design, Progress reporting, Error recovery
94
+
95
+ Phase: "API documentation"
96
+ → Structure/navigation, Code examples depth, Versioning approach, Interactive elements
97
+ ```
98
+
99
+ **The key question:** What decisions would change the outcome that the user should weigh in on?
100
+
101
+ **Claude handles these (don't ask):**
102
+ - Technical implementation details
103
+ - Architecture patterns
104
+ - Performance optimization
105
+ - Scope (roadmap defines this)
106
+ </gray_area_identification>
86
107
 
87
108
  <process>
88
109
 
@@ -169,47 +190,79 @@ We'll clarify HOW to implement this.
169
190
 
170
191
  **Then use AskUserQuestion (multiSelect: true):**
171
192
  - header: "Discuss"
172
- - question: "Which areas do you want to discuss?"
173
- - options: Generate 2-4 based on your analysis, each formatted as:
174
- - "[Category] — [Specific gray area question]"
175
- - Last option always: "None — you decide, proceed to planning"
193
+ - question: "Which areas do you want to discuss for [phase name]?"
194
+ - options: Generate 3-4 phase-specific gray areas, each formatted as:
195
+ - "[Specific area]" (label) concrete, not generic
196
+ - [1-2 questions this covers] (description)
197
+
198
+ **Do NOT include a "skip" or "you decide" option.** User ran this command to discuss — give them real choices.
176
199
 
177
- **Example options:**
200
+ **Examples by domain:**
201
+
202
+ For "Post Feed" (visual feature):
203
+ ```
204
+ ☐ Layout style — Cards vs list vs timeline? Information density?
205
+ ☐ Loading behavior — Infinite scroll or pagination? Pull to refresh?
206
+ ☐ Content ordering — Chronological, algorithmic, or user choice?
207
+ ☐ Post metadata — What info per post? Timestamps, reactions, author?
208
+ ```
209
+
210
+ For "Database backup CLI" (command-line tool):
211
+ ```
212
+ ☐ Output format — JSON, table, or plain text? Verbosity levels?
213
+ ☐ Flag design — Short flags, long flags, or both? Required vs optional?
214
+ ☐ Progress reporting — Silent, progress bar, or verbose logging?
215
+ ☐ Error recovery — Fail fast, retry, or prompt for action?
216
+ ```
217
+
218
+ For "Organize photo library" (organization task):
178
219
  ```
179
- UICard layout or timeline? How much of each post shows?
180
- BehaviorInfinite scroll or pagination? Pull to refresh?
181
- Empty stateWhat appears when there are no posts?
182
- NoneYou decide, proceed to planning
220
+ Grouping criteria By date, location, faces, or events?
221
+ Duplicate handling Keep best, keep all, or prompt each time?
222
+ Naming conventionOriginal names, dates, or descriptive?
223
+ Folder structure Flat, nested by year, or by category?
183
224
  ```
184
225
 
185
- If user selects "None": Skip to write_context with minimal context.
186
- Otherwise: Continue to discuss_areas with selected areas.
226
+ Continue to discuss_areas with selected areas.
187
227
  </step>
188
228
 
189
229
  <step name="discuss_areas">
190
230
  For each selected area, conduct a focused discussion loop.
191
231
 
232
+ **Philosophy: 4 questions, then check.**
233
+
234
+ Ask 4 questions per area before offering to continue or move on. Each answer often reveals the next question.
235
+
192
236
  **For each area:**
193
237
 
194
238
  1. **Announce the area:**
195
239
  ```
196
- Let's talk about [Category].
240
+ Let's talk about [Area].
197
241
  ```
198
242
 
199
- 2. **Ask focused questions using AskUserQuestion:**
200
- - header: "[Category]"
201
- - question: Specific question about that gray area
202
- - options: 2-3 concrete choices + "Let me describe" + "You decide"
243
+ 2. **Ask 4 questions using AskUserQuestion:**
244
+ - header: "[Area]"
245
+ - question: Specific decision for this area
246
+ - options: 2-3 concrete choices (AskUserQuestion adds "Other" automatically)
247
+ - Include "You decide" as an option when reasonable — captures Claude discretion
203
248
 
204
- 3. **Follow up based on response:**
205
- - If they chose an option: Capture it, ask if there's more about this area
206
- - If "Let me describe": Receive their input, reflect it back, confirm understanding
207
- - If "You decide": Note that Claude has discretion here
249
+ 3. **After 4 questions, check:**
250
+ - header: "[Area]"
251
+ - question: "More questions about [area], or move to next?"
252
+ - options: "More questions" / "Next area"
208
253
 
209
- 4. **Loop control Always offer:**
210
- - "Ask more about [Category]" Continue probing this area
211
- - "Move to next area" — Done with this category
212
- - "That's enough, create context" — Done with all discussion
254
+ If "More questions" → ask 4 more, then check again
255
+ If "Next area" proceed to next selected area
256
+
257
+ 4. **After all areas complete:**
258
+ - header: "Done"
259
+ - question: "That covers [list areas]. Ready to create context?"
260
+ - options: "Create context" / "Revisit an area"
261
+
262
+ **Question design:**
263
+ - Options should be concrete, not abstract ("Cards" not "Option A")
264
+ - Each answer should inform the next question
265
+ - If user picks "Other", receive their input, reflect it back, confirm
213
266
 
214
267
  **Scope creep handling:**
215
268
  If user mentions something outside the phase domain:
@@ -217,14 +270,10 @@ If user mentions something outside the phase domain:
217
270
  "[Feature] sounds like a new capability — that belongs in its own phase.
218
271
  I'll note it as a deferred idea.
219
272
 
220
- Back to [current domain]: [return to current question]"
273
+ Back to [current area]: [return to current question]"
221
274
  ```
222
275
 
223
276
  Track deferred ideas internally.
224
-
225
- **Continue until:**
226
- - User says "Move to next area" and all selected areas are done, OR
227
- - User says "That's enough, create context"
228
277
  </step>
229
278
 
230
279
  <step name="write_context">
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+ // Check for GSD updates in background, write result to cache
3
+ // Called by SessionStart hook - runs once per session
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const os = require('os');
8
+ const { execSync, spawn } = require('child_process');
9
+
10
+ const homeDir = os.homedir();
11
+ const cacheDir = path.join(homeDir, '.claude', 'cache');
12
+ const cacheFile = path.join(cacheDir, 'gsd-update-check.json');
13
+ const versionFile = path.join(homeDir, '.claude', 'get-shit-done', 'VERSION');
14
+
15
+ // Ensure cache directory exists
16
+ if (!fs.existsSync(cacheDir)) {
17
+ fs.mkdirSync(cacheDir, { recursive: true });
18
+ }
19
+
20
+ // Run check in background (spawn detached process)
21
+ const child = spawn(process.execPath, ['-e', `
22
+ const fs = require('fs');
23
+ const { execSync } = require('child_process');
24
+
25
+ const cacheFile = ${JSON.stringify(cacheFile)};
26
+ const versionFile = ${JSON.stringify(versionFile)};
27
+
28
+ let installed = '0.0.0';
29
+ try {
30
+ installed = fs.readFileSync(versionFile, 'utf8').trim();
31
+ } catch (e) {}
32
+
33
+ let latest = null;
34
+ try {
35
+ latest = execSync('npm view get-shit-done-cc version', { encoding: 'utf8', timeout: 10000 }).trim();
36
+ } catch (e) {}
37
+
38
+ const result = {
39
+ update_available: latest && installed !== latest,
40
+ installed,
41
+ latest: latest || 'unknown',
42
+ checked: Math.floor(Date.now() / 1000)
43
+ };
44
+
45
+ fs.writeFileSync(cacheFile, JSON.stringify(result));
46
+ `], {
47
+ detached: true,
48
+ stdio: 'ignore'
49
+ });
50
+
51
+ child.unref();
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+ // Claude Code Statusline - GSD Edition
3
+ // Shows: model | current task | directory | context usage
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const os = require('os');
8
+
9
+ // Read JSON from stdin
10
+ let input = '';
11
+ process.stdin.setEncoding('utf8');
12
+ process.stdin.on('data', chunk => input += chunk);
13
+ process.stdin.on('end', () => {
14
+ try {
15
+ const data = JSON.parse(input);
16
+ const model = data.model?.display_name || 'Claude';
17
+ const dir = data.workspace?.current_dir || process.cwd();
18
+ const session = data.session_id || '';
19
+ const remaining = data.context_window?.remaining_percentage;
20
+
21
+ // Context window display (shows USED percentage)
22
+ let ctx = '';
23
+ if (remaining != null) {
24
+ const rem = Math.round(remaining);
25
+ const used = 100 - rem;
26
+
27
+ // Build progress bar (10 segments)
28
+ const filled = Math.floor(used / 10);
29
+ const bar = '█'.repeat(filled) + '░'.repeat(10 - filled);
30
+
31
+ // Color based on usage
32
+ if (used < 50) {
33
+ ctx = ` \x1b[32m${bar} ${used}%\x1b[0m`;
34
+ } else if (used < 65) {
35
+ ctx = ` \x1b[33m${bar} ${used}%\x1b[0m`;
36
+ } else if (used < 80) {
37
+ ctx = ` \x1b[38;5;208m${bar} ${used}%\x1b[0m`;
38
+ } else {
39
+ ctx = ` \x1b[5;31m💀 ${bar} ${used}%\x1b[0m`;
40
+ }
41
+ }
42
+
43
+ // Current task from todos
44
+ let task = '';
45
+ const homeDir = os.homedir();
46
+ const todosDir = path.join(homeDir, '.claude', 'todos');
47
+ if (session && fs.existsSync(todosDir)) {
48
+ const files = fs.readdirSync(todosDir)
49
+ .filter(f => f.startsWith(session) && f.includes('-agent-') && f.endsWith('.json'))
50
+ .map(f => ({ name: f, mtime: fs.statSync(path.join(todosDir, f)).mtime }))
51
+ .sort((a, b) => b.mtime - a.mtime);
52
+
53
+ if (files.length > 0) {
54
+ try {
55
+ const todos = JSON.parse(fs.readFileSync(path.join(todosDir, files[0].name), 'utf8'));
56
+ const inProgress = todos.find(t => t.status === 'in_progress');
57
+ if (inProgress) task = inProgress.activeForm || '';
58
+ } catch (e) {}
59
+ }
60
+ }
61
+
62
+ // GSD update available?
63
+ let gsdUpdate = '';
64
+ const cacheFile = path.join(homeDir, '.claude', 'cache', 'gsd-update-check.json');
65
+ if (fs.existsSync(cacheFile)) {
66
+ try {
67
+ const cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
68
+ if (cache.update_available) {
69
+ gsdUpdate = '\x1b[33m⬆ /gsd:update\x1b[0m │ ';
70
+ }
71
+ } catch (e) {}
72
+ }
73
+
74
+ // Output
75
+ const dirname = path.basename(dir);
76
+ if (task) {
77
+ process.stdout.write(`${gsdUpdate}\x1b[2m${model}\x1b[0m │ \x1b[1m${task}\x1b[0m │ \x1b[2m${dirname}\x1b[0m${ctx}`);
78
+ } else {
79
+ process.stdout.write(`${gsdUpdate}\x1b[2m${model}\x1b[0m │ \x1b[2m${dirname}\x1b[0m${ctx}`);
80
+ }
81
+ } catch (e) {
82
+ // Silent fail - don't break statusline on parse errors
83
+ }
84
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "get-shit-done-cc",
3
- "version": "1.5.28",
3
+ "version": "1.5.29",
4
4
  "description": "A meta-prompting, context engineering and spec-driven development system for Claude Code by TÂCHES.",
5
5
  "bin": {
6
6
  "get-shit-done-cc": "bin/install.js"
@@ -1,20 +0,0 @@
1
- #!/bin/bash
2
- # Check for GSD updates in background, write result to cache
3
- # Called by SessionStart hook - runs once per session
4
-
5
- CACHE_FILE="$HOME/.claude/cache/gsd-update-check.json"
6
- mkdir -p "$HOME/.claude/cache"
7
-
8
- # Run check in background (non-blocking)
9
- (
10
- installed=$(cat "$HOME/.claude/get-shit-done/VERSION" 2>/dev/null || echo "0.0.0")
11
- latest=$(npm view get-shit-done-cc version 2>/dev/null)
12
-
13
- if [[ -n "$latest" && "$installed" != "$latest" ]]; then
14
- echo "{\"update_available\":true,\"installed\":\"$installed\",\"latest\":\"$latest\",\"checked\":$(date +%s)}" > "$CACHE_FILE"
15
- else
16
- echo "{\"update_available\":false,\"installed\":\"$installed\",\"latest\":\"${latest:-unknown}\",\"checked\":$(date +%s)}" > "$CACHE_FILE"
17
- fi
18
- ) &
19
-
20
- exit 0
@@ -1,59 +0,0 @@
1
- #!/bin/bash
2
- # GSD Completion Notification Hook
3
- # Cross-platform alert when Claude stops (task complete, needs input, etc.)
4
-
5
- input=$(cat)
6
- session_id=$(echo "$input" | jq -r '.session_id // empty')
7
- cwd=$(echo "$input" | jq -r '.cwd // empty')
8
- transcript_path=$(echo "$input" | jq -r '.transcript_path // empty')
9
-
10
- # Extract project name
11
- project="Claude Code"
12
- if [[ -n "$cwd" ]]; then
13
- project=$(basename "$cwd")
14
- fi
15
-
16
- # Check todo list for current/completed task
17
- message=""
18
- if [[ -n "$session_id" ]]; then
19
- todo_file=$(ls -t "$HOME/.claude/todos/${session_id}"*.json 2>/dev/null | head -1)
20
- if [[ -f "$todo_file" ]]; then
21
- # Get most recently completed task, or in-progress task
22
- completed=$(jq -r '[.[] | select(.status=="completed")] | last | .content // empty' "$todo_file" 2>/dev/null)
23
- if [[ -n "$completed" ]]; then
24
- message="Completed: $completed"
25
- else
26
- in_progress=$(jq -r '.[] | select(.status=="in_progress") | .content' "$todo_file" 2>/dev/null | head -1)
27
- if [[ -n "$in_progress" ]]; then
28
- message="Paused: $in_progress"
29
- fi
30
- fi
31
- fi
32
- fi
33
-
34
- # Fallback: generic message
35
- if [[ -z "$message" ]]; then
36
- message="Ready for input"
37
- fi
38
-
39
- # Send notification based on OS
40
- case "$(uname -s)" in
41
- Darwin)
42
- osascript -e "display alert \"GSD: $project\" message \"$message\" as informational" &>/dev/null &
43
- ;;
44
- Linux)
45
- if command -v notify-send &>/dev/null; then
46
- notify-send "GSD: $project" "$message" --urgency=normal
47
- elif command -v zenity &>/dev/null; then
48
- zenity --info --title="GSD: $project" --text="$message" &
49
- fi
50
- ;;
51
- MINGW*|CYGWIN*|MSYS*)
52
- # Windows via PowerShell
53
- if command -v powershell.exe &>/dev/null; then
54
- powershell.exe -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.MessageBox]::Show('$message', 'GSD: $project', 'OK', 'Information')" &>/dev/null &
55
- fi
56
- ;;
57
- esac
58
-
59
- exit 0
@@ -1,58 +0,0 @@
1
- #!/bin/bash
2
- # Claude Code Statusline - GSD Edition
3
- # Shows: model | current task | directory | context usage
4
-
5
- input=$(cat)
6
- model=$(echo "$input" | jq -r '.model.display_name')
7
- dir=$(echo "$input" | jq -r '.workspace.current_dir')
8
- session=$(echo "$input" | jq -r '.session_id')
9
- remaining=$(echo "$input" | jq -r '.context_window.remaining_percentage // empty')
10
-
11
- # Context window display (shows USED percentage)
12
- ctx=""
13
- if [ -n "$remaining" ]; then
14
- rem=$(printf "%.0f" "$remaining")
15
- used=$((100 - rem))
16
-
17
- # Build progress bar (10 segments) - fills as context is consumed
18
- filled=$((used / 10))
19
- bar=""
20
- for ((i=0; i<filled; i++)); do bar+="█"; done
21
- for ((i=filled; i<10; i++)); do bar+="░"; done
22
-
23
- # Color based on usage with blinking skull at 80%+
24
- if [ "$used" -lt 50 ]; then
25
- ctx=$' \033[32m'"$bar $used%"$'\033[0m'
26
- elif [ "$used" -lt 65 ]; then
27
- ctx=$' \033[33m'"$bar $used%"$'\033[0m'
28
- elif [ "$used" -lt 80 ]; then
29
- ctx=$' \033[38;5;208m'"$bar $used%"$'\033[0m'
30
- else
31
- # Blinking red with skull
32
- ctx=$' \033[5;31m💀 '"$bar $used%"$'\033[0m'
33
- fi
34
- fi
35
-
36
- # Current task from todos
37
- task=""
38
- todo=$(ls -t "$HOME/.claude/todos/${session}"-agent-*.json 2>/dev/null | head -1)
39
- if [[ -f "$todo" ]]; then
40
- task=$(jq -r '.[] | select(.status=="in_progress") | .activeForm' "$todo" 2>/dev/null | head -1)
41
- fi
42
-
43
- # GSD update available?
44
- gsd_update=""
45
- if [[ -f "$HOME/.claude/cache/gsd-update-check.json" ]]; then
46
- update_available=$(jq -r '.update_available' "$HOME/.claude/cache/gsd-update-check.json" 2>/dev/null)
47
- if [[ "$update_available" == "true" ]]; then
48
- gsd_update=$'\033[33m⬆ /gsd:update\033[0m │ '
49
- fi
50
- fi
51
-
52
- # Output
53
- dirname=$(basename "$dir")
54
- if [[ -n "$task" ]]; then
55
- printf '%s\033[2m%s\033[0m │ \033[1m%s\033[0m │ \033[2m%s\033[0m%s' "$gsd_update" "$model" "$task" "$dirname" "$ctx"
56
- else
57
- printf '%s\033[2m%s\033[0m │ \033[2m%s\033[0m%s' "$gsd_update" "$model" "$dirname" "$ctx"
58
- fi