ai-lens 0.8.103 → 0.8.105

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/.commithash CHANGED
@@ -1 +1 @@
1
- 0aee413
1
+ 0a107d0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  History of changes to the `ai-lens` CLI package on npm. New entries go on top. Format: `## X.Y.Z — YYYY-MM-DD`, followed by user-facing bullets.
4
4
 
5
+ ## 0.8.105 — 2026-06-18
6
+ - fix: Cyrillic (and other non-ASCII) in Claude Code hook payloads is no longer mangled on Windows. The windowless hook launcher read stdin through PowerShell's console codepage (OEM, e.g. CP866 on Russian Windows), corrupting prompts/paths on the way in; it now reads the raw stdin bytes and decodes UTF-8 directly. The earlier 0.8.102 fix only covered the write side, so machines that adopted the windowless launcher saw the corruption reappear.
7
+
8
+ ## 0.8.104 — 2026-06-18
9
+ - fix: subagent token usage is now captured for Claude Code. Tokens spent by Task / general-purpose subagents were silently dropped, so subagent-heavy sessions were badly undercounted — only the main thread's tokens showed up. Capture now reads each subagent's own transcript on `SubagentStop` instead of re-reading the parent session's.
10
+
5
11
  ## 0.8.103 — 2026-06-18
6
12
  - fix: `ai-lens status` no longer falsely reports the client as "outdated" on repo-path installs (where hooks run capture.js straight from a checkout that auto-updates on `git pull`). It now reads the client version from the repo the hooks actually run, instead of an unused leftover copy in `~/.ai-lens/client/` — which could be months stale and made the check show ✗ + "run init" even though the live client was current. The stale copy is now noted as ignored. Copy-mode installs are unchanged.
7
13
 
@@ -13,8 +13,16 @@
13
13
  #
14
14
  # Payload source differs by caller, so read BOTH:
15
15
  # - Cursor pipes it through the PowerShell pipeline → $input.
16
- # - Claude Code pipes it to the process's OS stdin → [Console]::In.
17
- # Read $input first; if empty (Claude Code invocation), fall back to [Console]::In.
16
+ # - Claude Code pipes it to the process's OS stdin → read it as raw bytes.
17
+ # Read $input first; if empty (Claude Code invocation), read the OS stdin stream.
18
+ #
19
+ # Why raw bytes (not [Console]::In.ReadToEnd()): [Console]::In decodes stdin via
20
+ # [Console]::InputEncoding, which on Windows PowerShell 5.1 defaults to the OEM
21
+ # codepage (e.g. CP866 on Russian Windows) — so Cyrillic/non-ASCII in the JSON
22
+ # payload is mangled at READ time, before we ever re-encode to UTF-8 on the way
23
+ # to node. Reading OpenStandardInput() as bytes and decoding UTF-8 ourselves
24
+ # bypasses the console codepage entirely. (The 0.8.102 fix only covered the WRITE
25
+ # side; the read side still mangled — observed live as mojibake'd prompts.)
18
26
  #
19
27
  # Args: $args[0] = node path, $args[1] = capture.js path.
20
28
 
@@ -25,7 +33,10 @@ try {
25
33
 
26
34
  $payload = @($input) -join "`n"
27
35
  if ([string]::IsNullOrEmpty($payload)) {
28
- $payload = [Console]::In.ReadToEnd()
36
+ $stdin = [Console]::OpenStandardInput()
37
+ $ms = New-Object System.IO.MemoryStream
38
+ $stdin.CopyTo($ms)
39
+ $payload = [System.Text.Encoding]::UTF8.GetString($ms.ToArray())
29
40
  }
30
41
 
31
42
  $psi = New-Object System.Diagnostics.ProcessStartInfo
package/client/capture.js CHANGED
@@ -892,12 +892,20 @@ function normalizeClaudeCode(event) {
892
892
  // TokenUsage event per real assistant API call. This is what makes Claude
893
893
  // Code's row-per-call granularity match Cursor and Codex.
894
894
  //
895
- // Claude Code's Stop hook passes the path as `transcript_path`, but
896
- // SubagentStop uses `agent_transcript_path` (see Claude Code hook docs and
897
- // test/fixtures/claude-code-events/subagent-stop.json). Check both so
898
- // subagent token usage is not silently dropped in production.
895
+ // Claude Code's Stop hook passes the path as `transcript_path`. SubagentStop
896
+ // carries BOTH `transcript_path` (the parent session's transcript) AND
897
+ // `agent_transcript_path` (the subagent's own transcript, a separate file in
898
+ // a `subagents/` subdir — the subagent's API calls live ONLY there, not in
899
+ // the parent transcript). So for SubagentStop we MUST prefer
900
+ // `agent_transcript_path`: a `transcript_path || agent_transcript_path`
901
+ // precedence always picks the parent transcript (whose delta is already
902
+ // consumed) and silently drops every subagent call — i.e. all the tokens
903
+ // burned by Task/general-purpose subagents. See
904
+ // test/fixtures/claude-code-events/subagent-stop.json for a real payload.
899
905
  if (hookType === 'Stop' || hookType === 'SubagentStop') {
900
- const transcriptPath = event.transcript_path || event.agent_transcript_path;
906
+ const transcriptPath = hookType === 'SubagentStop'
907
+ ? (event.agent_transcript_path || event.transcript_path)
908
+ : (event.transcript_path || event.agent_transcript_path);
901
909
  const { calls, commitOffset } = extractNewClaudeApiCallsFromTranscript(transcriptPath);
902
910
  const tokenEvents = calls.map(call => ({
903
911
  event_id: null, // assigned in main() from a stable hash of call.uuid
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-lens",
3
- "version": "0.8.103",
3
+ "version": "0.8.105",
4
4
  "type": "module",
5
5
  "description": "Centralized session analytics for AI coding tools",
6
6
  "bin": {