Skip to content

Commit f2da367

Browse files
authored
Merge pull request #8 from DojoCodingLabs/daniel/doj-2433-smart-filtering
feat: smart filtering with rate limits (DOJ-2433)
2 parents a7359cd + de50308 commit f2da367

File tree

3 files changed

+124
-5
lines changed

3 files changed

+124
-5
lines changed

scripts/session-stop.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ else
1919
fi
2020

2121
SESSION_LOG="${PROFILE_DIR}/sessions.log"
22+
SESSION_STATE="${PROFILE_DIR}/session-state.json"
2223
TODAY=$(date -u +%Y-%m-%d)
2324

2425
if [ ! -f "$PROFILE_FILE" ]; then
@@ -62,4 +63,7 @@ if [ "$SESSION_CONCEPTS" -gt 0 ]; then
6263
echo "You encountered $SESSION_CONCEPTS new concepts this session! Use /code-sensei:recap next time for a full summary."
6364
fi
6465

66+
rm -f "$SESSION_STATE"
67+
rm -f "$PROFILE_DIR/.jq-warned"
68+
6569
exit 0

scripts/track-code-change.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
1010
source "${SCRIPT_DIR}/lib/profile-io.sh"
1111

1212
CHANGES_LOG="${PROFILE_DIR}/session-changes.jsonl"
13+
SESSION_STATE="${PROFILE_DIR}/session-state.json"
14+
RATE_LIMIT_INTERVAL=30
15+
SESSION_CAP=12
1316

1417
LIB_DIR="${SCRIPT_DIR}/lib"
1518
if [ -f "${LIB_DIR}/error-handling.sh" ]; then
@@ -113,6 +116,51 @@ if [ -f "$PROFILE_FILE" ] && [ "$TECH" != "other" ]; then
113116
fi
114117
fi
115118

119+
if [ "$TECH" = "other" ]; then
120+
printf '{}\n'
121+
exit 0
122+
fi
123+
124+
NOW=$(date +%s)
125+
if [ -f "$SESSION_STATE" ]; then
126+
LAST_TRIGGER=$(jq -r '.last_trigger_time // 0' "$SESSION_STATE" 2>/dev/null || echo "0")
127+
TRIGGER_COUNT=$(jq -r '.trigger_count // 0' "$SESSION_STATE" 2>/dev/null || echo "0")
128+
else
129+
LAST_TRIGGER=0
130+
TRIGGER_COUNT=0
131+
fi
132+
133+
if [ "$TRIGGER_COUNT" -ge "$SESSION_CAP" ]; then
134+
printf '{}\n'
135+
exit 0
136+
fi
137+
138+
ELAPSED=$((NOW - LAST_TRIGGER))
139+
if [ "$ELAPSED" -lt "$RATE_LIMIT_INTERVAL" ] && [ "$IS_FIRST_EVER" != "true" ]; then
140+
printf '{}\n'
141+
exit 0
142+
fi
143+
144+
NEW_COUNT=$((TRIGGER_COUNT + 1))
145+
SESSION_START_VAL=""
146+
if [ -f "$SESSION_STATE" ]; then
147+
SESSION_START_VAL=$(jq -r '.session_start // ""' "$SESSION_STATE" 2>/dev/null || echo "")
148+
fi
149+
if [ -z "$SESSION_START_VAL" ]; then
150+
SESSION_START_VAL="$TIMESTAMP"
151+
fi
152+
153+
if ! jq -n \
154+
--argjson last "$NOW" \
155+
--argjson count "$NEW_COUNT" \
156+
--arg start "$SESSION_START_VAL" \
157+
'{"last_trigger_time": $last, "trigger_count": $count, "session_start": $start}' > "$SESSION_STATE"
158+
then
159+
log_error "$SCRIPT_NAME" "Failed to update session state: $SESSION_STATE"
160+
printf '{}\n'
161+
exit 0
162+
fi
163+
116164
BELT=$(jq -r '.belt // "white"' "$PROFILE_FILE" 2>&1)
117165
if [ $? -ne 0 ]; then
118166
log_error "$SCRIPT_NAME" "jq failed reading belt: $BELT"

scripts/track-command.sh

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
1010
source "${SCRIPT_DIR}/lib/profile-io.sh"
1111

1212
COMMANDS_LOG="${PROFILE_DIR}/session-commands.jsonl"
13+
SESSION_STATE="${PROFILE_DIR}/session-state.json"
14+
RATE_LIMIT_INTERVAL=30
15+
SESSION_CAP=12
16+
TRIVIAL_COMMANDS="cd ls pwd clear echo cat which man help exit history alias type file wc whoami hostname uname true false"
1317

1418
LIB_DIR="${SCRIPT_DIR}/lib"
1519
if [ -f "${LIB_DIR}/error-handling.sh" ]; then
@@ -48,6 +52,26 @@ if [ $? -ne 0 ]; then
4852
fi
4953

5054
TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
55+
BASE_CMD=$(printf '%s' "$COMMAND" | sed 's/^[[:space:]]*//' | awk '{print $1}' | sed 's|.*/||')
56+
57+
IS_TRIVIAL="false"
58+
for trivial in $TRIVIAL_COMMANDS; do
59+
if [ "$BASE_CMD" = "$trivial" ]; then
60+
IS_TRIVIAL="true"
61+
break
62+
fi
63+
done
64+
65+
if [ "$IS_TRIVIAL" = "true" ]; then
66+
SAFE_LOG_CMD=$(printf '%s' "$COMMAND" | head -c 200 | sed 's/\\/\\\\/g; s/"/\\"/g')
67+
if ! printf '{"timestamp":"%s","command":"%s","concept":"","skipped":"trivial"}\n' \
68+
"$TIMESTAMP" "$SAFE_LOG_CMD" >> "$COMMANDS_LOG" 2>&1
69+
then
70+
log_error "$SCRIPT_NAME" "Failed to write trivial command log: $COMMANDS_LOG"
71+
fi
72+
printf '{}\n'
73+
exit 0
74+
fi
5175

5276
CONCEPT=""
5377
case "$COMMAND" in
@@ -80,8 +104,13 @@ then
80104
log_error "$SCRIPT_NAME" "Failed to write to commands log: $COMMANDS_LOG"
81105
fi
82106

107+
if [ -z "$CONCEPT" ]; then
108+
printf '{}\n'
109+
exit 0
110+
fi
111+
83112
IS_FIRST_EVER="false"
84-
if [ -n "$CONCEPT" ] && [ -f "$PROFILE_FILE" ]; then
113+
if [ -f "$PROFILE_FILE" ]; then
85114
ALREADY_IN_SESSION=$(jq --arg c "$CONCEPT" '.session_concepts | index($c)' "$PROFILE_FILE" 2>&1)
86115
if [ $? -ne 0 ]; then
87116
log_error "$SCRIPT_NAME" "jq failed checking session_concepts for $CONCEPT: $ALREADY_IN_SESSION"
@@ -110,6 +139,46 @@ if [ -n "$CONCEPT" ] && [ -f "$PROFILE_FILE" ]; then
110139
fi
111140
fi
112141

142+
NOW=$(date +%s)
143+
if [ -f "$SESSION_STATE" ]; then
144+
LAST_TRIGGER=$(jq -r '.last_trigger_time // 0' "$SESSION_STATE" 2>/dev/null || echo "0")
145+
TRIGGER_COUNT=$(jq -r '.trigger_count // 0' "$SESSION_STATE" 2>/dev/null || echo "0")
146+
else
147+
LAST_TRIGGER=0
148+
TRIGGER_COUNT=0
149+
fi
150+
151+
if [ "$TRIGGER_COUNT" -ge "$SESSION_CAP" ]; then
152+
printf '{}\n'
153+
exit 0
154+
fi
155+
156+
ELAPSED=$((NOW - LAST_TRIGGER))
157+
if [ "$ELAPSED" -lt "$RATE_LIMIT_INTERVAL" ] && [ "$IS_FIRST_EVER" != "true" ]; then
158+
printf '{}\n'
159+
exit 0
160+
fi
161+
162+
NEW_COUNT=$((TRIGGER_COUNT + 1))
163+
SESSION_START_VAL=""
164+
if [ -f "$SESSION_STATE" ]; then
165+
SESSION_START_VAL=$(jq -r '.session_start // ""' "$SESSION_STATE" 2>/dev/null || echo "")
166+
fi
167+
if [ -z "$SESSION_START_VAL" ]; then
168+
SESSION_START_VAL="$TIMESTAMP"
169+
fi
170+
171+
if ! jq -n \
172+
--argjson last "$NOW" \
173+
--argjson count "$NEW_COUNT" \
174+
--arg start "$SESSION_START_VAL" \
175+
'{"last_trigger_time": $last, "trigger_count": $count, "session_start": $start}' > "$SESSION_STATE"
176+
then
177+
log_error "$SCRIPT_NAME" "Failed to update session state: $SESSION_STATE"
178+
printf '{}\n'
179+
exit 0
180+
fi
181+
113182
BELT=$(jq -r '.belt // "white"' "$PROFILE_FILE" 2>&1)
114183
if [ $? -ne 0 ]; then
115184
log_error "$SCRIPT_NAME" "jq failed reading belt: $BELT"
@@ -118,12 +187,10 @@ fi
118187

119188
SAFE_CMD=$(printf '%s' "$COMMAND" | head -c 80 | tr '"' "'" | tr '\\' '/')
120189

121-
if [ "$IS_FIRST_EVER" = "true" ] && [ -n "$CONCEPT" ]; then
190+
if [ "$IS_FIRST_EVER" = "true" ]; then
122191
CONTEXT="🥋 CodeSensei micro-lesson trigger: The user just encountered '$CONCEPT' for the FIRST TIME (command: $SAFE_CMD). Their belt level is '$BELT'. Provide a brief 2-sentence explanation of what $CONCEPT means and why it matters. Adapt language to their belt level. Keep it concise and non-intrusive."
123-
elif [ -n "$CONCEPT" ]; then
124-
CONTEXT="🥋 CodeSensei inline insight: Claude just ran a '$CONCEPT' command ($SAFE_CMD). The user's belt level is '$BELT'. Provide a brief 1-sentence explanation of what this command does, adapted to their belt level. Keep it natural and non-intrusive."
125192
else
126-
CONTEXT="🥋 CodeSensei inline insight: Claude just ran a shell command ($SAFE_CMD). The user's belt level is '$BELT'. If this command is educational, briefly explain what it does in 1 sentence. If trivial, skip the explanation."
193+
CONTEXT="🥋 CodeSensei inline insight: Claude just ran a '$CONCEPT' command ($SAFE_CMD). The user's belt level is '$BELT'. Provide a brief 1-sentence explanation of what this command does, adapted to their belt level. Keep it natural and non-intrusive."
127194
fi
128195

129196
ESCAPED_CONTEXT=$(json_escape "$CONTEXT")

0 commit comments

Comments
 (0)