@rulemetric/hooks 0.7.27 → 0.7.28

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulemetric/hooks",
3
- "version": "0.7.27",
3
+ "version": "0.7.28",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -117,16 +117,46 @@ _rulemetric_ensure_session() {
117
117
  is_headless="true"
118
118
  fi
119
119
 
120
+ # Harness version, read from the transcript the hook was handed.
121
+ #
122
+ # `claudeCodeVersion` used to arrive ONLY on the SessionEnd transcript
123
+ # reimport, so any session that never ended cleanly — still running, crashed,
124
+ # or killed by a "Hook cancelled" — never got one. Measured 2026-07-27:
125
+ # harness_version was null on 96 of 326 claude_code sessions over 14 days
126
+ # (~30%), including long-lived live sessions, which is what blocks
127
+ # version-scoped segment comparisons (§6c.f attribution depth).
128
+ #
129
+ # Claude Code stamps `version` on every transcript entry from the first user
130
+ # message onward, so scanning the head of the file finds it. The first line is
131
+ # an `operation` record with no version, hence the scan rather than a
132
+ # first-line read. Reposts merge metadata server-side (jsonb ||), so if the
133
+ # transcript had no entries yet at SessionStart, a later hook call fills it in.
134
+ # Extracted with grep, NOT jq, on purpose: the bundled `jq` shim supports only
135
+ # the narrow filter subset already used here and returns EMPTY for anything
136
+ # else (verified — `select(.version != null) | .version` and even `.version`
137
+ # yield nothing). On a host without real jq that would silently capture no
138
+ # version at all, which is the exact failure being fixed. The semver-shaped
139
+ # pattern keeps prose in message content from matching.
140
+ local harness_version=""
141
+ if [ -n "${HOOK_TRANSCRIPT_PATH:-}" ] && [ -f "$HOOK_TRANSCRIPT_PATH" ]; then
142
+ harness_version=$(head -n 50 "$HOOK_TRANSCRIPT_PATH" 2>/dev/null \
143
+ | grep -oE '"version"[[:space:]]*:[[:space:]]*"[0-9]+\.[0-9]+[0-9A-Za-z.-]*"' \
144
+ | head -n 1 \
145
+ | sed -E 's/.*"([0-9][^"]*)"$/\1/') || harness_version=""
146
+ fi
147
+
120
148
  local metadata
121
149
  metadata=$(jq -n \
122
150
  --arg gitRemote "$git_remote" \
123
151
  --arg gitRootCommit "$git_root_commit" \
124
152
  --arg launchProjectPath "$launch_project_path" \
153
+ --arg claudeCodeVersion "$harness_version" \
125
154
  --argjson isWorktree "$is_worktree" \
126
155
  --argjson isHeadless "$is_headless" \
127
156
  '{} + (if $gitRemote != "" then {gitRemote: $gitRemote} else {} end)
128
157
  + (if $gitRootCommit != "" then {gitRootCommit: $gitRootCommit} else {} end)
129
158
  + (if $launchProjectPath != "" then {launchProjectPath: $launchProjectPath} else {} end)
159
+ + (if $claudeCodeVersion != "" then {claudeCodeVersion: $claudeCodeVersion} else {} end)
130
160
  + {isWorktree: $isWorktree, headless: $isHeadless}')
131
161
 
132
162
  # Optional org attribution (Phase 8.1). RULEMETRIC_ORG_ID is exported by