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 +12 -13
- 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
|
@@ -157,19 +157,21 @@ function install(isGlobal) {
|
|
|
157
157
|
|
|
158
158
|
// 5. Install hooks
|
|
159
159
|
const hooksSrc = path.join(__dirname, '..', 'hooks');
|
|
160
|
-
const
|
|
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,
|
|
164
|
+
copyDir(hooksSrc, hooksBase);
|
|
163
165
|
// Make hooks executable
|
|
164
|
-
const hookFiles = fs.readdirSync(
|
|
166
|
+
const hookFiles = fs.readdirSync(hooksBase);
|
|
165
167
|
for (const f of hookFiles) {
|
|
166
|
-
fs.chmodSync(path.join(
|
|
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,
|
|
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: [
|
|
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 (
|
|
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",
|