session-intelligence-cli 0.1.2 → 0.1.4
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/README.md +67 -0
- package/dist/report-bg.d.ts +2 -0
- package/dist/report-bg.js +61 -0
- package/dist/report.js +27 -43
- package/dist/setup.js +1 -0
- package/package.json +17 -3
package/README.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Session Intelligence CLI
|
|
2
|
+
|
|
3
|
+
A [Claude Code](https://docs.anthropic.com/en/docs/claude-code) plugin that gives you a real-time dashboard for all your coding sessions.
|
|
4
|
+
|
|
5
|
+
See what every session is doing, track token usage and git changes, and get a full activity history across all your projects.
|
|
6
|
+
|
|
7
|
+
**[session-intelligence.com](https://www.session-intelligence.com)**
|
|
8
|
+
|
|
9
|
+
## What it does
|
|
10
|
+
|
|
11
|
+
- **Live session tracking** — see every session as it thinks, runs tools, or waits for input
|
|
12
|
+
- **Activity heatmap & stats** — tokens, lines changed, model usage, and streaks over time
|
|
13
|
+
- **GitHub integration** — auto-links sessions to PRs, tracks open issues and review requests
|
|
14
|
+
- **Multi-project** — one dashboard for all your repos and branches
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx session-intelligence-cli setup
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
This will:
|
|
23
|
+
|
|
24
|
+
1. Ask for your **server URL** and **API key** (get one at [session-intelligence.com](https://www.session-intelligence.com))
|
|
25
|
+
2. Save config to `~/.session-intelligence/config.json`
|
|
26
|
+
3. Register hooks in `~/.claude/settings.json`
|
|
27
|
+
|
|
28
|
+
Your next Claude Code session will start reporting automatically.
|
|
29
|
+
|
|
30
|
+
## How it works
|
|
31
|
+
|
|
32
|
+
The CLI registers lightweight [Claude Code hooks](https://docs.anthropic.com/en/docs/claude-code/hooks) that fire on session lifecycle events:
|
|
33
|
+
|
|
34
|
+
| Event | What it reports |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `SessionStart` | New session created |
|
|
37
|
+
| `PostToolUse` | Tool activity (Read, Edit, Bash, etc.) |
|
|
38
|
+
| `Notification` | Permission requests / input needed |
|
|
39
|
+
| `SubagentStop` | Subagent task completion |
|
|
40
|
+
| `Stop` | Task turn completed |
|
|
41
|
+
| `SessionEnd` | Session archived with token usage summary |
|
|
42
|
+
|
|
43
|
+
Each hook reads the current git state (branch, last commit, uncommitted changes) and sends a small payload to your dashboard. All calls are fire-and-forget with no impact on Claude Code performance.
|
|
44
|
+
|
|
45
|
+
## Updating
|
|
46
|
+
|
|
47
|
+
If you installed a previous version, re-run setup to register any new hooks:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npx session-intelligence-cli@latest setup
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Commands
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
session-intelligence setup # configure and install hooks
|
|
57
|
+
session-intelligence status # check connection and config
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Requirements
|
|
61
|
+
|
|
62
|
+
- Node.js >= 18
|
|
63
|
+
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) installed
|
|
64
|
+
|
|
65
|
+
## License
|
|
66
|
+
|
|
67
|
+
MIT
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { readGitState, readGitDiffStats } from "./git.js";
|
|
4
|
+
import { sendEvent } from "./api.js";
|
|
5
|
+
// Called as: node report-bg.js <json-payload>
|
|
6
|
+
// Runs detached from the parent process to avoid being killed on session teardown.
|
|
7
|
+
const raw = process.argv[2];
|
|
8
|
+
if (!raw)
|
|
9
|
+
process.exit(0);
|
|
10
|
+
const stdinData = JSON.parse(raw);
|
|
11
|
+
const sessionId = stdinData.session_id ?? "";
|
|
12
|
+
const cwd = stdinData.cwd ?? process.cwd();
|
|
13
|
+
const payload = {
|
|
14
|
+
session_id: sessionId,
|
|
15
|
+
cwd,
|
|
16
|
+
hook_event_name: "SessionEnd",
|
|
17
|
+
model: stdinData.model,
|
|
18
|
+
};
|
|
19
|
+
const transcriptPath = stdinData.transcript_path;
|
|
20
|
+
if (transcriptPath) {
|
|
21
|
+
try {
|
|
22
|
+
const content = readFileSync(transcriptPath, "utf-8");
|
|
23
|
+
const usage = { input_tokens: 0, output_tokens: 0, cache_read_tokens: 0, cache_creation_tokens: 0 };
|
|
24
|
+
for (const line of content.split("\n")) {
|
|
25
|
+
const trimmed = line.trim();
|
|
26
|
+
if (!trimmed)
|
|
27
|
+
continue;
|
|
28
|
+
try {
|
|
29
|
+
const entry = JSON.parse(trimmed);
|
|
30
|
+
if (entry.type !== "assistant" || !entry.message?.usage)
|
|
31
|
+
continue;
|
|
32
|
+
const u = entry.message.usage;
|
|
33
|
+
usage.input_tokens += u.input_tokens ?? 0;
|
|
34
|
+
usage.output_tokens += u.output_tokens ?? 0;
|
|
35
|
+
usage.cache_read_tokens += u.cache_read_input_tokens ?? 0;
|
|
36
|
+
usage.cache_creation_tokens += u.cache_creation_input_tokens ?? 0;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// skip malformed lines
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
payload.usage = usage;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// transcript unreadable
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const git = readGitState(cwd);
|
|
49
|
+
if (git) {
|
|
50
|
+
const stats = readGitDiffStats(cwd);
|
|
51
|
+
payload.git = {
|
|
52
|
+
branch: git.branch,
|
|
53
|
+
lastCommitSha: git.lastCommitSha,
|
|
54
|
+
lastCommitMessage: git.lastCommitMessage,
|
|
55
|
+
hasUncommittedChanges: git.hasUncommittedChanges,
|
|
56
|
+
filesChanged: stats.filesChanged,
|
|
57
|
+
insertions: stats.insertions,
|
|
58
|
+
deletions: stats.deletions,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
await sendEvent(payload);
|
package/dist/report.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
2
4
|
import { readGitState, readGitDiffStats } from "./git.js";
|
|
3
5
|
import { sendEvent } from "./api.js";
|
|
4
6
|
const EVENT_MAP = {
|
|
@@ -9,47 +11,35 @@ const EVENT_MAP = {
|
|
|
9
11
|
"notification": "Notification",
|
|
10
12
|
"subagent-stop": "SubagentStop",
|
|
11
13
|
};
|
|
12
|
-
function
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const trimmed = line.trim();
|
|
18
|
-
if (!trimmed)
|
|
19
|
-
continue;
|
|
14
|
+
function readStdin() {
|
|
15
|
+
return new Promise((resolve) => {
|
|
16
|
+
const chunks = [];
|
|
17
|
+
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
18
|
+
process.stdin.on("end", () => {
|
|
20
19
|
try {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
continue;
|
|
24
|
-
const u = entry.message.usage;
|
|
25
|
-
zero.input_tokens += u.input_tokens ?? 0;
|
|
26
|
-
zero.output_tokens += u.output_tokens ?? 0;
|
|
27
|
-
zero.cache_read_tokens += u.cache_read_input_tokens ?? 0;
|
|
28
|
-
zero.cache_creation_tokens += u.cache_creation_input_tokens ?? 0;
|
|
20
|
+
const raw = Buffer.concat(chunks).toString("utf-8").trim();
|
|
21
|
+
resolve(raw ? JSON.parse(raw) : {});
|
|
29
22
|
}
|
|
30
23
|
catch {
|
|
31
|
-
|
|
24
|
+
resolve({});
|
|
32
25
|
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
// file not found or unreadable
|
|
37
|
-
}
|
|
38
|
-
return zero;
|
|
26
|
+
});
|
|
27
|
+
process.stdin.on("error", () => resolve({}));
|
|
28
|
+
});
|
|
39
29
|
}
|
|
40
30
|
export async function report(eventType) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
31
|
+
const stdinData = await readStdin();
|
|
32
|
+
// For session-end, spawn a detached background process so the hook
|
|
33
|
+
// can exit immediately and avoid being killed during session teardown.
|
|
34
|
+
if (eventType === "session-end") {
|
|
35
|
+
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
|
36
|
+
const bgScript = join(scriptDir, "report-bg.js");
|
|
37
|
+
const child = spawn(process.execPath, [bgScript, JSON.stringify(stdinData)], {
|
|
38
|
+
detached: true,
|
|
39
|
+
stdio: "ignore",
|
|
40
|
+
});
|
|
41
|
+
child.unref();
|
|
42
|
+
return;
|
|
53
43
|
}
|
|
54
44
|
const sessionId = stdinData.session_id ?? "";
|
|
55
45
|
const cwd = stdinData.cwd ?? process.cwd();
|
|
@@ -60,12 +50,6 @@ export async function report(eventType) {
|
|
|
60
50
|
model: stdinData.model,
|
|
61
51
|
tool_name: stdinData.tool_name,
|
|
62
52
|
};
|
|
63
|
-
if (eventType === "session-end") {
|
|
64
|
-
const transcriptPath = stdinData.transcript_path;
|
|
65
|
-
if (transcriptPath) {
|
|
66
|
-
payload.usage = parseTranscriptTokens(transcriptPath);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
53
|
const git = readGitState(cwd);
|
|
70
54
|
if (git) {
|
|
71
55
|
const gitPayload = {
|
|
@@ -74,7 +58,7 @@ export async function report(eventType) {
|
|
|
74
58
|
lastCommitMessage: git.lastCommitMessage,
|
|
75
59
|
hasUncommittedChanges: git.hasUncommittedChanges,
|
|
76
60
|
};
|
|
77
|
-
if (eventType === "stop"
|
|
61
|
+
if (eventType === "stop") {
|
|
78
62
|
const stats = readGitDiffStats(cwd);
|
|
79
63
|
gitPayload.filesChanged = stats.filesChanged;
|
|
80
64
|
gitPayload.insertions = stats.insertions;
|
package/dist/setup.js
CHANGED
|
@@ -15,6 +15,7 @@ function prompt(question) {
|
|
|
15
15
|
const HOOKS_CONFIG = {
|
|
16
16
|
SessionStart: [{ hooks: [{ type: "command", command: "npx session-intelligence-cli report session-start" }] }],
|
|
17
17
|
Stop: [{ hooks: [{ type: "command", command: "npx session-intelligence-cli report stop" }] }],
|
|
18
|
+
SessionEnd: [{ hooks: [{ type: "command", command: "npx session-intelligence-cli report session-end" }] }],
|
|
18
19
|
PostToolUse: [{ hooks: [{ type: "command", command: "npx session-intelligence-cli report tool-use", timeout: 3000 }] }],
|
|
19
20
|
Notification: [{ hooks: [{ type: "command", command: "npx session-intelligence-cli report notification" }] }],
|
|
20
21
|
SubagentStop: [{ hooks: [{ type: "command", command: "npx session-intelligence-cli report subagent-stop", timeout: 3000 }] }],
|
package/package.json
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "session-intelligence-cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"description": "Real-time dashboard for Claude Code sessions — track activity, git changes, token usage, and session history across all your projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"claude",
|
|
9
|
+
"claude-code",
|
|
10
|
+
"anthropic",
|
|
11
|
+
"ai",
|
|
12
|
+
"session",
|
|
13
|
+
"dashboard",
|
|
14
|
+
"cli",
|
|
15
|
+
"hooks",
|
|
16
|
+
"developer-tools",
|
|
17
|
+
"productivity"
|
|
18
|
+
],
|
|
19
|
+
"homepage": "https://www.session-intelligence.com",
|
|
7
20
|
"repository": {
|
|
8
21
|
"type": "git",
|
|
9
22
|
"url": "https://github.com/0xleal/personal-dashboard.git",
|
|
@@ -13,7 +26,8 @@
|
|
|
13
26
|
"session-intelligence": "./dist/index.js"
|
|
14
27
|
},
|
|
15
28
|
"files": [
|
|
16
|
-
"dist"
|
|
29
|
+
"dist",
|
|
30
|
+
"README.md"
|
|
17
31
|
],
|
|
18
32
|
"scripts": {
|
|
19
33
|
"build": "tsc",
|