fix: session-activity-tracker never records (CLAUDE_SESSION_ID unset)

session-activity-tracker.js is correctly registered in
posttooluse-dispatcher.js's SYNC_HOOKS and fires on every tool call --
but buildActivityRow() only ever read the session id from
ECC_SESSION_ID/CLAUDE_SESSION_ID environment variables, which Claude
Code does not actually set for hook subprocesses. Every row silently
came back null and nothing was ever written to
~/.claude/metrics/tool-usage.jsonl, with no error anywhere.

The session id is available in the hook's own stdin JSON payload
(input.session_id) in real invocations. This prefers that, keeping the
env vars as a fallback. New test added; all 18 existing + new tests
pass.

Also adds a live Activity view to the capabilities dashboard
(scripts/dashboard-web.js) reading that same metrics file, so Skill
vs. Agent vs. raw-tool usage is visible in real time instead of
requiring a manual tail+jq -- verified live against a real session
before this commit. Documents the loopback-only, no-auth design and
the standard SSH-port-forwarding pattern for viewing it from another
device you control.
This commit is contained in:
Clovis
2026-09-05 16:58:17 -07:00
parent ec74a88644
commit cfca5c8153
3 changed files with 38 additions and 1 deletions
+9
View File
@@ -6,6 +6,15 @@
* Usage: node scripts/dashboard-web.js [port]
* Open http://localhost:3456
*
* The server only binds to loopback and has no authentication, by design --
* it is meant to be viewed on the same machine it runs on. To check it from
* another device you control (a tablet, a laptop), use standard SSH local
* port forwarding instead of exposing the port itself:
* ssh -L 3456:127.0.0.1:3456 <user>@<this-machine> -p <ssh-port>
* then open http://127.0.0.1:3456 on the *remote* device. This requires
* only SSH access you already control on your own machine -- nothing here
* opens the dashboard to the network.
*
* Contribution: https://github.com/affaan-m/ECC
*/
+7 -1
View File
@@ -567,7 +567,13 @@ function buildActivityRow(input, env = process.env) {
}
const toolName = String(input?.tool_name || '').trim();
const sessionId = String(env.ECC_SESSION_ID || env.CLAUDE_SESSION_ID || '').trim();
// Claude Code does not set CLAUDE_SESSION_ID as an environment variable for
// hook subprocesses -- the session id is only available in the hook's own
// stdin JSON payload. Prefer that; keep the env vars as a fallback for any
// caller that does set them.
const sessionId = String(
input?.session_id || env.ECC_SESSION_ID || env.CLAUDE_SESSION_ID || ''
).trim();
if (!toolName || !sessionId) {
return null;
}