gsd-cc 1.3.0 → 1.3.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
@@ -194,25 +194,31 @@ function installHooks(isGlobal, hooksDir) {
194
194
  if (!settings.hooks) settings.hooks = {};
195
195
 
196
196
  const boundaryGuard = path.join(hooksDir, 'gsd-boundary-guard.sh');
197
+ const promptGuard = path.join(hooksDir, 'gsd-prompt-guard.sh');
197
198
  const contextMonitor = path.join(hooksDir, 'gsd-context-monitor.sh');
198
199
  const workflowGuard = path.join(hooksDir, 'gsd-workflow-guard.sh');
200
+ const statusline = path.join(hooksDir, 'gsd-statusline.sh');
201
+
202
+ // Remove all existing GSD-CC hooks before adding (idempotent)
203
+ for (const event of Object.keys(settings.hooks)) {
204
+ settings.hooks[event] = settings.hooks[event].filter(
205
+ h => !JSON.stringify(h).includes('gsd-')
206
+ );
207
+ if (settings.hooks[event].length === 0) delete settings.hooks[event];
208
+ }
199
209
 
200
- // PreToolUse: boundary guard on Edit/Write
210
+ // PreToolUse: boundary guard + prompt injection guard on Edit/Write
201
211
  if (!settings.hooks.PreToolUse) settings.hooks.PreToolUse = [];
202
- // Remove existing GSD-CC hooks before adding (idempotent)
203
- settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter(
204
- h => !JSON.stringify(h).includes('gsd-boundary-guard')
205
- );
206
212
  settings.hooks.PreToolUse.push({
207
213
  matcher: 'Edit|Write',
208
- hooks: [{ type: 'command', command: boundaryGuard, timeout: 5000 }]
214
+ hooks: [
215
+ { type: 'command', command: boundaryGuard, timeout: 5000 },
216
+ { type: 'command', command: promptGuard, timeout: 5000 }
217
+ ]
209
218
  });
210
219
 
211
- // PostToolUse: context monitor + workflow guard
220
+ // PostToolUse: context monitor (all tools) + workflow guard (Edit/Write)
212
221
  if (!settings.hooks.PostToolUse) settings.hooks.PostToolUse = [];
213
- settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter(
214
- h => !JSON.stringify(h).includes('gsd-context-monitor') && !JSON.stringify(h).includes('gsd-workflow-guard')
215
- );
216
222
  settings.hooks.PostToolUse.push({
217
223
  hooks: [{ type: 'command', command: contextMonitor, timeout: 5000 }]
218
224
  });
@@ -221,6 +227,12 @@ function installHooks(isGlobal, hooksDir) {
221
227
  hooks: [{ type: 'command', command: workflowGuard, timeout: 5000 }]
222
228
  });
223
229
 
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
+
224
236
  fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
225
237
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
226
238
  console.log(` ${green}✓${reset} Hooks configured in ${settingsPath.replace(os.homedir(), '~').replace(process.cwd(), '.')}`);
@@ -0,0 +1,91 @@
1
+ #!/bin/bash
2
+ # GSD-CC Prompt Injection Guard — PreToolUse hook
3
+ # Scans Write/Edit operations targeting .gsd/ files for prompt injection patterns.
4
+ # Blocks suspicious content from being written into planning artifacts.
5
+
6
+ INPUT=$(cat)
7
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
8
+ CWD=$(echo "$INPUT" | jq -r '.cwd')
9
+
10
+ # Only check Edit and Write operations
11
+ if [ "$TOOL_NAME" != "Edit" ] && [ "$TOOL_NAME" != "Write" ]; then
12
+ exit 0
13
+ fi
14
+
15
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
16
+ if [ -z "$FILE_PATH" ]; then
17
+ exit 0
18
+ fi
19
+
20
+ # Only scan writes to .gsd/ directory (planning artifacts)
21
+ if [[ "$FILE_PATH" != *".gsd/"* ]]; then
22
+ exit 0
23
+ fi
24
+
25
+ # Get the content being written
26
+ if [ "$TOOL_NAME" = "Write" ]; then
27
+ CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // empty')
28
+ elif [ "$TOOL_NAME" = "Edit" ]; then
29
+ CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // empty')
30
+ fi
31
+
32
+ if [ -z "$CONTENT" ]; then
33
+ exit 0
34
+ fi
35
+
36
+ # Check for prompt injection patterns
37
+ SUSPICIOUS=false
38
+ REASON=""
39
+
40
+ # Pattern 1: Direct instruction override attempts
41
+ if echo "$CONTENT" | grep -iqE 'ignore (previous|prior|above|all) (instructions|prompts|rules)'; then
42
+ SUSPICIOUS=true
43
+ REASON="Detected 'ignore previous instructions' pattern"
44
+ fi
45
+
46
+ # Pattern 2: Role reassignment
47
+ if echo "$CONTENT" | grep -iqE '(you are now|act as|pretend to be|your new role|forget your|disregard your)'; then
48
+ SUSPICIOUS=true
49
+ REASON="Detected role reassignment pattern"
50
+ fi
51
+
52
+ # Pattern 3: System prompt extraction
53
+ if echo "$CONTENT" | grep -iqE '(show|reveal|print|output|display) (your|the|system) (prompt|instructions|rules)'; then
54
+ SUSPICIOUS=true
55
+ REASON="Detected system prompt extraction attempt"
56
+ fi
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
68
+ SUSPICIOUS=true
69
+ REASON="Detected potentially encoded instructions"
70
+ fi
71
+ fi
72
+
73
+ # Pattern 6: HTML/script injection in markdown
74
+ if echo "$CONTENT" | grep -iqE '<script|javascript:|on(load|error|click)='; then
75
+ SUSPICIOUS=true
76
+ REASON="Detected script injection in planning artifact"
77
+ fi
78
+
79
+ if [ "$SUSPICIOUS" = true ]; then
80
+ jq -n --arg reason "$REASON" --arg file "$FILE_PATH" '{
81
+ "hookSpecificOutput": {
82
+ "hookEventName": "PreToolUse",
83
+ "permissionDecision": "deny",
84
+ "permissionDecisionReason": ("PROMPT INJECTION BLOCKED: " + $reason + " in " + $file + ". This content cannot be written to planning artifacts. If this is a false positive, the user can write the file manually.")
85
+ }
86
+ }'
87
+ exit 0
88
+ fi
89
+
90
+ # Content is clean
91
+ exit 0
@@ -0,0 +1,73 @@
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.
5
+
6
+ INPUT=$(cat)
7
+ CWD=$(echo "$INPUT" | jq -r '.cwd')
8
+
9
+ # Only render if this is a GSD-CC project
10
+ STATE_FILE="$CWD/.gsd/STATE.md"
11
+ if [ ! -f "$STATE_FILE" ]; then
12
+ exit 0
13
+ fi
14
+
15
+ # Parse STATE.md frontmatter
16
+ PHASE=$(grep '^phase:' "$STATE_FILE" | head -1 | sed 's/phase: *//')
17
+ MILESTONE=$(grep '^milestone:' "$STATE_FILE" | head -1 | sed 's/milestone: *//')
18
+ SLICE=$(grep '^current_slice:' "$STATE_FILE" | head -1 | sed 's/current_slice: *//')
19
+ TASK=$(grep '^current_task:' "$STATE_FILE" | head -1 | sed 's/current_task: *//')
20
+ RIGOR=$(grep '^rigor:' "$STATE_FILE" | head -1 | sed 's/rigor: *//')
21
+
22
+ # Build position string
23
+ POSITION="$MILESTONE"
24
+ if [ "$SLICE" != "—" ] && [ -n "$SLICE" ]; then
25
+ POSITION="$POSITION / $SLICE"
26
+ fi
27
+ if [ "$TASK" != "—" ] && [ -n "$TASK" ]; then
28
+ POSITION="$POSITION / $TASK"
29
+ fi
30
+
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
+ # Count progress
47
+ TOTAL_SLICES=$(grep -c '| S[0-9]' "$STATE_FILE" 2>/dev/null || echo "0")
48
+ DONE_SLICES=$(grep '| done' "$STATE_FILE" | wc -l | xargs)
49
+
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"
52
+ jq -n \
53
+ --arg phase "$PHASE" \
54
+ --arg position "$POSITION" \
55
+ --arg rigor "$RIGOR" \
56
+ --arg total "$TOTAL_SLICES" \
57
+ --arg done "$DONE_SLICES" \
58
+ '{phase: $phase, position: $position, rigor: $rigor, total_slices: ($total|tonumber), done_slices: ($done|tonumber)}' \
59
+ > "$BRIDGE_FILE" 2>/dev/null
60
+
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
+ }'
73
+ exit 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gsd-cc",
3
- "version": "1.3.0",
3
+ "version": "1.3.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",