gsd-cc 1.3.3 → 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 +5 -8
- package/hooks/gsd-prompt-guard.sh +12 -11
- package/hooks/gsd-statusline.sh +22 -36
- package/package.json +1 -1
package/bin/install.js
CHANGED
|
@@ -219,22 +219,19 @@ function installHooks(isGlobal, hooksDir) {
|
|
|
219
219
|
]
|
|
220
220
|
});
|
|
221
221
|
|
|
222
|
-
// PostToolUse: context monitor (all tools) + workflow guard (Edit/Write)
|
|
222
|
+
// PostToolUse: context monitor + statusline (all tools) + workflow guard (Edit/Write)
|
|
223
223
|
if (!settings.hooks.PostToolUse) settings.hooks.PostToolUse = [];
|
|
224
224
|
settings.hooks.PostToolUse.push({
|
|
225
|
-
hooks: [
|
|
225
|
+
hooks: [
|
|
226
|
+
{ type: 'command', command: contextMonitor, timeout: 5000 },
|
|
227
|
+
{ type: 'command', command: statusline, timeout: 3000 }
|
|
228
|
+
]
|
|
226
229
|
});
|
|
227
230
|
settings.hooks.PostToolUse.push({
|
|
228
231
|
matcher: 'Edit|Write',
|
|
229
232
|
hooks: [{ type: 'command', command: workflowGuard, timeout: 5000 }]
|
|
230
233
|
});
|
|
231
234
|
|
|
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
235
|
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
|
|
239
236
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
240
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 (
|
|
59
|
-
if
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
|
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
|
package/hooks/gsd-statusline.sh
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# GSD-CC Statusline —
|
|
3
|
-
#
|
|
4
|
-
#
|
|
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
|
|
51
|
-
BRIDGE_FILE="/tmp/gsd-cc-
|
|
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,
|
|
54
|
+
'{phase: $phase, position: $position, total_slices: ($total|tonumber), done_slices: ($done|tonumber)}' \
|
|
59
55
|
> "$BRIDGE_FILE" 2>/dev/null
|
|
60
56
|
|
|
61
|
-
#
|
|
62
|
-
|
|
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
|
+
"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",
|