@rulemetric/hooks 0.7.41 → 0.7.43
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/package.json +1 -1
- package/scripts/session-start.sh +102 -12
package/package.json
CHANGED
package/scripts/session-start.sh
CHANGED
|
@@ -124,35 +124,124 @@ _rulemetric_suggest_instructions() {
|
|
|
124
124
|
# suggestions arrive with the actual rule body. The body is rendered to
|
|
125
125
|
# stdout, which Claude Code surfaces to the model as session context.
|
|
126
126
|
# Low-confidence suggestions still render as name+reason only.
|
|
127
|
+
# limit=10 (the API's clamp), not 3. The feed now carries THREE directions
|
|
128
|
+
# ranked by one score, and a trim scores 0.55 — below every add and every
|
|
129
|
+
# removal — so a shallow fetch would sort it off the end and the trim
|
|
130
|
+
# direction would appear to produce nothing. Fetching the maximum and capping
|
|
131
|
+
# per direction below is what keeps the weakest signal reachable. It is still
|
|
132
|
+
# possible for a project with >10 pending adds to crowd the trim out; that is
|
|
133
|
+
# accepted, it is the softest row in the feed.
|
|
134
|
+
# The feed carries BOTH directions ranked by one score,
|
|
135
|
+
# and a measured-harmful retirement can score up to 0.95 — so at limit=3 a bad
|
|
136
|
+
# week of grooming would evict every adoption proposal and the additive feed
|
|
137
|
+
# would silently go to zero. Over-fetch, then cap each direction separately
|
|
138
|
+
# below so neither can starve the other.
|
|
127
139
|
response="$(curl -s --noproxy '*' --max-time 4 \
|
|
128
|
-
"${api_url}/api/instruction-suggestions?projectPath=${encoded_path}&limit=
|
|
140
|
+
"${api_url}/api/instruction-suggestions?projectPath=${encoded_path}&limit=10&includeContent=true&groupByKind=true" \
|
|
129
141
|
-H "Authorization: Bearer ${auth_token}" 2>/dev/null)" || return 0
|
|
130
142
|
|
|
131
143
|
local count
|
|
132
144
|
count="$(printf '%s' "$response" | jq -r '.suggestions | length' 2>/dev/null)" || return 0
|
|
133
145
|
[ "$count" != "0" ] && [ "$count" != "null" ] && [ -n "$count" ] || return 0
|
|
134
146
|
|
|
135
|
-
#
|
|
147
|
+
# Per-direction index lists, capped independently. `kind` is absent on an older
|
|
148
|
+
# API, so an untagged row is an adoption proposal — the historical behaviour.
|
|
149
|
+
local add_idx rem_idx trim_idx render_idx
|
|
150
|
+
add_idx="$(printf '%s' "$response" | jq -r \
|
|
151
|
+
'[.suggestions | to_entries[] | select((.value.kind // "add") == "add") | .key][0:3] | .[]' 2>/dev/null)" || add_idx=""
|
|
152
|
+
rem_idx="$(printf '%s' "$response" | jq -r \
|
|
153
|
+
'[.suggestions | to_entries[] | select(.value.kind == "remove") | .key][0:2] | .[]' 2>/dev/null)" || rem_idx=""
|
|
154
|
+
trim_idx="$(printf '%s' "$response" | jq -r \
|
|
155
|
+
'[.suggestions | to_entries[] | select(.value.kind == "trim") | .key][0:1] | .[]' 2>/dev/null)" || trim_idx=""
|
|
156
|
+
render_idx="$(printf '%s\n%s\n%s\n' "$add_idx" "$rem_idx" "$trim_idx" | grep -v '^$' || true)"
|
|
157
|
+
[ -n "$render_idx" ] || return 0
|
|
158
|
+
|
|
159
|
+
# Count high-confidence rows among the ADDS WE WILL ACTUALLY RENDER. Counting
|
|
160
|
+
# the whole response over-counts now that the fetch pulls 6 to cap 3+2: the
|
|
161
|
+
# header announced "5 high-confidence instruction(s) included below" with 3
|
|
162
|
+
# below it. A header that miscounts what follows it is how a reader stops
|
|
163
|
+
# trusting the block.
|
|
136
164
|
local high_count
|
|
137
|
-
high_count="$(printf '%s' "$response" | jq -r
|
|
165
|
+
high_count="$(printf '%s' "$response" | jq -r \
|
|
166
|
+
'[[.suggestions | to_entries[] | select((.value.kind // "add") == "add")][0:3][] | select(.value.content != null)] | length' 2>/dev/null)" || high_count="0"
|
|
167
|
+
# ONE count for the shared "costing you" header. Counting only removals would
|
|
168
|
+
# under-report the moment a trim renders beneath the same heading — the same
|
|
169
|
+
# header-miscounts-its-own-list bug just fixed above, reintroduced by a third
|
|
170
|
+
# direction.
|
|
171
|
+
local rem_count trim_count cost_count
|
|
172
|
+
rem_count="$(printf '%s\n' "$rem_idx" | grep -c '[0-9]' 2>/dev/null)" || rem_count=0
|
|
173
|
+
trim_count="$(printf '%s\n' "$trim_idx" | grep -c '[0-9]' 2>/dev/null)" || trim_count=0
|
|
174
|
+
cost_count=$((rem_count + trim_count))
|
|
175
|
+
|
|
176
|
+
# The header describes the ADDITIVE rows only. `count` is now a mixed total, so
|
|
177
|
+
# using it here would announce "N instructions may help with this project" and
|
|
178
|
+
# then list retirements underneath it.
|
|
179
|
+
local add_count
|
|
180
|
+
add_count="$(printf '%s\n' "$add_idx" | grep -c '[0-9]' 2>/dev/null)" || add_count=0
|
|
138
181
|
|
|
139
182
|
printf '\n---\n'
|
|
140
183
|
if [ "$high_count" != "0" ] && [ -n "$high_count" ]; then
|
|
141
184
|
printf '[rulemetric] %s high-confidence instruction(s) included below as session context.\n' "$high_count"
|
|
142
185
|
printf 'Accept any of them permanently with the command shown under each one.\n'
|
|
143
|
-
|
|
144
|
-
printf '[rulemetric] %s instruction(s) may help with this project:\n' "$
|
|
186
|
+
elif [ "$add_count" != "0" ]; then
|
|
187
|
+
printf '[rulemetric] %s instruction(s) may help with this project:\n' "$add_count"
|
|
145
188
|
fi
|
|
146
189
|
|
|
147
|
-
local
|
|
148
|
-
|
|
149
|
-
|
|
190
|
+
local rendered_removal_header=0
|
|
191
|
+
local i
|
|
192
|
+
for i in $render_idx; do
|
|
193
|
+
local name reason score scope content sid kind tokens
|
|
150
194
|
name="$(printf '%s' "$response" | jq -r ".suggestions[$i].name" 2>/dev/null)" || break
|
|
151
195
|
reason="$(printf '%s' "$response" | jq -r ".suggestions[$i].reason" 2>/dev/null)" || break
|
|
152
196
|
score="$(printf '%s' "$response" | jq -r ".suggestions[$i].score" 2>/dev/null)" || break
|
|
153
197
|
scope="$(printf '%s' "$response" | jq -r ".suggestions[$i].instructionScope" 2>/dev/null)" || break
|
|
154
198
|
content="$(printf '%s' "$response" | jq -r ".suggestions[$i].content // empty" 2>/dev/null)" || content=""
|
|
155
199
|
sid="$(printf '%s' "$response" | jq -r ".suggestions[$i].id" 2>/dev/null)" || break
|
|
200
|
+
kind="$(printf '%s' "$response" | jq -r ".suggestions[$i].kind // \"add\"" 2>/dev/null)" || kind="add"
|
|
201
|
+
tokens="$(printf '%s' "$response" | jq -r ".suggestions[$i].estimatedTokens // empty" 2>/dev/null)" || tokens=""
|
|
202
|
+
|
|
203
|
+
if [ "$kind" = "trim" ]; then
|
|
204
|
+
# Shares the cost heading with retirements — both are "this is charging
|
|
205
|
+
# you context" — but never the verb. Neutral means no effect was
|
|
206
|
+
# DETECTED, which is not evidence the rule is worthless, so the ask is
|
|
207
|
+
# "shorten", never "delete".
|
|
208
|
+
if [ "$rendered_removal_header" = "0" ]; then
|
|
209
|
+
printf '\n[rulemetric] %s instruction(s) look like they are costing you more than they return:\n' "$cost_count"
|
|
210
|
+
rendered_removal_header=1
|
|
211
|
+
fi
|
|
212
|
+
if [ -n "$tokens" ] && [ "$tokens" != "null" ]; then
|
|
213
|
+
printf ' - TRIM [%s] %s (~%s tokens/session, measures neutral)\n %s\n' "$scope" "$name" "$tokens" "$reason"
|
|
214
|
+
else
|
|
215
|
+
printf ' - TRIM [%s] %s (measures neutral)\n %s\n' "$scope" "$name" "$reason"
|
|
216
|
+
fi
|
|
217
|
+
if [ -n "$sid" ] && [ "$sid" != "null" ]; then
|
|
218
|
+
printf ' Acknowledge: rulemetric suggestions accept %s (no automatic change; shorten it yourself)\n' "$sid"
|
|
219
|
+
fi
|
|
220
|
+
continue
|
|
221
|
+
fi
|
|
222
|
+
|
|
223
|
+
if [ "$kind" = "remove" ]; then
|
|
224
|
+
# Retirements get their own labelled section and their own verb. Rendered
|
|
225
|
+
# in the additive shape they would read as "adopt this" to both the human
|
|
226
|
+
# and the model — the same text that has always meant "add" would now
|
|
227
|
+
# sometimes mean "delete", which is how someone archives a rule they meant
|
|
228
|
+
# to keep. The body is deliberately NOT here: the API withholds content for
|
|
229
|
+
# this direction, so proposing a retirement never pays the cost of the rule
|
|
230
|
+
# it is proposing to retire.
|
|
231
|
+
if [ "$rendered_removal_header" = "0" ]; then
|
|
232
|
+
printf '\n[rulemetric] %s instruction(s) look like they are costing you more than they return:\n' "$cost_count"
|
|
233
|
+
rendered_removal_header=1
|
|
234
|
+
fi
|
|
235
|
+
if [ -n "$tokens" ] && [ "$tokens" != "null" ]; then
|
|
236
|
+
printf ' - RETIRE [%s] %s (~%s tokens/session)\n %s\n' "$scope" "$name" "$tokens" "$reason"
|
|
237
|
+
else
|
|
238
|
+
printf ' - RETIRE [%s] %s\n %s\n' "$scope" "$name" "$reason"
|
|
239
|
+
fi
|
|
240
|
+
if [ -n "$sid" ] && [ "$sid" != "null" ]; then
|
|
241
|
+
printf ' Retire it: rulemetric suggestions accept %s (archives it; undo: rulemetric instructions restore <instruction-id>)\n' "$sid"
|
|
242
|
+
fi
|
|
243
|
+
continue
|
|
244
|
+
fi
|
|
156
245
|
|
|
157
246
|
if [ -n "$content" ]; then
|
|
158
247
|
printf '\n## Skill: %s (%s, score: %s)\n%s\n\n%s\n' "$name" "$scope" "$score" "$reason" "$content"
|
|
@@ -167,16 +256,18 @@ _rulemetric_suggest_instructions() {
|
|
|
167
256
|
if [ -n "$sid" ] && [ "$sid" != "null" ]; then
|
|
168
257
|
printf ' Accept: rulemetric suggestions accept %s\n' "$sid"
|
|
169
258
|
fi
|
|
170
|
-
i=$((i + 1))
|
|
171
259
|
done
|
|
172
260
|
|
|
173
261
|
printf '\n---\n'
|
|
174
262
|
|
|
175
263
|
# Log surfaced events fire-and-forget. Event type differs based on whether
|
|
176
264
|
# the full content was included so we can later measure outcome differences.
|
|
177
|
-
|
|
265
|
+
# Only the rows actually rendered. Iterating 0..count would log a `surfaced`
|
|
266
|
+
# event for suggestions the over-fetch pulled but the per-direction caps never
|
|
267
|
+
# showed — inflating the denominator of the exact acceptance rate this feed
|
|
268
|
+
# exists to measure.
|
|
178
269
|
local tool="${HOOK_TOOL:-unknown}"
|
|
179
|
-
|
|
270
|
+
for i in $render_idx; do
|
|
180
271
|
local sid has_content event_type
|
|
181
272
|
sid="$(printf '%s' "$response" | jq -r ".suggestions[$i].id" 2>/dev/null)" || break
|
|
182
273
|
has_content="$(printf '%s' "$response" | jq -r ".suggestions[$i].content // \"null\"" 2>/dev/null)" || has_content="null"
|
|
@@ -200,7 +291,6 @@ _rulemetric_suggest_instructions() {
|
|
|
200
291
|
'{eventType: $eventType, tool: $tool, projectPath: $projectPath, sessionId: $sessionId}')" \
|
|
201
292
|
>/dev/null 2>&1 &
|
|
202
293
|
disown 2>/dev/null || true
|
|
203
|
-
i=$((i + 1))
|
|
204
294
|
done
|
|
205
295
|
}
|
|
206
296
|
|