gsd-cc 1.3.2 → 1.4.0

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
@@ -157,19 +157,21 @@ function install(isGlobal) {
157
157
 
158
158
  // 5. Install hooks
159
159
  const hooksSrc = path.join(__dirname, '..', 'hooks');
160
- const hooksDest = path.join(skillsBase, 'gsd-cc-shared', 'hooks');
160
+ const hooksBase = isGlobal
161
+ ? path.join(os.homedir(), '.claude', 'hooks')
162
+ : path.join(process.cwd(), '.claude', 'hooks');
161
163
  if (fs.existsSync(hooksSrc)) {
162
- copyDir(hooksSrc, hooksDest);
164
+ copyDir(hooksSrc, hooksBase);
163
165
  // Make hooks executable
164
- const hookFiles = fs.readdirSync(hooksDest);
166
+ const hookFiles = fs.readdirSync(hooksBase);
165
167
  for (const f of hookFiles) {
166
- fs.chmodSync(path.join(hooksDest, f), 0o755);
168
+ fs.chmodSync(path.join(hooksBase, f), 0o755);
167
169
  }
168
170
  fileCount += hookFiles.length;
169
171
  }
170
172
 
171
173
  // 6. Configure hooks in settings.json
172
- installHooks(isGlobal, hooksDest);
174
+ installHooks(isGlobal, hooksBase);
173
175
 
174
176
  console.log(` ${green}✓${reset} Installed ${fileCount} files to ${label}`);
175
177
  }
@@ -217,22 +219,19 @@ function installHooks(isGlobal, hooksDir) {
217
219
  ]
218
220
  });
219
221
 
220
- // PostToolUse: context monitor (all tools) + workflow guard (Edit/Write)
222
+ // PostToolUse: context monitor + statusline (all tools) + workflow guard (Edit/Write)
221
223
  if (!settings.hooks.PostToolUse) settings.hooks.PostToolUse = [];
222
224
  settings.hooks.PostToolUse.push({
223
- hooks: [{ type: 'command', command: contextMonitor, timeout: 5000 }]
225
+ hooks: [
226
+ { type: 'command', command: contextMonitor, timeout: 5000 },
227
+ { type: 'command', command: statusline, timeout: 3000 }
228
+ ]
224
229
  });
225
230
  settings.hooks.PostToolUse.push({
226
231
  matcher: 'Edit|Write',
227
232
  hooks: [{ type: 'command', command: workflowGuard, timeout: 5000 }]
228
233
  });
229
234
 
230
- // Notification: statusline
231
- if (!settings.hooks.Notification) settings.hooks.Notification = [];
232
- settings.hooks.Notification.push({
233
- hooks: [{ type: 'command', command: statusline, timeout: 3000 }]
234
- });
235
-
236
235
  fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
237
236
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
238
237
  console.log(` ${green}✓${reset} Hooks configured in ${settingsPath.replace(os.homedir(), '~').replace(process.cwd(), '.')}`);
@@ -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.2",
3
+ "version": "1.4.0",
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",