gsd-cc 1.3.3 → 1.4.1

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/bin/install.js CHANGED
@@ -29,7 +29,7 @@ ${cyan} ██████╗ ███████╗██████╗
29
29
  // Sub-skills that get their own top-level directory under .claude/skills/
30
30
  const SUB_SKILLS = ['apply', 'auto', 'config', 'discuss', 'help', 'ideate', 'ingest', 'plan', 'profile', 'seed', 'stack', 'status', 'tutorial', 'unify', 'update', 'vision'];
31
31
 
32
- // Shared directories that go into gsd-cc-shared/
32
+ // Shared directories installed directly into .claude/
33
33
  const SHARED_DIRS = ['checklists', 'prompts', 'templates'];
34
34
 
35
35
  // Parse args
@@ -139,13 +139,15 @@ function install(isGlobal) {
139
139
  }
140
140
  }
141
141
 
142
- // 3. Install shared resources (templates, checklists, prompts)
143
- const sharedDest = path.join(skillsBase, 'gsd-cc-shared');
142
+ // 3. Install shared resources (templates, checklists, prompts) into .claude/
143
+ const claudeBase = isGlobal
144
+ ? path.join(os.homedir(), '.claude')
145
+ : path.join(process.cwd(), '.claude');
144
146
  for (const dir of SHARED_DIRS) {
145
147
  const srcDir = path.join(skillsSrc, dir);
146
148
  if (fs.existsSync(srcDir)) {
147
- copyDir(srcDir, path.join(sharedDest, dir));
148
- fileCount += countFiles(path.join(sharedDest, dir));
149
+ copyDir(srcDir, path.join(claudeBase, dir));
150
+ fileCount += countFiles(path.join(claudeBase, dir));
149
151
  }
150
152
  }
151
153
 
@@ -219,22 +221,19 @@ function installHooks(isGlobal, hooksDir) {
219
221
  ]
220
222
  });
221
223
 
222
- // PostToolUse: context monitor (all tools) + workflow guard (Edit/Write)
224
+ // PostToolUse: context monitor + statusline (all tools) + workflow guard (Edit/Write)
223
225
  if (!settings.hooks.PostToolUse) settings.hooks.PostToolUse = [];
224
226
  settings.hooks.PostToolUse.push({
225
- hooks: [{ type: 'command', command: contextMonitor, timeout: 5000 }]
227
+ hooks: [
228
+ { type: 'command', command: contextMonitor, timeout: 5000 },
229
+ { type: 'command', command: statusline, timeout: 3000 }
230
+ ]
226
231
  });
227
232
  settings.hooks.PostToolUse.push({
228
233
  matcher: 'Edit|Write',
229
234
  hooks: [{ type: 'command', command: workflowGuard, timeout: 5000 }]
230
235
  });
231
236
 
232
- // Notification: statusline
233
- if (!settings.hooks.Notification) settings.hooks.Notification = [];
234
- settings.hooks.Notification.push({
235
- hooks: [{ type: 'command', command: statusline, timeout: 3000 }]
236
- });
237
-
238
237
  fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
239
238
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
240
239
  console.log(` ${green}✓${reset} Hooks configured in ${settingsPath.replace(os.homedir(), '~').replace(process.cwd(), '.')}`);
@@ -314,6 +313,7 @@ function countFiles(dir) {
314
313
  function uninstall() {
315
314
  const locations = [getSkillsBase(true), getSkillsBase(false)];
316
315
  const allDirs = ['gsd-cc', ...SUB_SKILLS.map(s => `gsd-cc-${s}`), 'gsd-cc-shared', 'gsd'];
316
+ const sharedDirs = ['checklists', 'prompts', 'templates', 'hooks'];
317
317
 
318
318
  let removed = false;
319
319
 
@@ -330,6 +330,15 @@ function uninstall() {
330
330
  }
331
331
  }
332
332
 
333
+ // Remove shared dirs from .claude/
334
+ const claudeDir = base.replace(/\/skills$/, '');
335
+ for (const dir of sharedDirs) {
336
+ const fullPath = path.join(claudeDir, dir);
337
+ if (removeDir(fullPath)) {
338
+ removedFromLocation = true;
339
+ }
340
+ }
341
+
333
342
  if (removedFromLocation) {
334
343
  console.log(` ${green}✓${reset} Removed GSD-CC from ${label}`);
335
344
  removed = true;
@@ -55,21 +55,22 @@ if echo "$CONTENT" | grep -iqE '(show|reveal|print|output|display) (your|the|sys
55
55
  REASON="Detected system prompt extraction attempt"
56
56
  fi
57
57
 
58
- # Pattern 4: Invisible Unicode characters (zero-width spaces, RTL override, etc.)
59
- if echo "$CONTENT" | grep -qP '[\x{200B}\x{200C}\x{200D}\x{FEFF}\x{202A}-\x{202E}\x{2066}-\x{2069}]' 2>/dev/null; then
60
- SUSPICIOUS=true
61
- REASON="Detected invisible Unicode characters"
62
- fi
63
-
64
- # Pattern 5: Base64-encoded instructions
65
- if echo "$CONTENT" | grep -qE '[A-Za-z0-9+/]{50,}={0,2}'; then
66
- # Only flag if it's in a suspicious context (not normal code)
67
- if echo "$CONTENT" | grep -iqE '(decode|eval|execute|base64).*[A-Za-z0-9+/]{50,}'; then
58
+ # Pattern 4: Invisible Unicode characters (macOS-compatible using perl)
59
+ if command -v perl >/dev/null 2>&1; then
60
+ if echo "$CONTENT" | perl -ne 'exit 1 if /[\x{200B}\x{200C}\x{200D}\x{FEFF}\x{202A}-\x{202E}\x{2066}-\x{2069}]/' 2>/dev/null; then
61
+ : # no match
62
+ else
68
63
  SUSPICIOUS=true
69
- REASON="Detected potentially encoded instructions"
64
+ REASON="Detected invisible Unicode characters"
70
65
  fi
71
66
  fi
72
67
 
68
+ # Pattern 5: Base64-encoded instructions in suspicious context
69
+ if echo "$CONTENT" | grep -iqE '(decode|eval|execute|base64).*[A-Za-z0-9+/]{50,}'; then
70
+ SUSPICIOUS=true
71
+ REASON="Detected potentially encoded instructions"
72
+ fi
73
+
73
74
  # Pattern 6: HTML/script injection in markdown
74
75
  if echo "$CONTENT" | grep -iqE '<script|javascript:|on(load|error|click)='; then
75
76
  SUSPICIOUS=true
@@ -1,7 +1,8 @@
1
1
  #!/bin/bash
2
- # GSD-CC Statusline — Notification hook
3
- # Renders project status in the terminal statusline.
4
- # Shows: current phase, milestone/slice/task position, and project type.
2
+ # GSD-CC Statusline — PostToolUse hook
3
+ # Injects current project status as additionalContext after each tool use.
4
+ # This keeps Claude aware of the current position in the project.
5
+ # Also writes a bridge file for other hooks to read.
5
6
 
6
7
  INPUT=$(cat)
7
8
  CWD=$(echo "$INPUT" | jq -r '.cwd')
@@ -12,12 +13,23 @@ if [ ! -f "$STATE_FILE" ]; then
12
13
  exit 0
13
14
  fi
14
15
 
16
+ # Debounce: only inject status every 10 tool calls
17
+ DEBOUNCE_FILE="/tmp/gsd-cc-statusline-$(echo "$CWD" | cksum | cut -d' ' -f1)"
18
+ COUNTER=0
19
+ if [ -f "$DEBOUNCE_FILE" ]; then
20
+ COUNTER=$(cat "$DEBOUNCE_FILE")
21
+ fi
22
+ COUNTER=$((COUNTER + 1))
23
+ echo "$COUNTER" > "$DEBOUNCE_FILE"
24
+ if [ $((COUNTER % 10)) -ne 0 ]; then
25
+ exit 0
26
+ fi
27
+
15
28
  # Parse STATE.md frontmatter
16
29
  PHASE=$(grep '^phase:' "$STATE_FILE" | head -1 | sed 's/phase: *//')
17
30
  MILESTONE=$(grep '^milestone:' "$STATE_FILE" | head -1 | sed 's/milestone: *//')
18
31
  SLICE=$(grep '^current_slice:' "$STATE_FILE" | head -1 | sed 's/current_slice: *//')
19
32
  TASK=$(grep '^current_task:' "$STATE_FILE" | head -1 | sed 's/current_task: *//')
20
- RIGOR=$(grep '^rigor:' "$STATE_FILE" | head -1 | sed 's/rigor: *//')
21
33
 
22
34
  # Build position string
23
35
  POSITION="$MILESTONE"
@@ -28,46 +40,20 @@ if [ "$TASK" != "—" ] && [ -n "$TASK" ]; then
28
40
  POSITION="$POSITION / $TASK"
29
41
  fi
30
42
 
31
- # Map phase to display name
32
- case "$PHASE" in
33
- "seed") PHASE_DISPLAY="Seed" ;;
34
- "seed-complete") PHASE_DISPLAY="Seed ✓" ;;
35
- "stack-complete") PHASE_DISPLAY="Stack ✓" ;;
36
- "roadmap-complete") PHASE_DISPLAY="Roadmap ✓" ;;
37
- "discuss-complete") PHASE_DISPLAY="Discuss ✓" ;;
38
- "plan-complete") PHASE_DISPLAY="Plan ✓" ;;
39
- "planning") PHASE_DISPLAY="Planning..." ;;
40
- "applying") PHASE_DISPLAY="Executing..." ;;
41
- "apply-complete") PHASE_DISPLAY="UNIFY required" ;;
42
- "unified") PHASE_DISPLAY="Unified ✓" ;;
43
- *) PHASE_DISPLAY="$PHASE" ;;
44
- esac
45
-
46
43
  # Count progress
47
44
  TOTAL_SLICES=$(grep -c '| S[0-9]' "$STATE_FILE" 2>/dev/null || echo "0")
48
- DONE_SLICES=$(grep '| done' "$STATE_FILE" | wc -l | xargs)
45
+ DONE_SLICES=$(grep '| done' "$STATE_FILE" 2>/dev/null | wc -l | xargs)
49
46
 
50
- # Write status to bridge file for other hooks
51
- BRIDGE_FILE="/tmp/gsd-cc-status-$(echo "$CWD" | md5sum 2>/dev/null | cut -c1-8 || echo "default").json"
47
+ # Write bridge file for other hooks
48
+ BRIDGE_FILE="/tmp/gsd-cc-bridge-$(echo "$CWD" | cksum | cut -d' ' -f1).json"
52
49
  jq -n \
53
50
  --arg phase "$PHASE" \
54
51
  --arg position "$POSITION" \
55
- --arg rigor "$RIGOR" \
56
52
  --arg total "$TOTAL_SLICES" \
57
53
  --arg done "$DONE_SLICES" \
58
- '{phase: $phase, position: $position, rigor: $rigor, total_slices: ($total|tonumber), done_slices: ($done|tonumber)}' \
54
+ '{phase: $phase, position: $position, total_slices: ($total|tonumber), done_slices: ($done|tonumber)}' \
59
55
  > "$BRIDGE_FILE" 2>/dev/null
60
56
 
61
- # Output statusline data
62
- jq -n \
63
- --arg pos "$POSITION" \
64
- --arg phase "$PHASE_DISPLAY" \
65
- --arg rigor "$RIGOR" \
66
- --arg progress "${DONE_SLICES}/${TOTAL_SLICES}" \
67
- '{
68
- "hookSpecificOutput": {
69
- "hookEventName": "Notification",
70
- "additionalContext": ("GSD-CC: " + $pos + " | " + $phase + " | " + $rigor + " | " + $progress + " slices")
71
- }
72
- }'
57
+ # No additionalContext output — this hook is silent.
58
+ # It only maintains the bridge file for cross-hook communication.
73
59
  exit 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gsd-cc",
3
- "version": "1.3.3",
3
+ "version": "1.4.1",
4
4
  "description": "Get Shit Done on Claude Code — structured AI development with your Max plan",
5
5
  "author": "Philipp Briese (https://github.com/0ui-labs)",
6
6
  "homepage": "https://github.com/0ui-labs/GSD-CC#readme",
@@ -202,8 +202,8 @@ One file per task, using the PLAN.xml template format:
202
202
 
203
203
  Before finishing, check against `checklists/planning-ready.md`:
204
204
 
205
- Read: `~/.claude/skills/gsd-cc-shared/checklists/planning-ready.md`
206
- (or `./.claude/skills/gsd-cc-shared/checklists/planning-ready.md`)
205
+ Read: `~/.claude/checklists/planning-ready.md`
206
+ (or `./.claude/checklists/planning-ready.md`)
207
207
 
208
208
  Verify ALL of these:
209
209
 
@@ -102,8 +102,8 @@ Walk through the guide sections **one at a time**. For each section:
102
102
 
103
103
  After completing all sections, mentally check against `checklists/planning-ready.md`:
104
104
 
105
- Read: `~/.claude/skills/gsd-cc-shared/checklists/planning-ready.md`
106
- (or `./.claude/skills/gsd-cc-shared/checklists/planning-ready.md`)
105
+ Read: `~/.claude/checklists/planning-ready.md`
106
+ (or `./.claude/checklists/planning-ready.md`)
107
107
 
108
108
  Verify:
109
109
  - Is there enough information to create a roadmap?
@@ -118,7 +118,7 @@ Create the `.gsd/` directory and write these files:
118
118
 
119
119
  #### `.gsd/PLANNING.md`
120
120
 
121
- Use the template from `~/.claude/skills/gsd-cc-shared/templates/PLANNING.md` (or `./.claude/skills/gsd-cc-shared/templates/PLANNING.md`). Fill in all sections from the conversation:
121
+ Use the template from `~/.claude/templates/PLANNING.md` (or `./.claude/templates/PLANNING.md`). Fill in all sections from the conversation:
122
122
  - Vision (from their initial description + refinements)
123
123
  - Users (from user/auth discussions)
124
124
  - Requirements v1, v2, Out of Scope (from exploration)
@@ -194,14 +194,14 @@ If no VISION.md exists, skip this step.
194
194
 
195
195
  Check against `checklists/unify-complete.md`:
196
196
 
197
- Read: `~/.claude/skills/gsd-cc-shared/checklists/unify-complete.md`
198
- (or `./.claude/skills/gsd-cc-shared/checklists/unify-complete.md`)
197
+ Read: `~/.claude/checklists/unify-complete.md`
198
+ (or `./.claude/checklists/unify-complete.md`)
199
199
 
200
200
  Verify ALL items pass. If any fails, fix the UNIFY document before proceeding.
201
201
 
202
202
  ## Step 9: Write UNIFY.md
203
203
 
204
- Write `.gsd/S{nn}-UNIFY.md` using the template from `~/.claude/skills/gsd-cc-shared/templates/UNIFY.md` (or `./.claude/skills/gsd-cc-shared/templates/UNIFY.md`). Include all sections from Steps 2-7.
204
+ Write `.gsd/S{nn}-UNIFY.md` using the template from `~/.claude/templates/UNIFY.md` (or `./.claude/templates/UNIFY.md`). Include all sections from Steps 2-7.
205
205
 
206
206
  Set frontmatter:
207
207
  ```yaml